mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-30 23:49:21 +01:00
TGUI NTOS - Total Conversion
This commit is contained in:
@@ -123,4 +123,7 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
|
||||
. += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status
|
||||
. += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags
|
||||
. += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality
|
||||
//VOREStation Add End
|
||||
//VOREStation Add End
|
||||
|
||||
/datum/tgui_module/proc/relaymove(mob/user, direction)
|
||||
return FALSE
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
#define COMM_SCREEN_MAIN 1
|
||||
#define COMM_SCREEN_STAT 2
|
||||
#define COMM_SCREEN_MESSAGES 3
|
||||
|
||||
#define COMM_AUTHENTICATION_NONE 0
|
||||
#define COMM_AUTHENTICATION_MIN 1
|
||||
#define COMM_AUTHENTICATION_MAX 2
|
||||
|
||||
#define COMM_MSGLEN_MINIMUM 6
|
||||
#define COMM_CCMSGLEN_MINIMUM 20
|
||||
|
||||
/datum/tgui_module/communications
|
||||
name = "Command & Communications"
|
||||
tgui_id = "CommunicationsConsole"
|
||||
|
||||
var/emagged = FALSE
|
||||
|
||||
var/current_viewing_message_id = 0
|
||||
var/current_viewing_message = null
|
||||
|
||||
var/authenticated = COMM_AUTHENTICATION_NONE
|
||||
var/menu_state = COMM_SCREEN_MAIN
|
||||
var/ai_menu_state = COMM_SCREEN_MAIN
|
||||
var/aicurrmsg
|
||||
|
||||
var/message_cooldown
|
||||
var/centcomm_message_cooldown
|
||||
var/tmp_alertlevel = 0
|
||||
|
||||
var/stat_msg1
|
||||
var/stat_msg2
|
||||
var/display_type = "blank"
|
||||
|
||||
var/datum/announcement/priority/crew_announcement
|
||||
|
||||
var/datum/lore/atc_controller/ATC
|
||||
|
||||
var/list/req_access = list()
|
||||
|
||||
/datum/tgui_module/communications/New(host)
|
||||
. = ..()
|
||||
ATC = atc
|
||||
crew_announcement = new()
|
||||
crew_announcement.newscast = TRUE
|
||||
|
||||
/datum/tgui_module/communications/tgui_interact(mob/user, datum/tgui/ui)
|
||||
if(using_map && !(get_z(user) in using_map.contact_levels))
|
||||
to_chat(user, "<span class='danger'>Unable to establish a connection: You're too far away from the station!</span>")
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
/datum/tgui_module/communications/proc/is_authenticated(mob/user, message = TRUE)
|
||||
if(authenticated == COMM_AUTHENTICATION_MAX)
|
||||
return COMM_AUTHENTICATION_MAX
|
||||
else if(isobserver(user))
|
||||
var/mob/observer/dead/D = user
|
||||
if(D.can_admin_interact())
|
||||
return COMM_AUTHENTICATION_MAX
|
||||
else if(authenticated)
|
||||
return COMM_AUTHENTICATION_MIN
|
||||
else
|
||||
if(message)
|
||||
to_chat(user, "<span class='warning'>Access denied.</span>")
|
||||
return COMM_AUTHENTICATION_NONE
|
||||
|
||||
/datum/tgui_module/communications/proc/change_security_level(new_level)
|
||||
tmp_alertlevel = new_level
|
||||
var/old_level = 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) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this
|
||||
set_security_level(tmp_alertlevel)
|
||||
if(security_level != old_level)
|
||||
//Only notify the admins if an actual change happened
|
||||
log_game("[key_name(usr)] has changed the security level to [get_security_level()].")
|
||||
message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].")
|
||||
switch(security_level)
|
||||
if(SEC_LEVEL_GREEN)
|
||||
feedback_inc("alert_comms_green",1)
|
||||
if(SEC_LEVEL_YELLOW)
|
||||
feedback_inc("alert_comms_yellow",1)
|
||||
if(SEC_LEVEL_VIOLET)
|
||||
feedback_inc("alert_comms_violet",1)
|
||||
if(SEC_LEVEL_ORANGE)
|
||||
feedback_inc("alert_comms_orange",1)
|
||||
if(SEC_LEVEL_BLUE)
|
||||
feedback_inc("alert_comms_blue",1)
|
||||
tmp_alertlevel = 0
|
||||
|
||||
/datum/tgui_module/communications/tgui_data(mob/user)
|
||||
var/list/data = ..()
|
||||
data["is_ai"] = isAI(user) || isrobot(user)
|
||||
data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state
|
||||
data["emagged"] = emagged
|
||||
data["authenticated"] = is_authenticated(user, 0)
|
||||
data["authmax"] = data["authenticated"] == COMM_AUTHENTICATION_MAX ? TRUE : FALSE
|
||||
data["atcsquelch"] = ATC.squelched
|
||||
data["boss_short"] = using_map.boss_short
|
||||
|
||||
data["stat_display"] = list(
|
||||
"type" = display_type,
|
||||
// "icon" = display_icon,
|
||||
"line_1" = (stat_msg1 ? stat_msg1 : "-----"),
|
||||
"line_2" = (stat_msg2 ? stat_msg2 : "-----"),
|
||||
|
||||
"presets" = list(
|
||||
list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"),
|
||||
list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."),
|
||||
list("name" = "message", "label" = "Message", "desc" = "A custom message.")
|
||||
),
|
||||
)
|
||||
|
||||
data["security_level"] = security_level
|
||||
switch(security_level)
|
||||
if(SEC_LEVEL_BLUE)
|
||||
data["security_level_color"] = "blue";
|
||||
if(SEC_LEVEL_ORANGE)
|
||||
data["security_level_color"] = "orange";
|
||||
if(SEC_LEVEL_VIOLET)
|
||||
data["security_level_color"] = "violet";
|
||||
if(SEC_LEVEL_YELLOW)
|
||||
data["security_level_color"] = "yellow";
|
||||
if(SEC_LEVEL_GREEN)
|
||||
data["security_level_color"] = "green";
|
||||
if(SEC_LEVEL_RED)
|
||||
data["security_level_color"] = "red";
|
||||
else
|
||||
data["security_level_color"] = "purple";
|
||||
data["str_security_level"] = capitalize(get_security_level())
|
||||
data["levels"] = list(
|
||||
list("id" = SEC_LEVEL_GREEN, "name" = "Green", "icon" = "dove"),
|
||||
list("id" = SEC_LEVEL_YELLOW, "name" = "Yellow", "icon" = "exclamation-triangle"),
|
||||
list("id" = SEC_LEVEL_BLUE, "name" = "Blue", "icon" = "eye"),
|
||||
list("id" = SEC_LEVEL_ORANGE, "name" = "Orange", "icon" = "wrench"),
|
||||
list("id" = SEC_LEVEL_VIOLET, "name" = "Violet", "icon" = "biohazard"),
|
||||
)
|
||||
|
||||
var/datum/comm_message_listener/l = obtain_message_listener()
|
||||
data["messages"] = l.messages
|
||||
data["message_deletion_allowed"] = l != global_message_listener
|
||||
data["message_current_id"] = current_viewing_message_id
|
||||
data["message_current"] = current_viewing_message
|
||||
|
||||
// data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null
|
||||
data["msg_cooldown"] = message_cooldown ? (round((message_cooldown - world.time) / 10)) : 0
|
||||
data["cc_cooldown"] = centcomm_message_cooldown ? (round((centcomm_message_cooldown - world.time) / 10)) : 0
|
||||
|
||||
data["esc_callable"] = emergency_shuttle.location() && !emergency_shuttle.online() ? TRUE : FALSE
|
||||
data["esc_recallable"] = emergency_shuttle.location() && emergency_shuttle.online() ? TRUE : FALSE
|
||||
data["esc_status"] = FALSE
|
||||
if(emergency_shuttle.has_eta())
|
||||
var/timeleft = emergency_shuttle.estimate_arrival_time()
|
||||
data["esc_status"] = emergency_shuttle.online() ? "ETA:" : "RECALLING:"
|
||||
data["esc_status"] += " [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]"
|
||||
return data
|
||||
|
||||
/datum/tgui_module/communications/proc/setCurrentMessage(mob/user, value)
|
||||
current_viewing_message_id = value
|
||||
|
||||
var/datum/comm_message_listener/l = obtain_message_listener()
|
||||
for(var/list/m in l.messages)
|
||||
if(m["id"] == current_viewing_message_id)
|
||||
current_viewing_message = m
|
||||
|
||||
/datum/tgui_module/communications/proc/setMenuState(mob/user, value)
|
||||
if(isAI(user) || isrobot(user))
|
||||
ai_menu_state = value
|
||||
else
|
||||
menu_state = value
|
||||
|
||||
/datum/tgui_module/communications/proc/obtain_message_listener()
|
||||
if(istype(host, /datum/computer_file/program/comm))
|
||||
var/datum/computer_file/program/comm/P = host
|
||||
return P.message_core
|
||||
return global_message_listener
|
||||
|
||||
/proc/post_status(atom/source, command, data1, data2, mob/user = null)
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
|
||||
|
||||
if(!frequency)
|
||||
return
|
||||
|
||||
var/datum/signal/status_signal = new
|
||||
status_signal.source = source
|
||||
status_signal.transmission_method = TRANSMISSION_RADIO
|
||||
status_signal.data["command"] = command
|
||||
|
||||
switch(command)
|
||||
if("message")
|
||||
status_signal.data["msg1"] = data1
|
||||
status_signal.data["msg2"] = data2
|
||||
log_admin("STATUS: [user] set status screen message: [data1] [data2]")
|
||||
//message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]")
|
||||
if("alert")
|
||||
status_signal.data["picture_state"] = data1
|
||||
|
||||
frequency.post_signal(null, status_signal)
|
||||
|
||||
/datum/tgui_module/communications/tgui_act(action, params)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(using_map && !(get_z(usr) in using_map.contact_levels))
|
||||
to_chat(usr, "<span class='danger'>Unable to establish a connection: You're too far away from the station!</span>")
|
||||
return FALSE
|
||||
|
||||
. = TRUE
|
||||
if(action == "auth")
|
||||
if(!ishuman(usr))
|
||||
to_chat(usr, "<span class='warning'>Access denied.</span>")
|
||||
return FALSE
|
||||
// Logout function.
|
||||
if(authenticated != COMM_AUTHENTICATION_NONE)
|
||||
authenticated = COMM_AUTHENTICATION_NONE
|
||||
crew_announcement.announcer = null
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
return
|
||||
// Login function.
|
||||
if(check_access(usr, access_heads))
|
||||
authenticated = COMM_AUTHENTICATION_MIN
|
||||
if(check_access(usr, access_captain))
|
||||
authenticated = COMM_AUTHENTICATION_MAX
|
||||
var/mob/M = usr
|
||||
var/obj/item/weapon/card/id = M.GetIdCard()
|
||||
if(istype(id))
|
||||
crew_announcement.announcer = GetNameAndAssignmentFromId(id)
|
||||
if(authenticated == COMM_AUTHENTICATION_NONE)
|
||||
to_chat(usr, "<span class='warning'>You need to wear your ID.</span>")
|
||||
|
||||
// All functions below this point require authentication.
|
||||
if(!is_authenticated(usr))
|
||||
return FALSE
|
||||
|
||||
switch(action)
|
||||
// main interface
|
||||
if("main")
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("newalertlevel")
|
||||
if(isAI(usr) || isrobot(usr))
|
||||
to_chat(usr, "<span class='warning'>Firewalls prevent you from changing the alert level.</span>")
|
||||
return
|
||||
else if(isobserver(usr))
|
||||
var/mob/observer/dead/D = usr
|
||||
if(D.can_admin_interact())
|
||||
change_security_level(text2num(params["level"]))
|
||||
return TRUE
|
||||
else if(!ishuman(usr))
|
||||
to_chat(usr, "<span class='warning'>Security measures prevent you from changing the alert level.</span>")
|
||||
return
|
||||
|
||||
if(check_access(usr, access_captain))
|
||||
change_security_level(text2num(params["level"]))
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>You are not authorized to do this.</span>")
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("announce")
|
||||
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
|
||||
if(message_cooldown > world.time)
|
||||
to_chat(usr, "<span class='warning'>Please allow at least one minute to pass between announcements.</span>")
|
||||
return
|
||||
var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|message
|
||||
if(!input || message_cooldown > world.time || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
return
|
||||
if(length(input) < COMM_MSGLEN_MINIMUM)
|
||||
to_chat(usr, "<span class='warning'>Message '[input]' is too short. [COMM_MSGLEN_MINIMUM] character minimum.</span>")
|
||||
return
|
||||
crew_announcement.Announce(input)
|
||||
message_cooldown = world.time + 600 //One minute
|
||||
|
||||
if("callshuttle")
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
call_shuttle_proc(usr)
|
||||
if(emergency_shuttle.online())
|
||||
post_status(src, "shuttle", user = usr)
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("cancelshuttle")
|
||||
if(isAI(usr) || isrobot(usr))
|
||||
to_chat(usr, "<span class='warning'>Firewalls prevent you from recalling the shuttle.</span>")
|
||||
return
|
||||
var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
cancel_call_proc(usr)
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("messagelist")
|
||||
current_viewing_message = null
|
||||
current_viewing_message_id = null
|
||||
if(params["msgid"])
|
||||
setCurrentMessage(usr, text2num(params["msgid"]))
|
||||
setMenuState(usr, COMM_SCREEN_MESSAGES)
|
||||
|
||||
if("toggleatc")
|
||||
ATC.squelched = !ATC.squelched
|
||||
|
||||
if("delmessage")
|
||||
var/datum/comm_message_listener/l = obtain_message_listener()
|
||||
if(params["msgid"])
|
||||
setCurrentMessage(usr, text2num(params["msgid"]))
|
||||
var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
if(current_viewing_message)
|
||||
if(l != global_message_listener)
|
||||
l.Remove(current_viewing_message)
|
||||
current_viewing_message = null
|
||||
setMenuState(usr, COMM_SCREEN_MESSAGES)
|
||||
|
||||
if("status")
|
||||
setMenuState(usr, COMM_SCREEN_STAT)
|
||||
|
||||
// Status display stuff
|
||||
if("setstat")
|
||||
display_type = params["statdisp"]
|
||||
switch(display_type)
|
||||
if("message")
|
||||
post_status(src, "message", stat_msg1, stat_msg2, user = usr)
|
||||
if("alert")
|
||||
post_status(src, "alert", params["alert"], user = usr)
|
||||
else
|
||||
post_status(src, params["statdisp"], user = usr)
|
||||
|
||||
if("setmsg1")
|
||||
stat_msg1 = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", stat_msg1) as text|null, 40), 40)
|
||||
setMenuState(usr, COMM_SCREEN_STAT)
|
||||
|
||||
if("setmsg2")
|
||||
stat_msg2 = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", stat_msg2) as text|null, 40), 40)
|
||||
setMenuState(usr, COMM_SCREEN_STAT)
|
||||
|
||||
// OMG CENTCOMM LETTERHEAD
|
||||
if("MessageCentCom")
|
||||
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
|
||||
if(centcomm_message_cooldown > world.time)
|
||||
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
|
||||
return
|
||||
var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. \
|
||||
Please be aware that this process is very expensive, and abuse will lead to... termination. \
|
||||
Transmission does not guarantee a response. \
|
||||
There is a 30 second delay before you may send another message, be clear, full and concise.", "Central Command Quantum Messaging") as null|message)
|
||||
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
return
|
||||
if(length(input) < COMM_CCMSGLEN_MINIMUM)
|
||||
to_chat(usr, "<span class='warning'>Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.</span>")
|
||||
return
|
||||
CentCom_announce(input, usr)
|
||||
to_chat(usr, "<font color='blue'>Message transmitted.</font>")
|
||||
log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]")
|
||||
centcomm_message_cooldown = world.time + 300 // 30 seconds
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
// OMG SYNDICATE ...LETTERHEAD
|
||||
if("MessageSyndicate")
|
||||
if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (emagged))
|
||||
if(centcomm_message_cooldown > world.time)
|
||||
to_chat(usr, "Arrays recycling. Please stand by.")
|
||||
return
|
||||
var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", ""))
|
||||
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
return
|
||||
if(length(input) < COMM_CCMSGLEN_MINIMUM)
|
||||
to_chat(usr, "<span class='warning'>Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.</span>")
|
||||
return
|
||||
Syndicate_announce(input, usr)
|
||||
to_chat(usr, "<font color='blue'>Message transmitted.</font>")
|
||||
log_game("[key_name(usr)] has made an illegal announcement: [input]")
|
||||
centcomm_message_cooldown = world.time + 300 // 30 seconds
|
||||
|
||||
if("RestoreBackup")
|
||||
to_chat(usr, "Backup routing data restored!")
|
||||
emagged = FALSE
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
/datum/tgui_module/communications/ntos
|
||||
ntos = TRUE
|
||||
|
||||
/* Etc global procs */
|
||||
/proc/enable_prison_shuttle(var/mob/user)
|
||||
for(var/obj/machinery/computer/prison_shuttle/PS in machines)
|
||||
PS.allowedtocall = !(PS.allowedtocall)
|
||||
|
||||
/proc/call_shuttle_proc(var/mob/user)
|
||||
if ((!( ticker ) || !emergency_shuttle.location()))
|
||||
return
|
||||
|
||||
if(!universe.OnShuttleCall(usr))
|
||||
to_chat(user, "<span class='notice'>Cannot establish a bluespace connection.</span>")
|
||||
return
|
||||
|
||||
if(deathsquad.deployed)
|
||||
to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.")
|
||||
return
|
||||
|
||||
if(emergency_shuttle.deny_shuttle)
|
||||
to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.")
|
||||
return
|
||||
|
||||
if(world.time < 6000) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE
|
||||
to_chat(user, "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/600)] minute\s before trying again.")
|
||||
return
|
||||
|
||||
if(emergency_shuttle.going_to_centcom())
|
||||
to_chat(user, "The emergency shuttle may not be called while returning to [using_map.boss_short].")
|
||||
return
|
||||
|
||||
if(emergency_shuttle.online())
|
||||
to_chat(user, "The emergency shuttle is already on its way.")
|
||||
return
|
||||
|
||||
if(ticker.mode.name == "blob")
|
||||
to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.")
|
||||
return
|
||||
|
||||
emergency_shuttle.call_evac()
|
||||
log_game("[key_name(user)] has called the shuttle.")
|
||||
message_admins("[key_name_admin(user)] has called the shuttle.", 1)
|
||||
admin_chat_message(message = "Emergency evac beginning! Called by [key_name(user)]!", color = "#CC2222") //VOREStation Add
|
||||
|
||||
return
|
||||
|
||||
/proc/init_shift_change(var/mob/user, var/force = 0)
|
||||
if ((!( ticker ) || !emergency_shuttle.location()))
|
||||
return
|
||||
|
||||
if(emergency_shuttle.going_to_centcom())
|
||||
to_chat(user, "The shuttle may not be called while returning to [using_map.boss_short].")
|
||||
return
|
||||
|
||||
if(emergency_shuttle.online())
|
||||
to_chat(user, "The shuttle is already on its way.")
|
||||
return
|
||||
|
||||
// if force is 0, some things may stop the shuttle call
|
||||
if(!force)
|
||||
if(emergency_shuttle.deny_shuttle)
|
||||
to_chat(user, "[using_map.boss_short] does not currently have a shuttle available in your sector. Please try again later.")
|
||||
return
|
||||
|
||||
if(deathsquad.deployed == 1)
|
||||
to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.")
|
||||
return
|
||||
|
||||
if(world.time < 54000) // 30 minute grace period to let the game get going
|
||||
to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again.")
|
||||
return
|
||||
|
||||
if(ticker.mode.auto_recall_shuttle)
|
||||
//New version pretends to call the shuttle but cause the shuttle to return after a random duration.
|
||||
emergency_shuttle.auto_recall = 1
|
||||
|
||||
if(ticker.mode.name == "blob" || ticker.mode.name == "epidemic")
|
||||
to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.")
|
||||
return
|
||||
|
||||
emergency_shuttle.call_transfer()
|
||||
|
||||
//delay events in case of an autotransfer
|
||||
if (isnull(user))
|
||||
SSevents.delay_events(EVENT_LEVEL_MODERATE, 9000) //15 minutes
|
||||
SSevents.delay_events(EVENT_LEVEL_MAJOR, 9000)
|
||||
|
||||
log_game("[user? key_name(user) : "Autotransfer"] has called the shuttle.")
|
||||
message_admins("[user? key_name_admin(user) : "Autotransfer"] has called the shuttle.", 1)
|
||||
admin_chat_message(message = "Autotransfer shuttle dispatched, shift ending soon.", color = "#2277BB") //VOREStation Add
|
||||
|
||||
return
|
||||
|
||||
/proc/cancel_call_proc(var/mob/user)
|
||||
if (!( ticker ) || !emergency_shuttle.can_recall())
|
||||
return
|
||||
if((ticker.mode.name == "blob")||(ticker.mode.name == "Meteor"))
|
||||
return
|
||||
|
||||
if(!emergency_shuttle.going_to_centcom()) //check that shuttle isn't already heading to CentCom
|
||||
emergency_shuttle.recall()
|
||||
log_game("[key_name(user)] has recalled the shuttle.")
|
||||
message_admins("[key_name_admin(user)] has recalled the shuttle.", 1)
|
||||
return
|
||||
|
||||
/proc/is_relay_online()
|
||||
for(var/obj/machinery/telecomms/relay/M in world)
|
||||
if(M.stat == 0)
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,235 @@
|
||||
// This really should be used for both regular ID computers and NTOS, but
|
||||
// the data structures are just different enough right now that I can't be assed
|
||||
/datum/tgui_module/cardmod
|
||||
name = "ID card modification program"
|
||||
ntos = TRUE
|
||||
tgui_id = "IdentificationComputer"
|
||||
var/mod_mode = 1
|
||||
var/is_centcom = 0
|
||||
|
||||
/datum/tgui_module/cardmod/tgui_static_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(data_core)
|
||||
data_core.get_manifest_list()
|
||||
data["manifest"] = PDA_Manifest
|
||||
return data
|
||||
|
||||
/datum/tgui_module/cardmod/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/datum/computer_file/program/card_mod/program = host
|
||||
if(!istype(program))
|
||||
return 0
|
||||
var/list/data = ..()
|
||||
data["station_name"] = station_name()
|
||||
data["mode"] = mod_mode
|
||||
data["printing"] = FALSE
|
||||
if(program && program.computer)
|
||||
data["have_id_slot"] = !!program.computer.card_slot
|
||||
data["have_printer"] = !!program.computer.nano_printer
|
||||
data["authenticated"] = program.can_run(user)
|
||||
if(!program.computer.card_slot)
|
||||
mod_mode = 0 //We can't modify IDs when there is no card reader
|
||||
else
|
||||
data["have_id_slot"] = 0
|
||||
data["have_printer"] = 0
|
||||
data["authenticated"] = 0
|
||||
data["centcom_access"] = is_centcom
|
||||
|
||||
|
||||
data["has_modify"] = null
|
||||
data["account_number"] = null
|
||||
data["id_rank"] = null
|
||||
data["target_owner"] = null
|
||||
data["target_name"] = null
|
||||
if(program && program.computer && program.computer.card_slot)
|
||||
var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
|
||||
data["has_modify"] = !!id_card
|
||||
data["account_number"] = id_card ? id_card.associated_account_number : null
|
||||
data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned"
|
||||
data["target_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----"
|
||||
data["target_name"] = id_card ? id_card.name : "-----"
|
||||
|
||||
var/list/departments = list()
|
||||
for(var/D in SSjob.get_all_department_datums())
|
||||
var/datum/department/dept = D
|
||||
if(!dept.assignable) // No AI ID cards for you.
|
||||
continue
|
||||
if(dept.centcom_only && !is_centcom)
|
||||
continue
|
||||
departments.Add(list(list(
|
||||
"department_name" = dept.name,
|
||||
"jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)),
|
||||
)))
|
||||
|
||||
data["departments"] = departments
|
||||
|
||||
var/list/all_centcom_access = list()
|
||||
var/list/regions = list()
|
||||
if(program.computer.card_slot && program.computer.card_slot.stored_card)
|
||||
var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
|
||||
if(is_centcom)
|
||||
for(var/access in get_all_centcom_access())
|
||||
all_centcom_access.Add(list(list(
|
||||
"desc" = replacetext(get_centcom_access_desc(access), " ", " "),
|
||||
"ref" = access,
|
||||
"allowed" = (access in id_card.access) ? 1 : 0)))
|
||||
data["all_centcom_access"] = all_centcom_access
|
||||
else
|
||||
for(var/i in ACCESS_REGION_SECURITY to ACCESS_REGION_SUPPLY)
|
||||
var/list/accesses = list()
|
||||
for(var/access in get_region_accesses(i))
|
||||
if(get_access_desc(access))
|
||||
accesses.Add(list(list(
|
||||
"desc" = replacetext(get_access_desc(access), " ", " "),
|
||||
"ref" = access,
|
||||
"allowed" = (access in id_card.access) ? 1 : 0)))
|
||||
|
||||
regions.Add(list(list(
|
||||
"name" = get_region_accesses_name(i),
|
||||
"accesses" = accesses)))
|
||||
data["regions"] = regions
|
||||
|
||||
data["regions"] = regions
|
||||
data["all_centcom_access"] = all_centcom_access
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_module/cardmod/proc/format_jobs(list/jobs)
|
||||
var/datum/computer_file/program/card_mod/program = host
|
||||
if(!istype(program))
|
||||
return null
|
||||
|
||||
var/obj/item/weapon/card/id/id_card = program.computer.card_slot ? program.computer.card_slot.stored_card : null
|
||||
var/list/formatted = list()
|
||||
for(var/job in jobs)
|
||||
formatted.Add(list(list(
|
||||
"display_name" = replacetext(job, " ", " "),
|
||||
"target_rank" = id_card && id_card.assignment ? id_card.assignment : "Unassigned",
|
||||
"job" = job)))
|
||||
|
||||
return formatted
|
||||
|
||||
/datum/tgui_module/cardmod/tgui_act(action, list/params, datum/tgui/ui)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
|
||||
var/datum/computer_file/program/card_mod/program = host
|
||||
if(!istype(program))
|
||||
return TRUE
|
||||
var/obj/item/modular_computer/computer = tgui_host()
|
||||
if(!istype(computer))
|
||||
return TRUE
|
||||
|
||||
var/obj/item/weapon/card/id/user_id_card = usr.GetIdCard()
|
||||
var/obj/item/weapon/card/id/id_card
|
||||
if(computer.card_slot)
|
||||
id_card = computer.card_slot.stored_card
|
||||
|
||||
switch(action)
|
||||
if("mode")
|
||||
mod_mode = clamp(text2num(params["mode_target"]), 0, 1)
|
||||
. = TRUE
|
||||
if("print")
|
||||
if(computer && computer.nano_printer) //This option should never be called if there is no printer
|
||||
if(!mod_mode)
|
||||
if(program.can_run(usr, 1))
|
||||
var/contents = {"<h4>Access Report</h4>
|
||||
<u>Prepared By:</u> [user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]<br>
|
||||
<u>For:</u> [id_card.registered_name ? id_card.registered_name : "Unregistered"]<br>
|
||||
<hr>
|
||||
<u>Assignment:</u> [id_card.assignment]<br>
|
||||
<u>Account Number:</u> #[id_card.associated_account_number]<br>
|
||||
<u>Blood Type:</u> [id_card.blood_type]<br><br>
|
||||
<u>Access:</u><br>
|
||||
"}
|
||||
|
||||
var/known_access_rights = get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM)
|
||||
for(var/A in id_card.access)
|
||||
if(A in known_access_rights)
|
||||
contents += " [get_access_desc(A)]"
|
||||
|
||||
if(!computer.nano_printer.print_text(contents,"access report"))
|
||||
to_chat(usr, "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>")
|
||||
return
|
||||
else
|
||||
computer.visible_message("<span class='notice'>\The [computer] prints out paper.</span>")
|
||||
else
|
||||
var/contents = {"<h4>Crew Manifest</h4>
|
||||
<br>
|
||||
[data_core ? data_core.get_manifest(0) : ""]
|
||||
"}
|
||||
if(!computer.nano_printer.print_text(contents,text("crew manifest ([])", stationtime2text())))
|
||||
to_chat(usr, "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>")
|
||||
return
|
||||
else
|
||||
computer.visible_message("<span class='notice'>\The [computer] prints out paper.</span>")
|
||||
. = TRUE
|
||||
if("modify")
|
||||
if(computer && computer.card_slot)
|
||||
if(id_card)
|
||||
data_core.manifest_modify(id_card.registered_name, id_card.assignment)
|
||||
computer.proc_eject_id(usr)
|
||||
. = TRUE
|
||||
if("terminate")
|
||||
if(computer && program.can_run(usr, 1))
|
||||
id_card.assignment = "Dismissed" //VOREStation Edit: setting adjustment
|
||||
id_card.access = list()
|
||||
callHook("terminate_employee", list(id_card))
|
||||
. = TRUE
|
||||
if("reg")
|
||||
if(computer && program.can_run(usr, 1))
|
||||
var/temp_name = sanitizeName(params["reg"], allow_numbers = TRUE)
|
||||
if(temp_name)
|
||||
id_card.registered_name = temp_name
|
||||
else
|
||||
computer.visible_message("<span class='notice'>[computer] buzzes rudely.</span>")
|
||||
. = TRUE
|
||||
if("account")
|
||||
if(computer && program.can_run(usr, 1))
|
||||
var/account_num = text2num(params["account"])
|
||||
id_card.associated_account_number = account_num
|
||||
. = TRUE
|
||||
if("assign")
|
||||
if(computer && program.can_run(usr, 1) && id_card)
|
||||
var/t1 = params["assign_target"]
|
||||
if(t1 == "Custom")
|
||||
var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45)
|
||||
//let custom jobs function as an impromptu alt title, mainly for sechuds
|
||||
if(temp_t)
|
||||
id_card.assignment = temp_t
|
||||
else
|
||||
var/list/access = list()
|
||||
if(is_centcom)
|
||||
access = get_centcom_access(t1)
|
||||
else
|
||||
var/datum/job/jobdatum
|
||||
for(var/jobtype in typesof(/datum/job))
|
||||
var/datum/job/J = new jobtype
|
||||
if(ckey(J.title) == ckey(t1))
|
||||
jobdatum = J
|
||||
break
|
||||
if(!jobdatum)
|
||||
to_chat(usr, "<span class='warning'>No log exists for this job: [t1]</span>")
|
||||
return
|
||||
|
||||
access = jobdatum.get_access()
|
||||
|
||||
id_card.access = access
|
||||
id_card.assignment = t1
|
||||
id_card.rank = t1
|
||||
|
||||
callHook("reassign_employee", list(id_card))
|
||||
. = TRUE
|
||||
if("access")
|
||||
if(computer && program.can_run(usr, 1))
|
||||
var/access_type = text2num(params["access_target"])
|
||||
var/access_allowed = text2num(params["allowed"])
|
||||
if(access_type in get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM))
|
||||
id_card.access -= access_type
|
||||
if(!access_allowed)
|
||||
id_card.access += access_type
|
||||
. = TRUE
|
||||
|
||||
if(id_card)
|
||||
id_card.name = text("[id_card.registered_name]'s ID Card ([id_card.assignment])")
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/datum/tgui_module/computer_configurator
|
||||
name = "NTOS Computer Configuration Tool"
|
||||
ntos = TRUE
|
||||
tgui_id = "Configuration"
|
||||
var/obj/item/modular_computer/movable = null
|
||||
|
||||
/datum/tgui_module/computer_configurator/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
movable = tgui_host()
|
||||
// No computer connection, we can't get data from that.
|
||||
if(!istype(movable))
|
||||
return 0
|
||||
|
||||
var/list/data = ..()
|
||||
|
||||
data["disk_size"] = movable.hard_drive.max_capacity
|
||||
data["disk_used"] = movable.hard_drive.used_capacity
|
||||
data["power_usage"] = movable.last_power_usage
|
||||
data["battery_exists"] = movable.battery_module ? 1 : 0
|
||||
if(movable.battery_module)
|
||||
data["battery_rating"] = movable.battery_module.battery.maxcharge
|
||||
data["battery_percent"] = round(movable.battery_module.battery.percent())
|
||||
|
||||
if(movable.battery_module && movable.battery_module.battery)
|
||||
data["battery"] = list("max" = movable.battery_module.battery.maxcharge, "charge" = round(movable.battery_module.battery.charge))
|
||||
|
||||
var/list/hardware = movable.get_all_components()
|
||||
var/list/all_entries[0]
|
||||
for(var/obj/item/weapon/computer_hardware/H in hardware)
|
||||
all_entries.Add(list(list(
|
||||
"name" = H.name,
|
||||
"desc" = H.desc,
|
||||
"enabled" = H.enabled,
|
||||
"critical" = H.critical,
|
||||
"powerusage" = H.power_usage
|
||||
)))
|
||||
|
||||
data["hardware"] = all_entries
|
||||
return data
|
||||
|
||||
/datum/tgui_module/computer_configurator/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("PC_toggle_component")
|
||||
var/obj/item/weapon/computer_hardware/H = movable.find_hardware_by_name(params["name"])
|
||||
if(H && istype(H))
|
||||
H.enabled = !H.enabled
|
||||
. = TRUE
|
||||
@@ -0,0 +1,476 @@
|
||||
/datum/tgui_module/email_client
|
||||
name = "Email Client"
|
||||
tgui_id = "NtosEmailClient"
|
||||
|
||||
var/stored_login = ""
|
||||
var/stored_password = ""
|
||||
var/error = ""
|
||||
|
||||
var/msg_title = ""
|
||||
var/msg_body = ""
|
||||
var/msg_recipient = ""
|
||||
var/datum/computer_file/msg_attachment = null
|
||||
var/folder = "Inbox"
|
||||
var/addressbook = FALSE
|
||||
var/new_message = FALSE
|
||||
|
||||
var/last_message_count = 0 // How many messages were there during last check.
|
||||
var/read_message_count = 0 // How many messages were there when user has last accessed the UI.
|
||||
|
||||
var/datum/computer_file/downloading = null
|
||||
var/download_progress = 0
|
||||
var/download_speed = 0
|
||||
|
||||
var/datum/computer_file/data/email_account/current_account = null
|
||||
var/datum/computer_file/data/email_message/current_message = null
|
||||
|
||||
/datum/tgui_module/email_client/proc/log_in()
|
||||
for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts)
|
||||
if(!account.can_login)
|
||||
continue
|
||||
if(account.login == stored_login)
|
||||
if(account.password == stored_password)
|
||||
if(account.suspended)
|
||||
error = "This account has been suspended. Please contact the system administrator for assistance."
|
||||
return 0
|
||||
current_account = account
|
||||
return 1
|
||||
else
|
||||
error = "Invalid Password"
|
||||
return 0
|
||||
error = "Invalid Login"
|
||||
return 0
|
||||
|
||||
// Returns 0 if no new messages were received, 1 if there is an unread message but notification has already been sent.
|
||||
// and 2 if there is a new message that appeared in this tick (and therefore notification should be sent by the program).
|
||||
/datum/tgui_module/email_client/proc/check_for_new_messages(var/messages_read = FALSE)
|
||||
if(!current_account)
|
||||
return 0
|
||||
|
||||
var/list/allmails = current_account.all_emails()
|
||||
|
||||
if(allmails.len > last_message_count)
|
||||
. = 2
|
||||
else if(allmails.len > read_message_count)
|
||||
. = 1
|
||||
else
|
||||
. = 0
|
||||
|
||||
last_message_count = allmails.len
|
||||
if(messages_read)
|
||||
read_message_count = allmails.len
|
||||
|
||||
/datum/tgui_module/email_client/proc/log_out()
|
||||
current_account = null
|
||||
downloading = null
|
||||
download_progress = 0
|
||||
last_message_count = 0
|
||||
read_message_count = 0
|
||||
|
||||
/datum/tgui_module/email_client/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/list/data = ..()
|
||||
|
||||
// Password has been changed by other client connected to this email account
|
||||
if(current_account)
|
||||
if(current_account.password != stored_password)
|
||||
log_out()
|
||||
error = "Invalid Password"
|
||||
// Banned.
|
||||
if(current_account.suspended)
|
||||
log_out()
|
||||
error = "This account has been suspended. Please contact the system administrator for assistance."
|
||||
|
||||
// So, TGUI has a bug/feature where it just conveniently doesn't bother to clear out old data if it only gets
|
||||
// a partial data update; as such, we have to make sure to null all of these out ourselves so the UI works properly.
|
||||
data["accounts"] = null
|
||||
data["addressbook"] = null
|
||||
data["cur_attachment_filename"] = null
|
||||
data["cur_attachment_size"] = null
|
||||
data["cur_body"] = null
|
||||
data["cur_hasattachment"] = null
|
||||
data["cur_source"] = null
|
||||
data["cur_timestamp"] = null
|
||||
data["cur_title"] = null
|
||||
data["cur_uid"] = null
|
||||
data["current_account"] = null
|
||||
data["down_filename"] = null
|
||||
data["down_progress"] = null
|
||||
data["down_size"] = null
|
||||
data["down_speed"] = null
|
||||
data["downloading"] = null
|
||||
data["error"] = null
|
||||
data["folder"] = null
|
||||
data["label_deleted"] = null
|
||||
data["label_inbox"] = null
|
||||
data["label_spam"] = null
|
||||
data["messagecount"] = null
|
||||
data["messages"] = null
|
||||
data["msg_attachment_filename"] = null
|
||||
data["msg_attachment_size"] = null
|
||||
data["msg_body"] = null
|
||||
data["msg_hasattachment"] = null
|
||||
data["msg_recipient"] = null
|
||||
data["msg_title"] = null
|
||||
data["new_message"] = null
|
||||
data["stored_login"] = null
|
||||
data["stored_password"] = null
|
||||
|
||||
if(error)
|
||||
data["error"] = error
|
||||
else if(downloading)
|
||||
data["downloading"] = 1
|
||||
data["down_filename"] = "[downloading.filename].[downloading.filetype]"
|
||||
data["down_progress"] = download_progress
|
||||
data["down_size"] = downloading.size
|
||||
data["down_speed"] = download_speed
|
||||
|
||||
else if(istype(current_account))
|
||||
data["current_account"] = current_account.login
|
||||
if(addressbook)
|
||||
var/list/all_accounts = list()
|
||||
for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts)
|
||||
if(!account.can_login)
|
||||
continue
|
||||
all_accounts.Add(list(list(
|
||||
"login" = account.login
|
||||
)))
|
||||
data["addressbook"] = 1
|
||||
data["accounts"] = all_accounts
|
||||
else if(new_message)
|
||||
data["new_message"] = 1
|
||||
data["msg_title"] = msg_title
|
||||
data["msg_body"] = pencode2html(msg_body)
|
||||
data["msg_recipient"] = msg_recipient
|
||||
if(msg_attachment)
|
||||
data["msg_hasattachment"] = 1
|
||||
data["msg_attachment_filename"] = "[msg_attachment.filename].[msg_attachment.filetype]"
|
||||
data["msg_attachment_size"] = msg_attachment.size
|
||||
else if (current_message)
|
||||
data["cur_title"] = current_message.title
|
||||
data["cur_body"] = pencode2html(current_message.stored_data)
|
||||
data["cur_timestamp"] = current_message.timestamp
|
||||
data["cur_source"] = current_message.source
|
||||
data["cur_uid"] = current_message.uid
|
||||
if(istype(current_message.attachment))
|
||||
data["cur_hasattachment"] = 1
|
||||
data["cur_attachment_filename"] = "[current_message.attachment.filename].[current_message.attachment.filetype]"
|
||||
data["cur_attachment_size"] = current_message.attachment.size
|
||||
else
|
||||
data["label_inbox"] = "Inbox ([current_account.inbox.len])"
|
||||
data["label_spam"] = "Spam ([current_account.spam.len])"
|
||||
data["label_deleted"] = "Deleted ([current_account.deleted.len])"
|
||||
var/list/message_source
|
||||
if(folder == "Inbox")
|
||||
message_source = current_account.inbox
|
||||
else if(folder == "Spam")
|
||||
message_source = current_account.spam
|
||||
else if(folder == "Deleted")
|
||||
message_source = current_account.deleted
|
||||
|
||||
if(message_source)
|
||||
data["folder"] = folder
|
||||
var/list/all_messages = list()
|
||||
for(var/datum/computer_file/data/email_message/message in message_source)
|
||||
all_messages.Add(list(list(
|
||||
"title" = message.title,
|
||||
"body" = pencode2html(message.stored_data),
|
||||
"source" = message.source,
|
||||
"timestamp" = message.timestamp,
|
||||
"uid" = message.uid
|
||||
)))
|
||||
data["messages"] = all_messages
|
||||
data["messagecount"] = all_messages.len
|
||||
else
|
||||
data["stored_login"] = stored_login
|
||||
data["stored_password"] = stars(stored_password, 0)
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_module/email_client/proc/find_message_by_fuid(var/fuid)
|
||||
if(!istype(current_account))
|
||||
return
|
||||
|
||||
// params works with strings, so this makes it a bit easier for us
|
||||
if(istext(fuid))
|
||||
fuid = text2num(fuid)
|
||||
|
||||
for(var/datum/computer_file/data/email_message/message in current_account.all_emails())
|
||||
if(message.uid == fuid)
|
||||
return message
|
||||
|
||||
/datum/tgui_module/email_client/proc/clear_message()
|
||||
new_message = FALSE
|
||||
msg_title = ""
|
||||
msg_body = ""
|
||||
msg_recipient = ""
|
||||
msg_attachment = null
|
||||
current_message = null
|
||||
|
||||
/datum/tgui_module/email_client/proc/relayed_process(var/netspeed)
|
||||
download_speed = netspeed
|
||||
if(!downloading)
|
||||
return
|
||||
download_progress = min(download_progress + netspeed, downloading.size)
|
||||
if(download_progress >= downloading.size)
|
||||
var/obj/item/modular_computer/MC = tgui_host()
|
||||
if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality())
|
||||
error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?"
|
||||
downloading = null
|
||||
download_progress = 0
|
||||
return 1
|
||||
|
||||
if(MC.hard_drive.store_file(downloading))
|
||||
error = "File successfully downloaded to local device."
|
||||
else
|
||||
error = "Error saving file: I/O Error: The hard drive may be full or nonfunctional."
|
||||
downloading = null
|
||||
download_progress = 0
|
||||
return 1
|
||||
|
||||
|
||||
/datum/tgui_module/email_client/tgui_act(action, params)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
var/mob/living/user = usr
|
||||
check_for_new_messages(1) // Any actual interaction (button pressing) is considered as acknowledging received message, for the purpose of notification icons.
|
||||
|
||||
switch(action)
|
||||
if("login")
|
||||
log_in()
|
||||
return 1
|
||||
|
||||
if("logout")
|
||||
log_out()
|
||||
return 1
|
||||
|
||||
if("reset")
|
||||
error = ""
|
||||
return 1
|
||||
|
||||
if("new_message")
|
||||
new_message = TRUE
|
||||
return 1
|
||||
|
||||
if("cancel")
|
||||
if(addressbook)
|
||||
addressbook = FALSE
|
||||
else
|
||||
clear_message()
|
||||
return 1
|
||||
|
||||
if("addressbook")
|
||||
addressbook = TRUE
|
||||
return 1
|
||||
|
||||
if("set_recipient")
|
||||
msg_recipient = sanitize(params["set_recipient"])
|
||||
addressbook = FALSE
|
||||
return 1
|
||||
|
||||
if("edit_title")
|
||||
var/newtitle = sanitize(params["val"], 100)
|
||||
if(newtitle)
|
||||
msg_title = newtitle
|
||||
return 1
|
||||
|
||||
// This uses similar editing mechanism as the FileManager program, therefore it supports various paper tags and remembers formatting.
|
||||
if("edit_body")
|
||||
var/oldtext = html_decode(msg_body)
|
||||
oldtext = replacetext(oldtext, "\[editorbr\]", "\n")
|
||||
|
||||
var/newtext = sanitize(replacetext(input(usr, "Enter your message. You may use most tags from paper formatting", "Message Editor", oldtext) as message|null, "\n", "\[editorbr\]"), 20000)
|
||||
if(newtext)
|
||||
msg_body = newtext
|
||||
return 1
|
||||
|
||||
if("edit_recipient")
|
||||
var/newrecipient = sanitize(params["val"], 100)
|
||||
if(newrecipient)
|
||||
msg_recipient = newrecipient
|
||||
return 1
|
||||
|
||||
if("edit_login")
|
||||
var/newlogin = sanitize(params["val"], 100)
|
||||
if(newlogin)
|
||||
stored_login = newlogin
|
||||
return 1
|
||||
|
||||
if("edit_password")
|
||||
var/newpass = sanitize(params["val"], 100)
|
||||
if(newpass)
|
||||
stored_password = newpass
|
||||
return 1
|
||||
|
||||
if("delete")
|
||||
if(!istype(current_account))
|
||||
return 1
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["delete"])
|
||||
if(!istype(M))
|
||||
return 1
|
||||
if(folder == "Deleted")
|
||||
current_account.deleted.Remove(M)
|
||||
qdel(M)
|
||||
else
|
||||
current_account.deleted.Add(M)
|
||||
current_account.inbox.Remove(M)
|
||||
current_account.spam.Remove(M)
|
||||
if(current_message == M)
|
||||
current_message = null
|
||||
return 1
|
||||
|
||||
if("send")
|
||||
if(!current_account)
|
||||
return 1
|
||||
if((msg_title == "") || (msg_body == "") || (msg_recipient == ""))
|
||||
error = "Error sending mail: Title or message body is empty!"
|
||||
return 1
|
||||
|
||||
var/datum/computer_file/data/email_message/message = new()
|
||||
message.title = msg_title
|
||||
message.stored_data = msg_body
|
||||
message.source = current_account.login
|
||||
message.attachment = msg_attachment
|
||||
if(!current_account.send_mail(msg_recipient, message))
|
||||
error = "Error sending email: this address doesn't exist."
|
||||
return 1
|
||||
else
|
||||
error = "Email successfully sent."
|
||||
clear_message()
|
||||
return 1
|
||||
|
||||
if("set_folder")
|
||||
folder = params["set_folder"]
|
||||
return 1
|
||||
|
||||
if("reply")
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["reply"])
|
||||
if(!istype(M))
|
||||
return 1
|
||||
|
||||
new_message = TRUE
|
||||
msg_recipient = M.source
|
||||
msg_title = "Re: [M.title]"
|
||||
msg_body = "\[editorbr\]\[editorbr\]\[editorbr\]\[br\]==============================\[br\]\[editorbr\]"
|
||||
msg_body += "Received by [current_account.login] at [M.timestamp]\[br\]\[editorbr\][M.stored_data]"
|
||||
return 1
|
||||
|
||||
if("view")
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["view"])
|
||||
if(istype(M))
|
||||
current_message = M
|
||||
return 1
|
||||
|
||||
if("changepassword")
|
||||
var/oldpassword = sanitize(input(user,"Please enter your old password:", "Password Change"), 100)
|
||||
if(!oldpassword)
|
||||
return 1
|
||||
var/newpassword1 = sanitize(input(user,"Please enter your new password:", "Password Change"), 100)
|
||||
if(!newpassword1)
|
||||
return 1
|
||||
var/newpassword2 = sanitize(input(user,"Please re-enter your new password:", "Password Change"), 100)
|
||||
if(!newpassword2)
|
||||
return 1
|
||||
|
||||
if(!istype(current_account))
|
||||
error = "Please log in before proceeding."
|
||||
return 1
|
||||
|
||||
if(current_account.password != oldpassword)
|
||||
error = "Incorrect original password"
|
||||
return 1
|
||||
|
||||
if(newpassword1 != newpassword2)
|
||||
error = "The entered passwords do not match."
|
||||
return 1
|
||||
|
||||
current_account.password = newpassword1
|
||||
stored_password = newpassword1
|
||||
error = "Your password has been successfully changed!"
|
||||
return 1
|
||||
|
||||
// The following entries are Modular Computer framework only, and therefore won't do anything in other cases (like AI View)
|
||||
|
||||
if("save")
|
||||
// Fully dependant on modular computers here.
|
||||
var/obj/item/modular_computer/MC = tgui_host()
|
||||
|
||||
if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality())
|
||||
error = "Error exporting file. Are you using a functional and NTOS-compliant device?"
|
||||
return 1
|
||||
|
||||
var/filename = sanitize(input(user,"Please specify file name:", "Message export"), 100)
|
||||
if(!filename)
|
||||
return 1
|
||||
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["save"])
|
||||
var/datum/computer_file/data/mail = istype(M) ? M.export() : null
|
||||
if(!istype(mail))
|
||||
return 1
|
||||
mail.filename = filename
|
||||
if(!MC.hard_drive || !MC.hard_drive.store_file(mail))
|
||||
error = "Internal I/O error when writing file, the hard drive may be full."
|
||||
else
|
||||
error = "Email exported successfully"
|
||||
return 1
|
||||
|
||||
if("addattachment")
|
||||
var/obj/item/modular_computer/MC = tgui_host()
|
||||
msg_attachment = null
|
||||
|
||||
if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality())
|
||||
error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?"
|
||||
return 1
|
||||
|
||||
var/list/filenames = list()
|
||||
for(var/datum/computer_file/CF in MC.hard_drive.stored_files)
|
||||
if(CF.unsendable)
|
||||
continue
|
||||
filenames.Add(CF.filename)
|
||||
var/picked_file = input(user, "Please pick a file to send as attachment (max 32GQ)") as null|anything in filenames
|
||||
|
||||
if(!picked_file)
|
||||
return 1
|
||||
|
||||
if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality())
|
||||
error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?"
|
||||
return 1
|
||||
|
||||
for(var/datum/computer_file/CF in MC.hard_drive.stored_files)
|
||||
if(CF.unsendable)
|
||||
continue
|
||||
if(CF.filename == picked_file)
|
||||
msg_attachment = CF.clone()
|
||||
break
|
||||
if(!istype(msg_attachment))
|
||||
msg_attachment = null
|
||||
error = "Unknown error when uploading attachment."
|
||||
return 1
|
||||
|
||||
if(msg_attachment.size > 32)
|
||||
error = "Error uploading attachment: File exceeds maximal permitted file size of 32GQ."
|
||||
msg_attachment = null
|
||||
else
|
||||
error = "File [msg_attachment.filename].[msg_attachment.filetype] has been successfully uploaded."
|
||||
return 1
|
||||
|
||||
if("downloadattachment")
|
||||
if(!current_account || !current_message || !current_message.attachment)
|
||||
return 1
|
||||
var/obj/item/modular_computer/MC = tgui_host()
|
||||
if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality())
|
||||
error = "Error downloading file. Are you using a functional and NTOSv2-compliant device?"
|
||||
return 1
|
||||
|
||||
downloading = current_message.attachment.clone()
|
||||
download_progress = 0
|
||||
return 1
|
||||
|
||||
if("canceldownload")
|
||||
downloading = null
|
||||
download_progress = 0
|
||||
return 1
|
||||
|
||||
if("remove_attachment")
|
||||
msg_attachment = null
|
||||
return 1
|
||||
@@ -0,0 +1,241 @@
|
||||
/datum/tgui_module/uav
|
||||
name = "UAV Control"
|
||||
tgui_id = "UAV"
|
||||
ntos = TRUE
|
||||
var/obj/item/device/uav/current_uav = null //The UAV we're watching
|
||||
var/signal_strength = 0 //Our last signal strength report (cached for a few seconds)
|
||||
var/signal_test_counter = 0 //How long until next signal strength check
|
||||
var/list/viewers //Who's viewing a UAV through us
|
||||
var/adhoc_range = 30 //How far we can operate on a UAV without NTnet
|
||||
|
||||
/datum/tgui_module/uav/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/list/data = ..()
|
||||
|
||||
if(current_uav)
|
||||
if(QDELETED(current_uav))
|
||||
set_current(null)
|
||||
else if(signal_test_counter-- <= 0)
|
||||
signal_strength = get_signal_to(current_uav)
|
||||
if(!signal_strength)
|
||||
set_current(null)
|
||||
else // Don't reset counter until we find a UAV that's actually in range we can stay connected to
|
||||
signal_test_counter = 20
|
||||
|
||||
data["current_uav"] = null
|
||||
if(current_uav)
|
||||
data["current_uav"] = list("status" = current_uav.get_status_string(), "power" = current_uav.state == 1 ? 1 : null)
|
||||
data["signal_strength"] = signal_strength ? signal_strength >= 2 ? "High" : "Low" : "None"
|
||||
data["in_use"] = LAZYLEN(viewers)
|
||||
|
||||
var/list/paired_map = list()
|
||||
var/obj/item/modular_computer/mc_host = tgui_host()
|
||||
if(istype(mc_host))
|
||||
for(var/puav in mc_host.paired_uavs)
|
||||
var/weakref/wr = puav
|
||||
var/obj/item/device/uav/U = wr.resolve()
|
||||
paired_map.Add(list(list("name" = "[U ? U.nickname : "!!Missing!!"]", "uavref" = "\ref[U]")))
|
||||
|
||||
data["paired_uavs"] = paired_map
|
||||
return data
|
||||
|
||||
/datum/tgui_module/uav/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
switch(action)
|
||||
if("switch_uav")
|
||||
var/obj/item/device/uav/U = locate(params["switch_uav"]) //This is a \ref to the UAV itself
|
||||
if(!istype(U))
|
||||
to_chat(usr,"<span class='warning'>Something is blocking the connection to that UAV. In-person investigation is required.</span>")
|
||||
return FALSE
|
||||
|
||||
if(!get_signal_to(U))
|
||||
to_chat(usr,"<span class='warning'>The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.</span>")
|
||||
return FALSE
|
||||
|
||||
set_current(U)
|
||||
return TRUE
|
||||
|
||||
if("del_uav")
|
||||
var/refstring = params["del_uav"] //This is a \ref to the UAV itself
|
||||
var/obj/item/modular_computer/mc_host = tgui_host()
|
||||
//This is so we can really scrape up any weakrefs that can't resolve
|
||||
for(var/weakref/wr in mc_host.paired_uavs)
|
||||
if(wr.ref == refstring)
|
||||
if(current_uav?.weakref == wr)
|
||||
set_current(null)
|
||||
LAZYREMOVE(mc_host.paired_uavs, wr)
|
||||
return TRUE
|
||||
|
||||
if("view_uav")
|
||||
if(!current_uav)
|
||||
return FALSE
|
||||
|
||||
if(current_uav.check_eye(usr) < 0)
|
||||
to_chat(usr,"<span class='warning'>The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.</span>")
|
||||
else
|
||||
viewing_uav(usr) ? unlook(usr) : look(usr)
|
||||
return TRUE
|
||||
|
||||
if("power_uav")
|
||||
if(!current_uav)
|
||||
return FALSE
|
||||
else if(current_uav.toggle_power())
|
||||
//Clean up viewers faster
|
||||
if(LAZYLEN(viewers))
|
||||
for(var/weakref/W in viewers)
|
||||
var/M = W.resolve()
|
||||
if(M)
|
||||
unlook(M)
|
||||
return TRUE
|
||||
|
||||
/datum/tgui_module/uav/proc/set_current(var/obj/item/device/uav/U)
|
||||
if(current_uav == U)
|
||||
return
|
||||
|
||||
signal_strength = 0
|
||||
current_uav = U
|
||||
|
||||
if(LAZYLEN(viewers))
|
||||
for(var/weakref/W in viewers)
|
||||
var/M = W.resolve()
|
||||
if(M)
|
||||
if(current_uav)
|
||||
to_chat(M, "<span class='warning'>You're disconnected from the UAV's camera!</span>")
|
||||
unlook(M)
|
||||
else
|
||||
look(M)
|
||||
|
||||
////
|
||||
//// Finding signal strength between us and the UAV
|
||||
////
|
||||
/datum/tgui_module/uav/proc/get_signal_to(atom/movable/AM)
|
||||
// Following roughly the ntnet signal levels
|
||||
// 0 is none
|
||||
// 1 is weak
|
||||
// 2 is strong
|
||||
var/obj/item/modular_computer/host = tgui_host() //Better not add this to anything other than modular computers.
|
||||
if(!istype(host))
|
||||
return
|
||||
var/our_signal = host.get_ntnet_status() //1 low, 2 good, 3 wired, 0 none
|
||||
var/their_z = get_z(AM)
|
||||
|
||||
//If we have no NTnet connection don't bother getting theirs
|
||||
if(!our_signal)
|
||||
if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range))
|
||||
return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs
|
||||
else
|
||||
return 0
|
||||
|
||||
var/list/zlevels_in_range = using_map.get_map_levels(their_z, FALSE)
|
||||
var/list/zlevels_in_long_range = using_map.get_map_levels(their_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) - zlevels_in_range
|
||||
var/their_signal = 0
|
||||
for(var/relay in ntnet_global.relays)
|
||||
var/obj/machinery/ntnet_relay/R = relay
|
||||
if(!R.operable())
|
||||
continue
|
||||
if(R.z == their_z)
|
||||
their_signal = 2
|
||||
break
|
||||
if(R.z in zlevels_in_range)
|
||||
their_signal = 2
|
||||
break
|
||||
if(R.z in zlevels_in_long_range)
|
||||
their_signal = 1
|
||||
break
|
||||
|
||||
if(!their_signal) //They have no NTnet at all
|
||||
if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range))
|
||||
return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs
|
||||
else
|
||||
return 0
|
||||
else
|
||||
return max(our_signal, their_signal)
|
||||
|
||||
/* All handling viewers */
|
||||
/datum/tgui_module/uav/Destroy()
|
||||
if(LAZYLEN(viewers))
|
||||
for(var/weakref/W in viewers)
|
||||
var/M = W.resolve()
|
||||
if(M)
|
||||
unlook(M)
|
||||
. = ..()
|
||||
|
||||
/datum/tgui_module/uav/tgui_status(mob/user)
|
||||
. = ..()
|
||||
if(. > STATUS_DISABLED)
|
||||
if(viewing_uav(user))
|
||||
look(user)
|
||||
return
|
||||
unlook(user)
|
||||
|
||||
/datum/tgui_module/uav/tgui_close(mob/user)
|
||||
. = ..()
|
||||
unlook(user)
|
||||
|
||||
/datum/tgui_module/uav/proc/viewing_uav(mob/user)
|
||||
return (weakref(user) in viewers)
|
||||
|
||||
/datum/tgui_module/uav/proc/look(mob/user)
|
||||
if(issilicon(user)) //Too complicated for me to want to mess with at the moment
|
||||
to_chat(user, "<span class='warning'>Regulations prevent you from controlling several corporeal forms at the same time!</span>")
|
||||
return
|
||||
|
||||
if(!current_uav)
|
||||
return
|
||||
|
||||
if(user.machine != tgui_host())
|
||||
user.set_machine(tgui_host())
|
||||
user.reset_view(current_uav)
|
||||
current_uav.add_master(user)
|
||||
LAZYDISTINCTADD(viewers, weakref(user))
|
||||
|
||||
/datum/tgui_module/uav/proc/unlook(mob/user)
|
||||
user.unset_machine()
|
||||
user.reset_view()
|
||||
if(current_uav)
|
||||
current_uav.remove_master(user)
|
||||
LAZYREMOVE(viewers, weakref(user))
|
||||
|
||||
/datum/tgui_module/uav/check_eye(mob/user)
|
||||
if(get_dist(user, tgui_host()) > 1 || user.blinded || !current_uav)
|
||||
unlook(user)
|
||||
return -1
|
||||
|
||||
var/viewflag = current_uav.check_eye(user)
|
||||
if(viewflag < 0) //camera doesn't work
|
||||
unlook(user)
|
||||
return -1
|
||||
|
||||
return viewflag
|
||||
|
||||
////
|
||||
//// Relaying movements to the UAV
|
||||
////
|
||||
/datum/tgui_module/uav/relaymove(var/mob/user, direction)
|
||||
if(current_uav)
|
||||
return current_uav.relaymove(user, direction, signal_strength)
|
||||
|
||||
////
|
||||
//// The effects when looking through a UAV
|
||||
////
|
||||
/datum/tgui_module/uav/apply_visual(mob/M)
|
||||
if(!M.client)
|
||||
return
|
||||
if(weakref(M) in viewers)
|
||||
M.overlay_fullscreen("fishbed",/obj/screen/fullscreen/fishbed)
|
||||
M.overlay_fullscreen("scanlines",/obj/screen/fullscreen/scanline)
|
||||
|
||||
if(signal_strength <= 1)
|
||||
M.overlay_fullscreen("whitenoise",/obj/screen/fullscreen/noise)
|
||||
else
|
||||
M.clear_fullscreen("whitenoise", 0)
|
||||
else
|
||||
remove_visual(M)
|
||||
|
||||
/datum/tgui_module/uav/remove_visual(mob/M)
|
||||
if(!M.client)
|
||||
return
|
||||
M.clear_fullscreen("fishbed",0)
|
||||
M.clear_fullscreen("scanlines",0)
|
||||
M.clear_fullscreen("whitenoise",0)
|
||||
Reference in New Issue
Block a user