diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index e65bdf134fe..9f82595eb33 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -187,31 +187,26 @@ SUBSYSTEM_DEF(shuttle)
return S
WARNING("couldn't find dock with id: [id]")
+/// Check if we can call the evac shuttle.
+/// Returns TRUE if we can. Otherwise, returns a string detailing the problem.
/datum/controller/subsystem/shuttle/proc/canEvac(mob/user)
var/srd = CONFIG_GET(number/shuttle_refuel_delay)
if(world.time - SSticker.round_start_time < srd)
- to_chat(user, "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before trying again.")
- return FALSE
+ return "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before attempting to call."
switch(emergency.mode)
if(SHUTTLE_RECALL)
- to_chat(user, "The emergency shuttle may not be called while returning to CentCom.")
- return FALSE
+ return "The emergency shuttle may not be called while returning to CentCom."
if(SHUTTLE_CALL)
- to_chat(user, "The emergency shuttle is already on its way.")
- return FALSE
+ return "The emergency shuttle is already on its way."
if(SHUTTLE_DOCKED)
- to_chat(user, "The emergency shuttle is already here.")
- return FALSE
+ return "The emergency shuttle is already here."
if(SHUTTLE_IGNITING)
- to_chat(user, "The emergency shuttle is firing its engines to leave.")
- return FALSE
+ return "The emergency shuttle is firing its engines to leave."
if(SHUTTLE_ESCAPE)
- to_chat(user, "The emergency shuttle is moving away to a safe distance.")
- return FALSE
- if(SHUTTLE_STRANDED, SHUTTLE_DISABLED)
- to_chat(user, "The emergency shuttle has been disabled by CentCom.")
- return FALSE
+ return "The emergency shuttle is moving away to a safe distance."
+ if(SHUTTLE_STRANDED)
+ return "The emergency shuttle has been disabled by CentCom."
return TRUE
@@ -229,7 +224,9 @@ SUBSYSTEM_DEF(shuttle)
Good luck.")
emergency = backup_shuttle
- if(!canEvac(user))
+ var/can_evac_or_fail_reason = SSshuttle.canEvac(user)
+ if(can_evac_or_fail_reason != TRUE)
+ to_chat(user, "[can_evac_or_fail_reason]")
return
call_reason = trim(html_encode(call_reason))
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index e4af943f2e7..7bde5574b71 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -99,7 +99,7 @@
minor_announce(input["message"], "Incoming message from [input["message_sender"]]")
for(var/obj/machinery/computer/communications/CM in GLOB.machines)
- CM.overrideCooldown()
+ CM.override_cooldown()
/datum/world_topic/news_report
keyword = "News_Report"
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 911d6061664..953021652e6 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -1,14 +1,10 @@
-#define STATE_DEFAULT 1
-#define STATE_CALLSHUTTLE 2
-#define STATE_CANCELSHUTTLE 3
-#define STATE_MESSAGELIST 4
-#define STATE_VIEWMESSAGE 5
-#define STATE_DELMESSAGE 6
-#define STATE_STATUSDISPLAY 7
-#define STATE_ALERT_LEVEL 8
-#define STATE_CONFIRM_LEVEL 9
-#define STATE_TOGGLE_EMERGENCY 10
-#define STATE_PURCHASE 11
+#define IMPORTANT_ACTION_COOLDOWN (60 SECONDS)
+#define MAX_STATUS_LINE_LENGTH 40
+
+#define STATE_BUYING_SHUTTLE "buying_shuttle"
+#define STATE_CHANGING_STATUS "changing_status"
+#define STATE_MAIN "main"
+#define STATE_MESSAGES "messages"
// The communications computer
/obj/machinery/computer/communications
@@ -19,409 +15,53 @@
req_access = list(ACCESS_HEADS)
circuit = /obj/item/circuitboard/computer/communications
light_color = LIGHT_COLOR_BLUE
- var/auth_id = "Unknown" //Who is currently logged in?
- var/list/datum/comm_message/messages = list()
- var/datum/comm_message/currmsg
- var/datum/comm_message/aicurrmsg
- var/state = STATE_DEFAULT
- var/aistate = STATE_DEFAULT
- var/message_cooldown = 0
- var/ai_message_cooldown = 0
- var/tmp_alertlevel = 0
- var/stat_msg1
- var/stat_msg2
+ /// Cooldown for important actions, such as messaging CentCom or other sectors
+ COOLDOWN_DECLARE(static/important_action_cooldown)
+ /// The current state of the UI
+ var/state = STATE_MAIN
-/obj/machinery/computer/communications/proc/checkCCcooldown()
- var/obj/item/circuitboard/computer/communications/CM = circuit
- if(CM.lastTimeUsed + 600 > world.time)
- return FALSE
- return TRUE
+ /// The current state of the UI for AIs
+ var/ai_state = STATE_MAIN
+
+ /// The name of the user who logged in
+ var/authorize_name
+
+ /// The access that the card had on login
+ var/list/authorize_access
+
+ /// The messages this console has been sent
+ var/list/datum/comm_message/messages
+
+ /// How many times the alert level has been changed
+ /// Used to clear the modal to change alert level
+ var/alert_level_tick = 0
+
+ /// The last lines used for changing the status display
+ var/static/last_status_display
/obj/machinery/computer/communications/Initialize()
. = ..()
GLOB.shuttle_caller_list += src
-/obj/machinery/computer/communications/Topic(href, href_list)
- if(..())
- return
- if(!usr.canUseTopic(src, !issilicon(usr)))
- return
- if(!is_station_level(z) && !is_reserved_level(z)) //Can only use in transit and on SS13
- to_chat(usr, "Unable to establish a connection: \black You're too far away from the station!")
- return
- usr.set_machine(src)
+/// Are we NOT the AI, AND we're logged in as the captain?
+/obj/machinery/computer/communications/proc/authenticated_as_non_ai_captain(mob/user)
+ if (isAI(user))
+ return FALSE
+ return ACCESS_CAPTAIN in authorize_access
+/// Are we the AI, OR we're logged in as the captain?
+/obj/machinery/computer/communications/proc/authenticated_as_ai_or_captain(mob/user)
+ if (isAI(user))
+ return TRUE
+ return ACCESS_CAPTAIN in authorize_access
- if(!href_list["operation"])
- return
- var/obj/item/circuitboard/computer/communications/CM = circuit
- switch(href_list["operation"])
- // main interface
- if("main")
- state = STATE_DEFAULT
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
- if("login")
- var/mob/M = usr
-
- var/obj/item/card/id/I = M.get_idcard(TRUE)
-
- if(I && istype(I))
- if(check_access(I))
- authenticated = 1
- auth_id = "[I.registered_name] ([I.assignment])"
- if((20 in I.access))
- authenticated = 2
- playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
- if(obj_flags & EMAGGED)
- authenticated = 2
- auth_id = "Unknown"
- to_chat(M, "[src] lets out a quiet alarm as its login is overridden.")
- playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
- playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE)
- if(prob(25))
- for(var/mob/living/silicon/ai/AI in active_ais())
- SEND_SOUND(AI, sound('sound/machines/terminal_alert.ogg', volume = 10)) //Very quiet for balance reasons
- if("logout")
- authenticated = 0
- playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
-
- if("swipeidseclevel")
- var/mob/M = usr
- var/obj/item/card/id/I = M.get_active_held_item()
- if (istype(I, /obj/item/pda))
- var/obj/item/pda/pda = I
- I = pda.id
- if (I && istype(I))
- if(ACCESS_CAPTAIN in I.access)
- var/old_level = GLOB.security_level
- if(!tmp_alertlevel)
- tmp_alertlevel = SEC_LEVEL_GREEN
- if(tmp_alertlevel < SEC_LEVEL_GREEN)
- tmp_alertlevel = SEC_LEVEL_GREEN
- //if(tmp_alertlevel > SEC_LEVEL_BLUE) //ORIGINAL
- // tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this //ORIGINAL
- if(tmp_alertlevel > SEC_LEVEL_AMBER) //SKYRAT EDIT CHANGE - ALERTS
- tmp_alertlevel = SEC_LEVEL_AMBER //Cannot engage delta with this //SKYRAT EDIT CHANGE - ALERTS
- set_security_level(tmp_alertlevel)
- if(GLOB.security_level != old_level)
- to_chat(usr, "Authorization confirmed. Modifying security level.")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- //Only notify people if an actual change happened
- var/security_level = get_security_level()
- log_game("[key_name(usr)] has changed the security level to [security_level] with [src] at [AREACOORD(usr)].")
- message_admins("[ADMIN_LOOKUPFLW(usr)] has changed the security level to [security_level] with [src] at [AREACOORD(usr)].")
- deadchat_broadcast(" has changed the security level to [security_level] with [src] at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- tmp_alertlevel = 0
- else
- to_chat(usr, "You are not authorized to do this!")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
- tmp_alertlevel = 0
- state = STATE_DEFAULT
- else
- to_chat(usr, "You need to swipe your ID!")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
-
- if("announce")
- if(authenticated==2)
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
- make_announcement(usr)
-
- if("crossserver")
- if(authenticated==2)
- var/dest = href_list["cross_dest"]
- if(!checkCCcooldown())
- to_chat(usr, "Arrays recycling. Please stand by.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
- return
- var/warning = dest == "all" ? "Please choose a message to transmit to allied stations." : "Please choose a message to transmit to [dest] sector station."
- var/input = stripped_multiline_input(usr, "[warning] Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "")
- if(!input || !(usr in view(1,src)) || !checkCCcooldown())
- return
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- var/payload = list()
- var/network_name = CONFIG_GET(string/cross_comms_network)
- if (network_name)
- payload["network"] = network_name
- send2otherserver("[station_name()]", input,"Comms_Console", dest == "all" ? null : list(dest), additional_data = payload)
- minor_announce(input, title = "Outgoing message to allied station")
- usr.log_talk(input, LOG_SAY, tag="message to the other server")
- message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server\[s].")
- deadchat_broadcast(" has sent an outgoing message to the other station(s).", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- CM.lastTimeUsed = world.time
-
- if("purchase_menu")
- state = STATE_PURCHASE
-
- if("buyshuttle")
- if(authenticated==2)
- var/list/shuttles = flatten_list(SSmapping.shuttle_templates)
- var/datum/map_template/shuttle/S = locate(href_list["chosen_shuttle"]) in shuttles
- if(S && istype(S))
- if(SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
- to_chat(usr, "It's a bit late to buy a new shuttle, don't you think?")
- return
- if(SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_PURCHASED)
- to_chat(usr, "A replacement shuttle has already been purchased.")
- else if(SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_FORCED)
- to_chat(usr, "Due to unforseen circumstances, shuttle purchasing is no longer available.")
- else if(!S.prerequisites_met())
- to_chat(usr, "You have not met the requirements for purchasing this shuttle.")
- else
- var/points_to_check
- var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
- if(D)
- points_to_check = D.account_balance
- if(points_to_check >= S.credit_cost)
- SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_PURCHASED
- SSshuttle.unload_preview()
- SSshuttle.existing_shuttle = SSshuttle.emergency
- SSshuttle.action_load(S)
- D.adjust_money(-S.credit_cost)
- minor_announce("[usr.real_name] has purchased [S.name] for [S.credit_cost] credits.[S.extra_desc ? " [S.extra_desc]" : ""]" , "Shuttle Purchase")
- message_admins("[ADMIN_LOOKUPFLW(usr)] purchased [S.name].")
- log_shuttle("[key_name(usr)] has purchased [S.name].")
- SSblackbox.record_feedback("text", "shuttle_purchase", 1, "[S.name]")
- else
- to_chat(usr, "Insufficient credits.")
-
- if("callshuttle")
- state = STATE_DEFAULT
- if(authenticated && SSshuttle.canEvac(usr))
- state = STATE_CALLSHUTTLE
- if("callshuttle2")
- if(authenticated)
- SSshuttle.requestEvac(usr, href_list["call"])
- if(SSshuttle.emergency.timer)
- post_status("shuttle")
- state = STATE_DEFAULT
- if("cancelshuttle")
- state = STATE_DEFAULT
- if(authenticated)
- state = STATE_CANCELSHUTTLE
- if("cancelshuttle2")
- if(authenticated)
- SSshuttle.cancelEvac(usr)
- state = STATE_DEFAULT
- if("messagelist")
- currmsg = 0
- state = STATE_MESSAGELIST
- if("viewmessage")
- state = STATE_VIEWMESSAGE
- if (!currmsg)
- if(href_list["message-num"])
- var/msgnum = text2num(href_list["message-num"])
- currmsg = messages[msgnum]
- else
- state = STATE_MESSAGELIST
- if("delmessage")
- state = currmsg ? STATE_DELMESSAGE : STATE_MESSAGELIST
- if("delmessage2")
- if(authenticated)
- if(currmsg)
- if(aicurrmsg == currmsg)
- aicurrmsg = null
- messages -= currmsg
- currmsg = null
- state = STATE_MESSAGELIST
- else
- state = STATE_VIEWMESSAGE
- if("respond")
- var/answer = text2num(href_list["answer"])
- if(!currmsg || !answer || currmsg.possible_answers.len < answer)
- state = STATE_MESSAGELIST
- else
- if(!currmsg.answered)
- currmsg.answered = answer
- log_game("[key_name(usr)] answered [currmsg.title] comm message. Answer : [currmsg.answered]")
- if(currmsg)
- currmsg.answer_callback.InvokeAsync()
- state = STATE_VIEWMESSAGE
- updateDialog()
- if("status")
- state = STATE_STATUSDISPLAY
- if("securitylevel")
- tmp_alertlevel = text2num( href_list["newalertlevel"] )
- if(!tmp_alertlevel)
- tmp_alertlevel = 0
- state = STATE_CONFIRM_LEVEL
- if("changeseclevel")
- state = STATE_ALERT_LEVEL
-
- if("emergencyaccess")
- state = STATE_TOGGLE_EMERGENCY
- if("enableemergency")
- make_maint_all_access()
- log_game("[key_name(usr)] enabled emergency maintenance access.")
- message_admins("[ADMIN_LOOKUPFLW(usr)] enabled emergency maintenance access.")
- deadchat_broadcast(" enabled emergency maintenance access at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- state = STATE_DEFAULT
- if("disableemergency")
- revoke_maint_all_access()
- log_game("[key_name(usr)] disabled emergency maintenance access.")
- message_admins("[ADMIN_LOOKUPFLW(usr)] disabled emergency maintenance access.")
- deadchat_broadcast(" disabled emergency maintenance access at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- state = STATE_DEFAULT
-
- // Status display stuff
- if("setstat")
- playsound(src, "terminal_type", 50, FALSE)
- switch(href_list["statdisp"])
- if("message")
- post_status("message", stat_msg1, stat_msg2)
- if("alert")
- post_status("alert", href_list["alert"])
- else
- post_status(href_list["statdisp"])
-
- if("setmsg1")
- stat_msg1 = reject_bad_text(input("Line 1", "Enter Message Text", stat_msg1) as text|null, 40)
- updateDialog()
- if("setmsg2")
- stat_msg2 = reject_bad_text(input("Line 2", "Enter Message Text", stat_msg2) as text|null, 40)
- updateDialog()
-
- // OMG CENTCOM LETTERHEAD
- if("MessageCentCom")
- if(authenticated)
- if(!checkCCcooldown())
- to_chat(usr, "Arrays recycling. Please stand by.")
- return
- var/input = stripped_input(usr, "Please choose a message to transmit to CentCom via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "Send a message to CentCom.", "")
- if(!input || !(usr in view(1,src)) || !checkCCcooldown())
- return
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- CentCom_announce(input, usr)
- to_chat(usr, "Message transmitted to Central Command.")
- usr.log_talk(input, LOG_SAY, tag="CentCom announcement")
- deadchat_broadcast(" has messaged CentCom, \"[input]\" at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- CM.lastTimeUsed = world.time
-
- // OMG SYNDICATE ...LETTERHEAD
- if("MessageSyndicate")
- if((authenticated) && (obj_flags & EMAGGED))
- if(!checkCCcooldown())
- to_chat(usr, "Arrays recycling. Please stand by.")
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
- return
- var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING COORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "Send a message to /??????/.", "")
- if(!input || !(usr in view(1,src)) || !checkCCcooldown())
- return
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- Syndicate_announce(input, usr)
- to_chat(usr, "SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND.")
- usr.log_talk(input, LOG_SAY, tag="Syndicate announcement")
- deadchat_broadcast(" has messaged the Syndicate, \"[input]\" at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- CM.lastTimeUsed = world.time
-
- if("RestoreBackup")
- to_chat(usr, "Backup routing data restored!")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- obj_flags &= ~EMAGGED
- updateDialog()
-
- if("nukerequest") //When there's no other way
- if(authenticated==2)
- if(!checkCCcooldown())
- to_chat(usr, "Arrays recycling. Please stand by.")
- return
- var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self-Destruct Code Request.","")
- if(!input || !(usr in view(1,src)) || !checkCCcooldown())
- return
- Nuke_request(input, usr)
- to_chat(usr, "Request sent.")
- usr.log_message("has requested the nuclear codes from CentCom with reason \"[input]\"", LOG_SAY)
- priority_announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self-Destruct Codes Requested",'sound/ai/commandreport.ogg')
- CM.lastTimeUsed = world.time
-
-
- // AI interface
- if("ai-main")
- aicurrmsg = null
- aistate = STATE_DEFAULT
- if("ai-callshuttle")
- aistate = STATE_DEFAULT
- if(SSshuttle.canEvac(usr))
- aistate = STATE_CALLSHUTTLE
- if("ai-callshuttle2")
- SSshuttle.requestEvac(usr, href_list["call"])
- aistate = STATE_DEFAULT
- if("ai-messagelist")
- aicurrmsg = null
- aistate = STATE_MESSAGELIST
- if("ai-viewmessage")
- aistate = STATE_VIEWMESSAGE
- if (!aicurrmsg)
- if(href_list["message-num"])
- var/msgnum = text2num(href_list["message-num"])
- aicurrmsg = messages[msgnum]
- else
- aistate = STATE_MESSAGELIST
- if("ai-delmessage")
- aistate = aicurrmsg ? STATE_DELMESSAGE : STATE_MESSAGELIST
- if("ai-delmessage2")
- if(aicurrmsg)
- if(currmsg == aicurrmsg)
- currmsg = null
- messages -= aicurrmsg
- aicurrmsg = null
- aistate = STATE_MESSAGELIST
- if("ai-respond")
- var/answer = text2num(href_list["answer"])
- if(!aicurrmsg || !answer || aicurrmsg.possible_answers.len < answer)
- aistate = STATE_MESSAGELIST
- else
- if(!aicurrmsg.answered)
- aicurrmsg.answered = answer
- log_game("[key_name(usr)] answered [aicurrmsg.title] comm message. Answer : [aicurrmsg.answered]")
- if(aicurrmsg.answer_callback)
- aicurrmsg.answer_callback.InvokeAsync()
- aistate = STATE_VIEWMESSAGE
- if("ai-status")
- aistate = STATE_STATUSDISPLAY
- if("ai-announce")
- make_announcement(usr, 1)
- if("ai-securitylevel")
- tmp_alertlevel = text2num( href_list["newalertlevel"] )
- if(!tmp_alertlevel)
- tmp_alertlevel = 0
- var/old_level = GLOB.security_level
- if(!tmp_alertlevel)
- tmp_alertlevel = SEC_LEVEL_GREEN
- if(tmp_alertlevel < SEC_LEVEL_GREEN)
- tmp_alertlevel = SEC_LEVEL_GREEN
- //if(tmp_alertlevel > SEC_LEVEL_BLUE) //ORIGINAL
- // tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this //ORIGINAL
- if(tmp_alertlevel > SEC_LEVEL_AMBER) //SKYRAT EDIT CHANGE - ALERTS
- tmp_alertlevel = SEC_LEVEL_AMBER //Cannot engage delta with this //SKYRAT EDIT CHANGE - ALERTS
- set_security_level(tmp_alertlevel)
- if(GLOB.security_level != old_level)
- //Only notify people if an actual change happened
- var/security_level = get_security_level()
- log_game("[key_name(usr)] has changed the security level to [security_level] from [src] at [AREACOORD(usr)].")
- message_admins("[ADMIN_LOOKUPFLW(usr)] has changed the security level to [security_level] from [src] at [AREACOORD(usr)].")
- deadchat_broadcast(" has changed the security level to [security_level] from [src] at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- tmp_alertlevel = 0
- aistate = STATE_DEFAULT
- if("ai-changeseclevel")
- aistate = STATE_ALERT_LEVEL
- if("ai-emergencyaccess")
- aistate = STATE_TOGGLE_EMERGENCY
- if("ai-enableemergency")
- make_maint_all_access()
- log_game("[key_name(usr)] enabled emergency maintenance access.")
- message_admins("[ADMIN_LOOKUPFLW(usr)] enabled emergency maintenance access.")
- deadchat_broadcast(" enabled emergency maintenance access.", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- aistate = STATE_DEFAULT
- if("ai-disableemergency")
- revoke_maint_all_access()
- log_game("[key_name(usr)] disabled emergency maintenance access.")
- message_admins("[ADMIN_LOOKUPFLW(usr)] disabled emergency maintenance access.")
- deadchat_broadcast(" disabled emergency maintenance access.", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
- aistate = STATE_DEFAULT
-
- updateUsrDialog()
+/// Are we the AI, OR logged in?
+/obj/machinery/computer/communications/proc/authenticated(mob/user)
+ if (isAI(user))
+ return TRUE
+ return authenticated
/obj/machinery/computer/communications/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/card/id))
@@ -430,298 +70,400 @@
return ..()
/obj/machinery/computer/communications/emag_act(mob/user)
- if(obj_flags & EMAGGED)
+ if (obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
- if(authenticated == 1)
- authenticated = 2
+ if (authenticated)
+ authorize_access = get_all_accesses()
to_chat(user, "You scramble the communication routing circuits!")
playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE)
-/obj/machinery/computer/communications/ui_interact(mob/user)
+/obj/machinery/computer/communications/ui_act(action, list/params)
+ var/static/list/approved_states = list(STATE_BUYING_SHUTTLE, STATE_CHANGING_STATUS, STATE_MAIN, STATE_MESSAGES)
+ var/static/list/approved_status_pictures = list("biohazard", "blank", "default", "lockdown", "redalert", "shuttle")
+
. = ..()
- if (z > 6)
- to_chat(user, "Unable to establish a connection: \black You're too far away from the station!")
+ if (.)
return
- var/dat = ""
- if(SSshuttle.emergency.mode == SHUTTLE_CALL)
- var/timeleft = SSshuttle.emergency.timeLeft()
- dat += "Emergency shuttle\n
\nETA: [timeleft / 60 % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]"
+ . = TRUE
+ switch (action)
+ if ("answerMessage")
+ if (!authenticated(usr))
+ return
+ var/answer_index = text2num(params["answer"])
+ var/message_index = text2num(params["message"])
+ if (!answer_index || !message_index || answer_index < 1 || message_index < 1)
+ return
+ var/datum/comm_message/message = messages[message_index]
+ if (message.answered)
+ return
+ message.answered = answer_index
+ message.answer_callback.InvokeAsync()
+ if ("callShuttle")
+ if (!authenticated(usr))
+ return
+ var/reason = trim(params["reason"], MAX_MESSAGE_LEN)
+ if (length(reason) < CALL_SHUTTLE_REASON_LENGTH)
+ return
+ SSshuttle.requestEvac(usr, reason)
+ post_status("shuttle")
+ if ("changeSecurityLevel")
+ if (!authenticated_as_ai_or_captain(usr))
+ return
- var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500)
+ // Check if they have
+ if (!isAI(usr))
+ var/obj/item/held_item = usr.get_active_held_item()
+ var/obj/item/card/id/id_card = held_item?.GetID()
+ if (!istype(id_card))
+ to_chat(usr, "You need to swipe your ID!")
+ playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
+ return
+ if (!(ACCESS_CAPTAIN in id_card.access))
+ to_chat(usr, "You are not authorized to do this!")
+ playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
+ return
- if(issilicon(user))
- var/dat2 = interact_ai(user) // give the AI a different interact proc to limit its access
- if(dat2)
- dat += dat2
- popup.set_content(dat)
- popup.open()
- return
+ var/new_sec_level = seclevel2num(params["newSecurityLevel"])
+ if (new_sec_level != SEC_LEVEL_GREEN && new_sec_level != SEC_LEVEL_BLUE)
+ return
+ if (GLOB.security_level == new_sec_level)
+ return
- switch(state)
- if(STATE_DEFAULT)
- if (authenticated)
- if(SSshuttle.emergencyCallAmount)
- if(SSshuttle.emergencyLastCallLoc)
- dat += "Most recent shuttle call/recall traced to: [format_text(SSshuttle.emergencyLastCallLoc.name)]
"
- else
- dat += "Unable to trace most recent shuttle call/recall signal.
"
- dat += "Logged in as: [auth_id]"
- dat += "
"
- dat += "
\[ Log Out \]
"
- dat += "
General Functions"
- dat += "
\[ Message List \]"
- switch(SSshuttle.emergency.mode)
- if(SHUTTLE_IDLE, SHUTTLE_RECALL)
- dat += "
\[ Call Emergency Shuttle \]"
- else
- dat += "
\[ Cancel Shuttle Call \]"
+ set_security_level(new_sec_level)
- dat += "
\[ Set Status Display \]"
- if (authenticated==2)
- dat += "
Captain Functions"
- dat += "
\[ Make a Captain's Announcement \]"
- var/list/cross_servers = CONFIG_GET(keyed_list/cross_server)
+ to_chat(usr, "Authorization confirmed. Modifying security level.")
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+
+ // Only notify people if an actual change happened
+ log_game("[key_name(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
+ deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type=DEADCHAT_ANNOUNCEMENT)
+
+ alert_level_tick += 1
+ if ("deleteMessage")
+ if (!authenticated(usr))
+ return
+ var/message_index = text2num(params["message"])
+ if (!message_index)
+ return
+ LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
+ if ("makePriorityAnnouncement")
+ if (!authenticated_as_ai_or_captain(usr))
+ return
+ make_announcement(usr)
+ if ("messageAssociates")
+ if (!authenticated_as_non_ai_captain(usr))
+ return
+ if (!COOLDOWN_FINISHED(src, important_action_cooldown))
+ return
+
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ var/message = trim(html_encode(params["message"]), MAX_MESSAGE_LEN)
+
+ var/emagged = obj_flags & EMAGGED
+ if (emagged)
+ message_syndicate(message, usr)
+ to_chat(usr, "SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND.")
+ else
+ message_centcom(message, usr)
+ to_chat(usr, "Message transmitted to Central Command.")
+
+ var/associates = emagged ? "the Syndicate": "CentCom"
+ usr.log_talk(message, LOG_SAY, tag = "message to [associates]")
+ deadchat_broadcast(" has messaged [associates], \"[message]\" at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type = DEADCHAT_ANNOUNCEMENT)
+ COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
+ if ("purchaseShuttle")
+ var/can_buy_shuttles_or_fail_reason = can_buy_shuttles(usr)
+ if (can_buy_shuttles_or_fail_reason != TRUE)
+ if (can_buy_shuttles_or_fail_reason != FALSE)
+ to_chat(usr, "[can_buy_shuttles_or_fail_reason]")
+ return
+ var/list/shuttles = flatten_list(SSmapping.shuttle_templates)
+ var/datum/map_template/shuttle/shuttle = locate(params["shuttle"]) in shuttles
+ if (!istype(shuttle))
+ return
+ if (!shuttle.prerequisites_met())
+ to_chat(usr, "You have not met the requirements for purchasing this shuttle.")
+ return
+ var/datum/bank_account/bank_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ if (bank_account.account_balance < shuttle.credit_cost)
+ return
+ SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_PURCHASED
+ SSshuttle.unload_preview()
+ SSshuttle.existing_shuttle = SSshuttle.emergency
+ SSshuttle.action_load(shuttle)
+ bank_account.adjust_money(-shuttle.credit_cost)
+ minor_announce("[usr.real_name] has purchased [shuttle.name] for [shuttle.credit_cost] credits.[shuttle.extra_desc ? " [shuttle.extra_desc]" : ""]" , "Shuttle Purchase")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] purchased [shuttle.name].")
+ log_shuttle("[key_name(usr)] has purchased [shuttle.name].")
+ SSblackbox.record_feedback("text", "shuttle_purchase", 1, shuttle.name)
+ state = STATE_MAIN
+ if ("recallShuttle")
+ // AIs cannot recall the shuttle
+ if (!authenticated(usr) || isAI(usr))
+ return
+ SSshuttle.cancelEvac(usr)
+ if ("requestNukeCodes")
+ if (!authenticated_as_non_ai_captain(usr))
+ return
+ if (!COOLDOWN_FINISHED(src, important_action_cooldown))
+ return
+ var/reason = trim(html_encode(params["reason"]), MAX_MESSAGE_LEN)
+ nuke_request(reason, usr)
+ to_chat(usr, "Request sent.")
+ usr.log_message("has requested the nuclear codes from CentCom with reason \"[reason]\"", LOG_SAY)
+ priority_announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self-Destruct Codes Requested", 'sound/ai/commandreport.ogg')
+ playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
+ if ("restoreBackupRoutingData")
+ if (!authenticated_as_non_ai_captain(usr))
+ return
+ if (!(obj_flags & EMAGGED))
+ return
+ to_chat(usr, "Backup routing data restored.")
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ obj_flags &= ~EMAGGED
+ if ("sendToOtherSector")
+ if (!authenticated_as_non_ai_captain(usr))
+ return
+ if (!can_send_messages_to_other_sectors(usr))
+ return
+ if (!COOLDOWN_FINISHED(src, important_action_cooldown))
+ return
+
+ var/message = trim(html_encode(params["message"]), MAX_MESSAGE_LEN)
+ if (!message)
+ return
+
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+
+ var/destination = params["destination"]
+ var/list/payload = list()
+
+ var/network_name = CONFIG_GET(string/cross_comms_network)
+ if (network_name)
+ payload["network"] = network_name
+
+ send2otherserver(station_name(), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
+ minor_announce(message, title = "Outgoing message to allied station")
+ usr.log_talk(message, LOG_SAY, tag = "message to the other server")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server\[s].")
+ deadchat_broadcast(" has sent an outgoing message to the other station(s).", "[usr.real_name]", usr, message_type = DEADCHAT_ANNOUNCEMENT)
+
+ COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
+ if ("setState")
+ if (!authenticated(usr))
+ return
+ if (!(params["state"] in approved_states))
+ return
+ if (state == STATE_BUYING_SHUTTLE && can_buy_shuttles(usr) != TRUE)
+ return
+ set_state(usr, params["state"])
+ playsound(src, "terminal_type", 50, FALSE)
+ if ("setStatusMessage")
+ if (!authenticated(usr))
+ return
+ var/line_one = reject_bad_text(params["lineOne"] || "", MAX_STATUS_LINE_LENGTH)
+ var/line_two = reject_bad_text(params["lineTwo"] || "", MAX_STATUS_LINE_LENGTH)
+ post_status("alert", "blank")
+ post_status("message", line_one, line_two)
+ last_status_display = list(line_one, line_two)
+ playsound(src, "terminal_type", 50, FALSE)
+ if ("setStatusPicture")
+ if (!authenticated(usr))
+ return
+ var/picture = params["picture"]
+ if (!(picture in approved_status_pictures))
+ return
+ post_status("alert", picture)
+ playsound(src, "terminal_type", 50, FALSE)
+ if ("toggleAuthentication")
+ // Log out if we're logged in
+ if (authorize_name)
+ authenticated = FALSE
+ authorize_access = null
+ authorize_name = null
+ playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
+ return
+
+ if (obj_flags & EMAGGED)
+ authenticated = TRUE
+ authorize_access = get_all_accesses()
+ authorize_name = "Unknown"
+ to_chat(usr, "[src] lets out a quiet alarm as its login is overridden.")
+ playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE)
+ else
+ var/obj/item/card/id/id_card = usr.get_idcard(hand_first = TRUE)
+ if (check_access(id_card))
+ authenticated = TRUE
+ authorize_access = id_card.access
+ authorize_name = "[id_card.registered_name] - [id_card.assignment]"
+
+ state = STATE_MAIN
+ playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
+ if ("toggleEmergencyAccess")
+ if (!authenticated_as_ai_or_captain(usr))
+ return
+ if (GLOB.emergency_access)
+ revoke_maint_all_access()
+ log_game("[key_name(usr)] disabled emergency maintenance access.")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] disabled emergency maintenance access.")
+ deadchat_broadcast(" disabled emergency maintenance access at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type = DEADCHAT_ANNOUNCEMENT)
+ else
+ make_maint_all_access()
+ log_game("[key_name(usr)] enabled emergency maintenance access.")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] enabled emergency maintenance access.")
+ deadchat_broadcast(" enabled emergency maintenance access at [get_area_name(usr, TRUE)].", "[usr.real_name]", usr, message_type = DEADCHAT_ANNOUNCEMENT)
+
+/obj/machinery/computer/communications/ui_data(mob/user)
+ var/list/data = list(
+ "authenticated" = FALSE,
+ "emagged" = FALSE,
+ )
+
+ var/ui_state = isAI(user) ? ai_state : state
+
+ if (authenticated || isAI(user))
+ data["authenticated"] = TRUE
+ data["canLogOut"] = !isAI(user)
+ data["page"] = ui_state
+
+ if (obj_flags & EMAGGED)
+ data["emagged"] = TRUE
+
+ switch (ui_state)
+ if (STATE_MAIN)
+ data["canBuyShuttles"] = can_buy_shuttles(user)
+ data["canMakeAnnouncement"] = FALSE
+ data["canMessageAssociates"] = FALSE
+ data["canRequestNuke"] = FALSE
+ data["canSendToSectors"] = FALSE
+ data["canSetAlertLevel"] = FALSE
+ data["canToggleEmergencyAccess"] = FALSE
+ data["importantActionReady"] = COOLDOWN_FINISHED(src, important_action_cooldown)
+ data["shuttleCalled"] = FALSE
+ data["shuttleLastCalled"] = FALSE
+
+ data["alertLevel"] = get_security_level()
+ data["authorizeName"] = authorize_name
+ data["canLogOut"] = !isAI(user)
+ data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user)
+
+ if (authenticated_as_non_ai_captain(user))
+ data["canMessageAssociates"] = TRUE
+ data["canRequestNuke"] = TRUE
+
+ if (can_send_messages_to_other_sectors(user))
+ data["canSendToSectors"] = TRUE
+
+ var/list/sectors = list()
var/our_id = CONFIG_GET(string/cross_comms_name)
- if(cross_servers.len)
- for(var/server in cross_servers)
- if(server == our_id)
- continue
- dat += "
\[ Send a message to station in [server] sector. \]"
- if(cross_servers.len > 2)
- dat += "
\[ Send a message to all allied stations \]"
- if(SSmapping.config.allow_custom_shuttles)
- dat += "
\[ Purchase Shuttle \]"
- dat += "
\[ Change Alert Level \]"
- dat += "
\[ Emergency Maintenance Access \]"
- dat += "
\[ Request Nuclear Authentication Codes \]"
- if(!(obj_flags & EMAGGED))
- dat += "
\[ Send Message to CentCom \]"
- else
- dat += "
\[ Send Message to \[UNKNOWN\] \]"
- dat += "
\[ Restore Backup Routing Data \]"
- else
- dat += "
\[ Log In \]"
- if(STATE_CALLSHUTTLE)
- dat += get_call_shuttle_form()
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
- if(STATE_CANCELSHUTTLE)
- dat += get_cancel_shuttle_form()
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
- if(STATE_MESSAGELIST)
- dat += "Messages:"
- for(var/i in 1 to messages.len)
- var/datum/comm_message/M = messages[i]
- dat += "
[M.title]"
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- if(STATE_VIEWMESSAGE)
- if (currmsg)
- dat += "[currmsg.title]
[currmsg.content]"
- if(!currmsg.answered && currmsg.possible_answers.len)
- for(var/i in 1 to currmsg.possible_answers.len)
- var/answer = currmsg.possible_answers[i]
- dat += "
\[ Answer : [answer] \]"
- else if(currmsg.answered)
- var/answered = currmsg.possible_answers[currmsg.answered]
- dat += "
Archived Answer : [answered]"
- dat += "
\[ Delete \]"
- else
- aistate = STATE_MESSAGELIST
- attack_hand(user)
- return
- if(STATE_DELMESSAGE)
- if (currmsg)
- dat += "Are you sure you want to delete this message? \[ OK | Cancel \]"
- else
- state = STATE_MESSAGELIST
- attack_hand(user)
- return
- if(STATE_STATUSDISPLAY)
- dat += "Set Status Displays
"
- dat += "\[ Clear \]
"
- dat += "\[ Shuttle ETA \]
"
- dat += "\[ Message \]"
- dat += "
"
- dat += "\[ Alert: None |"
- dat += " Red Alert |"
- dat += " Lockdown |"
- dat += " Biohazard \]
"
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
- if(STATE_ALERT_LEVEL)
- dat += "Current alert level: [get_security_level()]
"
- if(GLOB.security_level == SEC_LEVEL_DELTA)
- dat += "The self-destruct mechanism is active. Find a way to deactivate the mechanism to lower the alert level or evacuate."
- else
- //SKYRAT CHANGE ADDITION BEGIN - ALERTS
- dat += "Amber
"
- dat += "Orange
"
- dat += "Violet
"
- //SKYRAT CHANGE END
- dat += "Blue
"
- dat += "Green"
- if(STATE_CONFIRM_LEVEL)
- dat += "Current alert level: [get_security_level()]
"
- dat += "Confirm the change to: [num2seclevel(tmp_alertlevel)]
"
- dat += "Swipe ID to confirm change.
"
- if(STATE_TOGGLE_EMERGENCY)
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
- if(GLOB.emergency_access == 1)
- dat += "Emergency Maintenance Access is currently ENABLED"
- dat += "
Restore maintenance access restrictions?
\[ OK | Cancel \]"
- else
- dat += "Emergency Maintenance Access is currently DISABLED"
- dat += "
Lift access restrictions on maintenance and external airlocks?
\[ OK | Cancel \]"
- if(STATE_PURCHASE)
- var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
- dat += "Budget: [D.account_balance] Credits.
"
- dat += "
"
- dat += "Caution: Purchasing dangerous shuttles may lead to mutiny and/or death.
"
- dat += "
"
- for(var/shuttle_id in SSmapping.shuttle_templates)
- var/datum/map_template/shuttle/S = SSmapping.shuttle_templates[shuttle_id]
- if(S.can_be_bought && S.credit_cost < INFINITY)
- dat += "[S.name] | [S.credit_cost] Credits
"
- dat += "[S.description]
"
- if(S.prerequisites)
- dat += "Prerequisites: [S.prerequisites]
"
- dat += "(Purchase)
"
+ for (var/server in CONFIG_GET(keyed_list/cross_server))
+ if (server == our_id)
+ continue
+ sectors += server
- dat += "
\[ [(state != STATE_DEFAULT) ? "Main Menu | " : ""]Close \]"
+ data["sectors"] = sectors
- popup.set_content(dat)
- popup.open()
+ if (authenticated_as_ai_or_captain(user))
+ data["canToggleEmergencyAccess"] = TRUE
+ data["emergencyAccess"] = GLOB.emergency_access
-/obj/machinery/computer/communications/proc/get_javascript_header(form_id)
- var/dat = {""}
- return dat
+ data["alertLevelTick"] = alert_level_tick
+ data["canMakeAnnouncement"] = TRUE
+ data["canSetAlertLevel"] = isAI(user) ? "NO_SWIPE_NEEDED" : "SWIPE_NEEDED"
-/obj/machinery/computer/communications/proc/get_call_shuttle_form(ai_interface = 0)
- var/form_id = "callshuttle"
- var/dat = get_javascript_header(form_id)
- dat += "
+ )}
+ key={shuttle.ref}
+ buttons={(
+
"+se(c.message+"",!0)+"";throw c}}return pe.options=pe.setOptions=function(e){return ue(pe.defaults,e),fe(pe.defaults),pe},pe.getDefaults=le,pe.defaults=de,pe.use=function(e){var t=ue({},e);if(e.renderer&&function(){var n=pe.defaults.renderer||new te,r=function(){function t(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a
'+(n?e:ee(e,!0))+"\n":""+(n?e:ee(e,!0))+"\n"}return e}(),t.blockquote=function(){function e(e){return"\n"+e+"\n"}return e}(),t.html=function(){function e(e){return e}return e}(),t.heading=function(){function e(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"}return e}(),t.table=function(){function e(e,t){return t&&(t=""+t+""),""+e+""}return e}(),t.br=function(){function e(){return this.options.xhtml?""+se(c.message+"",!0)+"";throw c}}return pe.options=pe.setOptions=function(e){return ue(pe.defaults,e),fe(pe.defaults),pe},pe.getDefaults=le,pe.defaults=de,pe.use=function(e){var t=ue({},e);if(e.renderer&&function(){var n=pe.defaults.renderer||new te,r=function(){function t(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a