mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2026-07-15 10:13:45 +01:00
[REVIEW] Ports Modular Computers from Baystation
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
/datum/computer_file/program/access_decrypter
|
||||
filename = "nt_accrypt"
|
||||
filedesc = "NTNet Access Decrypter"
|
||||
program_icon_state = "hostile"
|
||||
program_key_state = "security_key"
|
||||
program_menu_icon = "unlocked"
|
||||
extended_desc = "This highly advanced script can very slowly decrypt operational codes used in almost any network. These codes can be downloaded to an ID card to expand the available access. The system administrator will probably notice this."
|
||||
size = 34
|
||||
requires_ntnet = 1
|
||||
available_on_ntnet = 0
|
||||
available_on_syndinet = 1
|
||||
nanomodule_path = /datum/nano_module/program/access_decrypter/
|
||||
var/message = ""
|
||||
var/running = FALSE
|
||||
var/progress = 0
|
||||
var/target_progress = 300
|
||||
var/datum/access/target_access = null
|
||||
|
||||
/datum/computer_file/program/access_decrypter/kill_program(var/forced)
|
||||
reset()
|
||||
..(forced)
|
||||
|
||||
/datum/computer_file/program/access_decrypter/proc/reset()
|
||||
running = FALSE
|
||||
message = ""
|
||||
progress = 0
|
||||
|
||||
/datum/computer_file/program/access_decrypter/process_tick()
|
||||
. = ..()
|
||||
if(!running)
|
||||
return
|
||||
var/obj/item/weapon/computer_hardware/processor_unit/CPU = computer.processor_unit
|
||||
var/obj/item/weapon/computer_hardware/card_slot/RFID = computer.card_slot
|
||||
if(!istype(CPU) || !CPU.check_functionality() || !istype(RFID) || !RFID.check_functionality())
|
||||
message = "A fatal hardware error has been detected."
|
||||
return
|
||||
if(!istype(RFID.stored_card))
|
||||
message = "RFID card has been removed from the device. Operation aborted."
|
||||
return
|
||||
|
||||
progress += CPU.max_idle_programs
|
||||
if(progress >= target_progress)
|
||||
reset()
|
||||
RFID.stored_card.access |= target_access.id
|
||||
if(ntnet_global.intrusion_detection_enabled)
|
||||
ntnet_global.add_log("IDS WARNING - Unauthorised access to primary keycode database from device: [computer.network_card.get_network_tag()] - downloaded access codes for: [target_access.desc].")
|
||||
ntnet_global.intrusion_detection_alarm = 1
|
||||
message = "Successfully decrypted and saved operational key codes. Downloaded access codes for: [target_access.desc]"
|
||||
target_access = null
|
||||
|
||||
/datum/computer_file/program/access_decrypter/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["PRG_reset"])
|
||||
reset()
|
||||
return 1
|
||||
if(href_list["PRG_execute"])
|
||||
if(running)
|
||||
return 1
|
||||
if(text2num(href_list["allowed"]))
|
||||
return 1
|
||||
var/obj/item/weapon/computer_hardware/processor_unit/CPU = computer.processor_unit
|
||||
var/obj/item/weapon/computer_hardware/card_slot/RFID = computer.card_slot
|
||||
if(!istype(CPU) || !CPU.check_functionality() || !istype(RFID) || !RFID.check_functionality())
|
||||
message = "A fatal hardware error has been detected."
|
||||
return
|
||||
if(!istype(RFID.stored_card))
|
||||
message = "RFID card is not present in the device. Operation aborted."
|
||||
return
|
||||
running = TRUE
|
||||
target_access = get_access_by_id(href_list["PRG_execute"])
|
||||
if(ntnet_global.intrusion_detection_enabled)
|
||||
ntnet_global.add_log("IDS WARNING - Unauthorised access attempt to primary keycode database from device: [computer.network_card.get_network_tag()]")
|
||||
ntnet_global.intrusion_detection_alarm = 1
|
||||
return 1
|
||||
|
||||
/datum/nano_module/program/access_decrypter
|
||||
name = "NTNet Access Decrypter"
|
||||
var/list/restricted_access_codes = list(access_change_ids, access_network) // access codes that are not hackable due to balance reasons
|
||||
|
||||
/datum/nano_module/program/access_decrypter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(!ntnet_global)
|
||||
return
|
||||
var/datum/computer_file/program/access_decrypter/PRG = program
|
||||
var/list/data = list()
|
||||
if(!istype(PRG))
|
||||
return
|
||||
data = PRG.get_header_data()
|
||||
|
||||
if(PRG.message)
|
||||
data["message"] = PRG.message
|
||||
else if(PRG.running)
|
||||
data["running"] = 1
|
||||
data["rate"] = PRG.computer.processor_unit.max_idle_programs
|
||||
|
||||
// Stolen from DOS traffic generator, generates strings of 1s and 0s
|
||||
var/percentage = (PRG.progress / PRG.target_progress) * 100
|
||||
var/list/strings[0]
|
||||
for(var/j, j<10, j++)
|
||||
var/string = ""
|
||||
for(var/i, i<20, i++)
|
||||
string = "[string][prob(percentage)]"
|
||||
strings.Add(string)
|
||||
data["dos_strings"] = strings
|
||||
else 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
|
||||
var/list/regions = list()
|
||||
for(var/i = 1; i <= 7; i++)
|
||||
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,
|
||||
"blocked" = (access in restricted_access_codes) ? 1 : 0)))
|
||||
|
||||
regions.Add(list(list(
|
||||
"name" = get_region_accesses_name(i),
|
||||
"accesses" = accesses)))
|
||||
data["regions"] = regions
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "access_decrypter.tmpl", "NTNet Access Decrypter", 550, 400, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
@@ -0,0 +1,108 @@
|
||||
/datum/computer_file/program/ntnet_dos
|
||||
filename = "ntn_dos"
|
||||
filedesc = "DoS Traffic Generator"
|
||||
program_icon_state = "hostile"
|
||||
program_key_state = "security_key"
|
||||
program_menu_icon = "arrow-4-diag"
|
||||
extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect"
|
||||
size = 20
|
||||
requires_ntnet = 1
|
||||
available_on_ntnet = 0
|
||||
available_on_syndinet = 1
|
||||
nanomodule_path = /datum/nano_module/program/computer_dos/
|
||||
var/obj/machinery/ntnet_relay/target = null
|
||||
var/dos_speed = 0
|
||||
var/error = ""
|
||||
var/executed = 0
|
||||
|
||||
/datum/computer_file/program/ntnet_dos/process_tick()
|
||||
dos_speed = 0
|
||||
switch(ntnet_status)
|
||||
if(1)
|
||||
dos_speed = NTNETSPEED_LOWSIGNAL * NTNETSPEED_DOS_AMPLIFICATION
|
||||
if(2)
|
||||
dos_speed = NTNETSPEED_HIGHSIGNAL * NTNETSPEED_DOS_AMPLIFICATION
|
||||
if(3)
|
||||
dos_speed = NTNETSPEED_ETHERNET * NTNETSPEED_DOS_AMPLIFICATION
|
||||
if(target && executed)
|
||||
target.dos_overload += dos_speed
|
||||
if(!target.operable())
|
||||
target.dos_sources.Remove(src)
|
||||
target = null
|
||||
error = "Connection to destination relay lost."
|
||||
|
||||
/datum/computer_file/program/ntnet_dos/kill_program(var/forced)
|
||||
if(target)
|
||||
target.dos_sources.Remove(src)
|
||||
target = null
|
||||
executed = 0
|
||||
|
||||
..(forced)
|
||||
|
||||
/datum/nano_module/program/computer_dos
|
||||
name = "DoS Traffic Generator"
|
||||
|
||||
/datum/nano_module/program/computer_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(!ntnet_global)
|
||||
return
|
||||
var/datum/computer_file/program/ntnet_dos/PRG = program
|
||||
var/list/data = list()
|
||||
if(!istype(PRG))
|
||||
return
|
||||
data = PRG.get_header_data()
|
||||
|
||||
if(PRG.error)
|
||||
data["error"] = PRG.error
|
||||
else if(PRG.target && PRG.executed)
|
||||
data["target"] = 1
|
||||
data["speed"] = PRG.dos_speed
|
||||
|
||||
// This is mostly visual, generate some strings of 1s and 0s
|
||||
// Probability of 1 is equal of completion percentage of DoS attack on this relay.
|
||||
// Combined with UI updates this adds quite nice effect to the UI
|
||||
var/percentage = PRG.target.dos_overload * 100 / PRG.target.dos_capacity
|
||||
var/list/strings[0]
|
||||
for(var/j, j<10, j++)
|
||||
var/string = ""
|
||||
for(var/i, i<20, i++)
|
||||
string = "[string][prob(percentage)]"
|
||||
strings.Add(string)
|
||||
data["dos_strings"] = strings
|
||||
else
|
||||
var/list/relays[0]
|
||||
for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays)
|
||||
relays.Add(R.uid)
|
||||
data["relays"] = relays
|
||||
data["focus"] = PRG.target ? PRG.target.uid : null
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 400, 250, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/datum/computer_file/program/ntnet_dos/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["PRG_target_relay"])
|
||||
for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays)
|
||||
if("[R.uid]" == href_list["PRG_target_relay"])
|
||||
target = R
|
||||
return 1
|
||||
if(href_list["PRG_reset"])
|
||||
if(target)
|
||||
target.dos_sources.Remove(src)
|
||||
target = null
|
||||
executed = 0
|
||||
error = ""
|
||||
return 1
|
||||
if(href_list["PRG_execute"])
|
||||
if(target)
|
||||
executed = 1
|
||||
target.dos_sources.Add(src)
|
||||
if(ntnet_global.intrusion_detection_enabled)
|
||||
ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [computer.network_card.get_network_tag()]")
|
||||
ntnet_global.intrusion_detection_alarm = 1
|
||||
return 1
|
||||
@@ -0,0 +1,39 @@
|
||||
/datum/computer_file/program/camera_monitor/hacked
|
||||
filename = "camcrypt"
|
||||
filedesc = "Camera Decryption Tool"
|
||||
nanomodule_path = /datum/nano_module/camera_monitor/hacked
|
||||
program_icon_state = "hostile"
|
||||
program_key_state = "security_key"
|
||||
program_menu_icon = "zoomin"
|
||||
extended_desc = "This very advanced piece of software uses adaptive programming and large database of cipherkeys to bypass most encryptions used on camera networks. Be warned that system administrator may notice this."
|
||||
size = 73 // Very large, a price for bypassing ID checks completely.
|
||||
available_on_ntnet = 0
|
||||
available_on_syndinet = 1
|
||||
|
||||
/datum/computer_file/program/camera_monitor/hacked/process_tick()
|
||||
..()
|
||||
if(program_state != PROGRAM_STATE_ACTIVE) // Background programs won't trigger alarms.
|
||||
return
|
||||
|
||||
var/datum/nano_module/camera_monitor/hacked/HNM = NM
|
||||
|
||||
// The program is active and connected to one of the station's networks. Has a very small chance to trigger IDS alarm every tick.
|
||||
if(HNM && HNM.current_network && (HNM.current_network in using_map.station_networks) && prob(0.1))
|
||||
if(ntnet_global.intrusion_detection_enabled)
|
||||
ntnet_global.add_log("IDS WARNING - Unauthorised access detected to camera network [HNM.current_network] by device with NID [computer.network_card.get_network_tag()]")
|
||||
ntnet_global.intrusion_detection_alarm = 1
|
||||
|
||||
|
||||
/datum/nano_module/camera_monitor/hacked
|
||||
name = "Hacked Camera Monitoring Program"
|
||||
//available_to_ai = FALSE
|
||||
|
||||
/datum/nano_module/camera_monitor/hacked/can_access_network(var/mob/user, var/network_access)
|
||||
return 1
|
||||
|
||||
// The hacked variant has access to all commonly used networks.
|
||||
/datum/nano_module/camera_monitor/hacked/modify_networks_list(var/list/networks)
|
||||
networks.Add(list(list("tag" = NETWORK_MERCENARY, "has_access" = 1)))
|
||||
networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1)))
|
||||
networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1)))
|
||||
return networks
|
||||
@@ -0,0 +1,84 @@
|
||||
/datum/computer_file/program/revelation
|
||||
filename = "revelation"
|
||||
filedesc = "Revelation"
|
||||
program_icon_state = "hostile"
|
||||
program_key_state = "security_key"
|
||||
program_menu_icon = "home"
|
||||
extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution."
|
||||
size = 13
|
||||
requires_ntnet = 0
|
||||
available_on_ntnet = 0
|
||||
available_on_syndinet = 1
|
||||
nanomodule_path = /datum/nano_module/program/revelation/
|
||||
var/armed = 0
|
||||
|
||||
/datum/computer_file/program/revelation/run_program(var/mob/living/user)
|
||||
. = ..(user)
|
||||
if(armed)
|
||||
activate()
|
||||
|
||||
/datum/computer_file/program/revelation/proc/activate()
|
||||
if(!computer)
|
||||
return
|
||||
|
||||
computer.visible_message("<span class='notice'>\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.</span>")
|
||||
computer.enabled = 0
|
||||
computer.update_icon()
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(10, 1, computer.loc)
|
||||
s.start()
|
||||
|
||||
if(computer.hard_drive)
|
||||
qdel(computer.hard_drive)
|
||||
|
||||
if(computer.battery_module && prob(25))
|
||||
qdel(computer.battery_module)
|
||||
|
||||
if(computer.tesla_link && prob(50))
|
||||
qdel(computer.tesla_link)
|
||||
|
||||
/datum/computer_file/program/revelation/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
else if(href_list["PRG_arm"])
|
||||
armed = !armed
|
||||
else if(href_list["PRG_activate"])
|
||||
activate()
|
||||
else if(href_list["PRG_obfuscate"])
|
||||
var/mob/living/user = usr
|
||||
var/newname = sanitize(input(user, "Enter new program name: "))
|
||||
if(!newname)
|
||||
return
|
||||
filedesc = newname
|
||||
for(var/datum/computer_file/program/P in ntnet_global.available_station_software)
|
||||
if(filedesc == P.filedesc)
|
||||
program_menu_icon = P.program_menu_icon
|
||||
break
|
||||
return 1
|
||||
|
||||
/datum/computer_file/program/revelation/clone()
|
||||
var/datum/computer_file/program/revelation/temp = ..()
|
||||
temp.armed = armed
|
||||
return temp
|
||||
|
||||
/datum/nano_module/program/revelation
|
||||
name = "Revelation Virus"
|
||||
|
||||
/datum/nano_module/program/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = list()
|
||||
var/datum/computer_file/program/revelation/PRG = program
|
||||
if(!istype(PRG))
|
||||
return
|
||||
|
||||
data = PRG.get_header_data()
|
||||
|
||||
data["armed"] = PRG.armed
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "revelation.tmpl", "Revelation Virus", 400, 250, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/datum/computer_file/program/card_mod
|
||||
filename = "cardmod"
|
||||
filedesc = "ID card modification program"
|
||||
nanomodule_path = /datum/nano_module/program/card_mod
|
||||
program_icon_state = "id"
|
||||
program_key_state = "id_key"
|
||||
program_menu_icon = "key"
|
||||
extended_desc = "Program for programming crew ID cards."
|
||||
required_access = access_change_ids
|
||||
requires_ntnet = 0
|
||||
size = 8
|
||||
|
||||
/datum/nano_module/program/card_mod
|
||||
name = "ID card modification program"
|
||||
var/mod_mode = 1
|
||||
var/is_centcom = 0
|
||||
var/show_assignments = 0
|
||||
|
||||
/datum/nano_module/program/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
data["src"] = "\ref[src]"
|
||||
data["station_name"] = station_name()
|
||||
data["manifest"] = data_core ? data_core.get_manifest(0) : null
|
||||
data["assignments"] = show_assignments
|
||||
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["mmode"] = mod_mode
|
||||
data["centcom_access"] = is_centcom
|
||||
|
||||
if(program && program.computer && program.computer.card_slot)
|
||||
var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card
|
||||
data["has_id"] = !!id_card
|
||||
data["id_account_number"] = id_card ? id_card.associated_account_number : null
|
||||
data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned"
|
||||
data["id_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----"
|
||||
data["id_name"] = id_card ? id_card.name : "-----"
|
||||
|
||||
data["command_jobs"] = format_jobs(command_positions)
|
||||
data["engineering_jobs"] = format_jobs(engineering_positions)
|
||||
data["medical_jobs"] = format_jobs(medical_positions)
|
||||
data["science_jobs"] = format_jobs(science_positions)
|
||||
data["security_jobs"] = format_jobs(security_positions)
|
||||
data["cargo_jobs"] = format_jobs(cargo_positions)
|
||||
data["civilian_jobs"] = format_jobs(civilian_positions)
|
||||
data["centcom_jobs"] = format_jobs(get_all_centcom_jobs())
|
||||
|
||||
data["all_centcom_access"] = is_centcom ? get_accesses(1) : null
|
||||
data["regions"] = get_accesses()
|
||||
|
||||
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)
|
||||
var/list/all_centcom_access = list()
|
||||
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
|
||||
var/list/regions = list()
|
||||
for(var/i = 1; i <= 7; i++)
|
||||
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
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "mod_identification_computer.tmpl", name, 600, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
/datum/nano_module/program/card_mod/proc/format_jobs(list/jobs)
|
||||
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/nano_module/program/card_mod/proc/get_accesses(var/is_centcom = 0)
|
||||
return null
|
||||
|
||||
|
||||
/datum/computer_file/program/card_mod/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
var/mob/user = usr
|
||||
var/obj/item/weapon/card/id/user_id_card = user.GetIdCard()
|
||||
var/obj/item/weapon/card/id/id_card
|
||||
if (computer.card_slot)
|
||||
id_card = computer.card_slot.stored_card
|
||||
|
||||
var/datum/nano_module/program/card_mod/module = NM
|
||||
switch(href_list["action"])
|
||||
if("switchm")
|
||||
if(href_list["target"] == "mod")
|
||||
module.mod_mode = 1
|
||||
else if (href_list["target"] == "manifest")
|
||||
module.mod_mode = 0
|
||||
if("togglea")
|
||||
if(module.show_assignments)
|
||||
module.show_assignments = 0
|
||||
else
|
||||
module.show_assignments = 1
|
||||
if("print")
|
||||
if(computer && computer.nano_printer) //This option should never be called if there is no printer
|
||||
if(module.mod_mode)
|
||||
if(can_run(user, 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>")
|
||||
if("eject")
|
||||
if(computer && computer.card_slot)
|
||||
if(id_card)
|
||||
data_core.manifest_modify(id_card.registered_name, id_card.assignment)
|
||||
computer.proc_eject_id(user)
|
||||
if("terminate")
|
||||
if(computer && can_run(user, 1))
|
||||
id_card.assignment = "Terminated"
|
||||
remove_nt_access(id_card)
|
||||
callHook("terminate_employee", list(id_card))
|
||||
if("edit")
|
||||
if(computer && can_run(user, 1))
|
||||
if(href_list["name"])
|
||||
var/temp_name = sanitizeName(input("Enter name.", "Name", id_card.registered_name),allow_numbers=TRUE)
|
||||
if(temp_name)
|
||||
id_card.registered_name = temp_name
|
||||
else
|
||||
computer.visible_message("<span class='notice'>[computer] buzzes rudely.</span>")
|
||||
else if(href_list["account"])
|
||||
var/account_num = text2num(input("Enter account number.", "Account", id_card.associated_account_number))
|
||||
id_card.associated_account_number = account_num
|
||||
if("assign")
|
||||
if(computer && can_run(user, 1) && id_card)
|
||||
var/t1 = href_list["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(module.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()
|
||||
|
||||
remove_nt_access(id_card)
|
||||
apply_access(id_card, access)
|
||||
id_card.assignment = t1
|
||||
id_card.rank = t1
|
||||
|
||||
callHook("reassign_employee", list(id_card))
|
||||
if("access")
|
||||
if(href_list["allowed"] && computer && can_run(user, 1))
|
||||
var/access_type = text2num(href_list["access_target"])
|
||||
var/access_allowed = text2num(href_list["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
|
||||
if(id_card)
|
||||
id_card.name = text("[id_card.registered_name]'s ID Card ([id_card.assignment])")
|
||||
|
||||
SSnanoui.update_uis(NM)
|
||||
return 1
|
||||
|
||||
/datum/computer_file/program/card_mod/proc/remove_nt_access(var/obj/item/weapon/card/id/id_card)
|
||||
id_card.access -= get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM)
|
||||
|
||||
/datum/computer_file/program/card_mod/proc/apply_access(var/obj/item/weapon/card/id/id_card, var/list/accesses)
|
||||
id_card.access |= accesses
|
||||
@@ -0,0 +1,326 @@
|
||||
#define STATE_DEFAULT 1
|
||||
#define STATE_MESSAGELIST 2
|
||||
#define STATE_VIEWMESSAGE 3
|
||||
#define STATE_STATUSDISPLAY 4
|
||||
#define STATE_ALERT_LEVEL 5
|
||||
/datum/computer_file/program/comm
|
||||
filename = "comm"
|
||||
filedesc = "Command and Communications Program"
|
||||
program_icon_state = "comm"
|
||||
program_key_state = "med_key"
|
||||
program_menu_icon = "flag"
|
||||
nanomodule_path = /datum/nano_module/program/comm
|
||||
extended_desc = "Used to command and control. Can relay long-range communications. This program can not be run on tablet computers."
|
||||
required_access = access_heads
|
||||
requires_ntnet = 1
|
||||
size = 12
|
||||
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
|
||||
network_destination = "long-range communication array"
|
||||
var/datum/comm_message_listener/message_core = new
|
||||
|
||||
/datum/computer_file/program/comm/clone()
|
||||
var/datum/computer_file/program/comm/temp = ..()
|
||||
temp.message_core.messages = null
|
||||
temp.message_core.messages = message_core.messages.Copy()
|
||||
return temp
|
||||
|
||||
/datum/nano_module/program/comm
|
||||
name = "Command and Communications Program"
|
||||
//available_to_ai = TRUE
|
||||
var/current_status = STATE_DEFAULT
|
||||
var/msg_line1 = ""
|
||||
var/msg_line2 = ""
|
||||
var/centcomm_message_cooldown = 0
|
||||
var/announcment_cooldown = 0
|
||||
var/datum/announcement/priority/crew_announcement = new
|
||||
var/current_viewing_message_id = 0
|
||||
var/current_viewing_message = null
|
||||
|
||||
/datum/nano_module/program/comm/New()
|
||||
..()
|
||||
crew_announcement.newscast = 1
|
||||
|
||||
/datum/nano_module/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
if(program)
|
||||
data["emagged"] = program.computer_emagged
|
||||
data["net_comms"] = !!program.get_signal(NTNET_COMMUNICATION) //Double !! is needed to get 1 or 0 answer
|
||||
data["net_syscont"] = !!program.get_signal(NTNET_SYSTEMCONTROL)
|
||||
if(program.computer)
|
||||
data["have_printer"] = !!program.computer.nano_printer
|
||||
else
|
||||
data["have_printer"] = 0
|
||||
else
|
||||
data["emagged"] = 0
|
||||
data["net_comms"] = 1
|
||||
data["net_syscont"] = 1
|
||||
data["have_printer"] = 0
|
||||
|
||||
data["message_line1"] = msg_line1
|
||||
data["message_line2"] = msg_line2
|
||||
data["state"] = current_status
|
||||
data["isAI"] = issilicon(usr)
|
||||
data["authenticated"] = is_autenthicated(user)
|
||||
data["current_security_level"] = security_level
|
||||
data["current_security_level_title"] = num2seclevel(security_level)
|
||||
|
||||
data["def_SEC_LEVEL_DELTA"] = SEC_LEVEL_DELTA
|
||||
data["def_SEC_LEVEL_YELLOW"] = SEC_LEVEL_YELLOW
|
||||
data["def_SEC_LEVEL_ORANGE"] = SEC_LEVEL_ORANGE
|
||||
data["def_SEC_LEVEL_VIOLET"] = SEC_LEVEL_VIOLET
|
||||
data["def_SEC_LEVEL_BLUE"] = SEC_LEVEL_BLUE
|
||||
data["def_SEC_LEVEL_GREEN"] = SEC_LEVEL_GREEN
|
||||
|
||||
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
|
||||
if(current_viewing_message)
|
||||
data["message_current"] = current_viewing_message
|
||||
|
||||
if(emergency_shuttle.location())
|
||||
data["have_shuttle"] = 1
|
||||
if(emergency_shuttle.online())
|
||||
data["have_shuttle_called"] = 1
|
||||
else
|
||||
data["have_shuttle_called"] = 0
|
||||
else
|
||||
data["have_shuttle"] = 0
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "mod_communication.tmpl", name, 550, 420, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
/datum/nano_module/program/comm/proc/is_autenthicated(var/mob/user)
|
||||
if(program)
|
||||
return program.can_run(user)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/program/comm/proc/obtain_message_listener()
|
||||
if(program)
|
||||
var/datum/computer_file/program/comm/P = program
|
||||
return P.message_core
|
||||
return global_message_listener
|
||||
|
||||
/datum/nano_module/program/comm/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
var/mob/user = usr
|
||||
var/ntn_comm = program ? !!program.get_signal(NTNET_COMMUNICATION) : 1
|
||||
var/ntn_cont = program ? !!program.get_signal(NTNET_SYSTEMCONTROL) : 1
|
||||
var/datum/comm_message_listener/l = obtain_message_listener()
|
||||
switch(href_list["action"])
|
||||
if("sw_menu")
|
||||
. = 1
|
||||
current_status = text2num(href_list["target"])
|
||||
if("announce")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && !issilicon(usr) && ntn_comm)
|
||||
if(user)
|
||||
var/obj/item/weapon/card/id/id_card = user.GetIdCard()
|
||||
crew_announcement.announcer = GetNameAndAssignmentFromId(id_card)
|
||||
else
|
||||
crew_announcement.announcer = "Unknown"
|
||||
if(announcment_cooldown)
|
||||
to_chat(usr, "Please allow at least one minute to pass between announcements")
|
||||
return TRUE
|
||||
var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|text
|
||||
if(!input || !can_still_topic())
|
||||
return 1
|
||||
crew_announcement.Announce(input)
|
||||
announcment_cooldown = 1
|
||||
spawn(600)//One minute cooldown
|
||||
announcment_cooldown = 0
|
||||
if("message")
|
||||
. = 1
|
||||
if(href_list["target"] == "emagged")
|
||||
if(program)
|
||||
if(is_autenthicated(user) && program.computer_emagged && !issilicon(usr) && ntn_comm)
|
||||
if(centcomm_message_cooldown)
|
||||
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
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.", "") as null|text)
|
||||
if(!input || !can_still_topic())
|
||||
return 1
|
||||
Syndicate_announce(input, usr)
|
||||
to_chat(usr, "<span class='notice'>Message transmitted.</span>")
|
||||
log_say("[key_name(usr)] has made an illegal announcement: [input]")
|
||||
centcomm_message_cooldown = 1
|
||||
spawn(300)//30 second cooldown
|
||||
centcomm_message_cooldown = 0
|
||||
else if(href_list["target"] == "regular")
|
||||
if(is_autenthicated(user) && !issilicon(usr) && ntn_comm)
|
||||
if(centcomm_message_cooldown)
|
||||
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
return
|
||||
if(!is_relay_online())//Contact Centcom has a check, Syndie doesn't to allow for Traitor funs.
|
||||
to_chat(usr, "<span class='warning'>No Emergency Bluespace Relay detected. Unable to transmit message.</span>")
|
||||
return 1
|
||||
var/input = sanitize(input("Please choose a message to transmit to Centcomm 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.", "") as null|text)
|
||||
if(!input || !can_still_topic())
|
||||
return 1
|
||||
CentCom_announce(input, usr)
|
||||
to_chat(usr, "<span class='notice'>Message transmitted.</span>")
|
||||
log_say("[key_name(usr)] has made an IA Centcomm announcement: [input]")
|
||||
centcomm_message_cooldown = 1
|
||||
spawn(300) //30 second cooldown
|
||||
centcomm_message_cooldown = 0
|
||||
if("shuttle")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && ntn_cont)
|
||||
if(href_list["target"] == "call")
|
||||
var/confirm = alert("Are you sure you want to call the shuttle?", name, "No", "Yes")
|
||||
if(confirm == "Yes" && can_still_topic())
|
||||
call_shuttle_proc(usr)
|
||||
|
||||
if(href_list["target"] == "cancel" && !issilicon(usr))
|
||||
var/confirm = alert("Are you sure you want to cancel the shuttle?", name, "No", "Yes")
|
||||
if(confirm == "Yes" && can_still_topic())
|
||||
cancel_call_proc(usr)
|
||||
if("setstatus")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && ntn_cont)
|
||||
switch(href_list["target"])
|
||||
if("line1")
|
||||
var/linput = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", msg_line1) as text|null, 40), 40)
|
||||
if(can_still_topic())
|
||||
msg_line1 = linput
|
||||
if("line2")
|
||||
var/linput = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", msg_line2) as text|null, 40), 40)
|
||||
if(can_still_topic())
|
||||
msg_line2 = linput
|
||||
if("message")
|
||||
post_status("message", msg_line1, msg_line2)
|
||||
if("alert")
|
||||
post_status("alert", href_list["alert"])
|
||||
else
|
||||
post_status(href_list["target"])
|
||||
if("setalert")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && !issilicon(usr) && ntn_cont && ntn_comm)
|
||||
var/current_level = text2num(href_list["target"])
|
||||
var/confirm = alert("Are you sure you want to change alert level to [num2seclevel(current_level)]?", name, "No", "Yes")
|
||||
if(confirm == "Yes" && can_still_topic())
|
||||
var/old_level = security_level
|
||||
if(!current_level) current_level = SEC_LEVEL_GREEN
|
||||
if(current_level < SEC_LEVEL_GREEN) current_level = SEC_LEVEL_GREEN
|
||||
if(current_level > SEC_LEVEL_BLUE) current_level = SEC_LEVEL_BLUE //Cannot engage delta with this
|
||||
set_security_level(current_level)
|
||||
if(security_level != old_level)
|
||||
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_ORANGE)
|
||||
feedback_inc("alert_comms_orange",1)
|
||||
if(SEC_LEVEL_VIOLET)
|
||||
feedback_inc("alert_comms_violet",1)
|
||||
if(SEC_LEVEL_BLUE)
|
||||
feedback_inc("alert_comms_blue",1)
|
||||
else
|
||||
to_chat(usr, "You press button, but red light flashes and nothing happens.")//This should never happen
|
||||
|
||||
current_status = STATE_DEFAULT
|
||||
if("viewmessage")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && ntn_comm)
|
||||
current_viewing_message_id = text2num(href_list["target"])
|
||||
for(var/list/m in l.messages)
|
||||
if(m["id"] == current_viewing_message_id)
|
||||
current_viewing_message = m
|
||||
current_status = STATE_VIEWMESSAGE
|
||||
if("delmessage")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && ntn_comm && l != global_message_listener)
|
||||
l.Remove(current_viewing_message)
|
||||
current_status = STATE_MESSAGELIST
|
||||
if("printmessage")
|
||||
. = 1
|
||||
if(is_autenthicated(user) && ntn_comm)
|
||||
if(program && program.computer && program.computer.nano_printer)
|
||||
if(!program.computer.nano_printer.print_text(current_viewing_message["contents"],current_viewing_message["title"]))
|
||||
to_chat(usr, "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>")
|
||||
else
|
||||
program.computer.visible_message("<span class='notice'>\The [program.computer] prints out paper.</span>")
|
||||
|
||||
|
||||
/datum/nano_module/program/comm/proc/post_status(var/command, var/data1, var/data2)
|
||||
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
|
||||
|
||||
if(!frequency) return
|
||||
|
||||
|
||||
var/datum/signal/status_signal = new
|
||||
status_signal.source = src
|
||||
status_signal.transmission_method = 1
|
||||
status_signal.data["command"] = command
|
||||
|
||||
switch(command)
|
||||
if("message")
|
||||
status_signal.data["msg1"] = data1
|
||||
status_signal.data["msg2"] = data2
|
||||
log_admin("STATUS: [key_name(usr)] set status screen message with [src]: [data1] [data2]")
|
||||
if("alert")
|
||||
status_signal.data["picture_state"] = data1
|
||||
|
||||
frequency.post_signal(src, status_signal)
|
||||
|
||||
#undef STATE_DEFAULT
|
||||
#undef STATE_MESSAGELIST
|
||||
#undef STATE_VIEWMESSAGE
|
||||
#undef STATE_STATUSDISPLAY
|
||||
#undef STATE_ALERT_LEVEL
|
||||
|
||||
/*
|
||||
General message handling stuff
|
||||
*/
|
||||
var/list/comm_message_listeners = list() //We first have to initialize list then we can use it.
|
||||
var/datum/comm_message_listener/global_message_listener = new //May be used by admins
|
||||
var/last_message_id = 0
|
||||
|
||||
proc/get_comm_message_id()
|
||||
last_message_id = last_message_id + 1
|
||||
return last_message_id
|
||||
|
||||
proc/post_comm_message(var/message_title, var/message_text)
|
||||
var/list/message = list()
|
||||
message["id"] = get_comm_message_id()
|
||||
message["title"] = message_title
|
||||
message["contents"] = message_text
|
||||
|
||||
for (var/datum/comm_message_listener/l in comm_message_listeners)
|
||||
l.Add(message)
|
||||
|
||||
//Old console support
|
||||
for (var/obj/machinery/computer/communications/comm in machines)
|
||||
if (!(comm.stat & (BROKEN | NOPOWER)) && comm.prints_intercept)
|
||||
var/obj/item/weapon/paper/intercept = new /obj/item/weapon/paper( comm.loc )
|
||||
intercept.name = message_title
|
||||
intercept.info = message_text
|
||||
|
||||
comm.messagetitle.Add(message_title)
|
||||
comm.messagetext.Add(message_text)
|
||||
|
||||
/datum/comm_message_listener
|
||||
var/list/messages
|
||||
|
||||
/datum/comm_message_listener/New()
|
||||
..()
|
||||
messages = list()
|
||||
comm_message_listeners.Add(src)
|
||||
|
||||
/datum/comm_message_listener/proc/Add(var/list/message)
|
||||
messages[++messages.len] = message
|
||||
|
||||
/datum/comm_message_listener/proc/Remove(var/list/message)
|
||||
messages -= list(message)
|
||||
@@ -0,0 +1,134 @@
|
||||
/datum/computer_file/program/alarm_monitor
|
||||
filename = "alarmmonitor"
|
||||
filedesc = "Alarm Monitoring"
|
||||
nanomodule_path = /datum/nano_module/alarm_monitor/engineering
|
||||
ui_header = "alarm_green.gif"
|
||||
program_icon_state = "alert-green"
|
||||
program_key_state = "atmos_key"
|
||||
program_menu_icon = "alert"
|
||||
extended_desc = "This program provides visual interface for the alarm system."
|
||||
requires_ntnet = 1
|
||||
network_destination = "alarm monitoring network"
|
||||
size = 5
|
||||
var/has_alert = 0
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/process_tick()
|
||||
..()
|
||||
var/datum/nano_module/alarm_monitor/NMA = NM
|
||||
if(istype(NMA) && NMA.has_major_alarms())
|
||||
if(!has_alert)
|
||||
program_icon_state = "alert-red"
|
||||
ui_header = "alarm_red.gif"
|
||||
update_computer_icon()
|
||||
has_alert = 1
|
||||
else
|
||||
if(has_alert)
|
||||
program_icon_state = "alert-green"
|
||||
ui_header = "alarm_green.gif"
|
||||
update_computer_icon()
|
||||
has_alert = 0
|
||||
return 1
|
||||
|
||||
/datum/nano_module/alarm_monitor
|
||||
name = "Alarm monitor"
|
||||
var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use.
|
||||
var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
|
||||
//available_to_ai = FALSE
|
||||
|
||||
/datum/nano_module/alarm_monitor/New()
|
||||
..()
|
||||
alarm_handlers = list()
|
||||
|
||||
/datum/nano_module/alarm_monitor/all/New()
|
||||
..()
|
||||
alarm_handlers = alarm_manager.all_handlers
|
||||
|
||||
/datum/nano_module/alarm_monitor/engineering/New()
|
||||
..()
|
||||
alarm_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, power_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/security/New()
|
||||
..()
|
||||
alarm_handlers = list(camera_alarm, motion_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/register_alarm(var/object, var/procName)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.register_alarm(object, procName)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/unregister_alarm(var/object)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister_alarm(object)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/all_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.visible_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/major_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.major_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
// Modified version of above proc that uses slightly less resources, returns 1 if there is a major alarm, 0 otherwise.
|
||||
/datum/nano_module/alarm_monitor/proc/has_major_alarms()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
if(AH.has_major_alarms())
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/minor_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.minor_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/Topic(ref, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["switchTo"])
|
||||
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras
|
||||
if(!C)
|
||||
return
|
||||
|
||||
usr.switch_to_camera(C)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
var/categories[0]
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
|
||||
for(var/datum/alarm/A in AH.major_alarms())
|
||||
var/cameras[0]
|
||||
var/lost_sources[0]
|
||||
|
||||
if(isAI(user))
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameras[++cameras.len] = C.nano_structure()
|
||||
for(var/datum/alarm_source/AS in A.sources)
|
||||
if(!AS.source)
|
||||
lost_sources[++lost_sources.len] = AS.source_name
|
||||
|
||||
categories[categories.len]["alarms"] += list(list(
|
||||
"name" = sanitize(A.alarm_name()),
|
||||
"origin_lost" = A.origin == null,
|
||||
"has_cameras" = cameras.len,
|
||||
"cameras" = cameras,
|
||||
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
|
||||
data["categories"] = categories
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state)
|
||||
if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI.
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
@@ -0,0 +1,103 @@
|
||||
/datum/computer_file/program/atmos_control
|
||||
filename = "atmoscontrol"
|
||||
filedesc = "Atmosphere Control"
|
||||
nanomodule_path = /datum/nano_module/atmos_control
|
||||
program_icon_state = "atmos_control"
|
||||
program_key_state = "atmos_key"
|
||||
program_menu_icon = "shuffle"
|
||||
extended_desc = "This program allows remote control of air alarms. This program can not be run on tablet computers."
|
||||
required_access = access_atmospherics
|
||||
requires_ntnet = 1
|
||||
network_destination = "atmospheric control system"
|
||||
requires_ntnet_feature = NTNET_SYSTEMCONTROL
|
||||
usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE
|
||||
size = 17
|
||||
|
||||
/datum/nano_module/atmos_control
|
||||
name = "Atmospherics Control"
|
||||
var/obj/access = new()
|
||||
var/emagged = 0
|
||||
var/ui_ref
|
||||
var/list/monitored_alarms = list()
|
||||
|
||||
/datum/nano_module/atmos_control/New(atmos_computer, req_access, req_one_access, monitored_alarm_ids)
|
||||
..()
|
||||
access.req_access = req_access
|
||||
access.req_one_access = req_one_access
|
||||
|
||||
if(monitored_alarm_ids)
|
||||
for(var/obj/machinery/alarm/alarm in machines)
|
||||
if(alarm.alarm_id && alarm.alarm_id in monitored_alarm_ids)
|
||||
monitored_alarms += alarm
|
||||
// machines may not yet be ordered at this point
|
||||
monitored_alarms = dd_sortedObjectList(monitored_alarms)
|
||||
|
||||
/datum/nano_module/atmos_control/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["alarm"])
|
||||
if(ui_ref)
|
||||
var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms.len ? monitored_alarms : machines)
|
||||
if(alarm)
|
||||
var/datum/topic_state/TS = generate_state(alarm)
|
||||
alarm.ui_interact(usr, master_ui = ui_ref, state = TS)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
var/alarms[0]
|
||||
var/turf/T = get_turf(nano_host())
|
||||
|
||||
// TODO: Move these to a cache, similar to cameras
|
||||
for(var/obj/machinery/alarm/alarm in (monitored_alarms.len ? monitored_alarms : machines))
|
||||
if(!monitored_alarms.len && alarm.alarms_hidden)
|
||||
continue
|
||||
alarms[++alarms.len] = list(
|
||||
"name" = sanitize(alarm.name),
|
||||
"ref"= "\ref[alarm]",
|
||||
"danger" = max(alarm.danger_level, alarm.alarm_area.atmosalm),
|
||||
"x" = alarm.x,
|
||||
"y" = alarm.y,
|
||||
"z" = alarm.z)
|
||||
data["alarms"] = alarms
|
||||
data["map_levels"] = using_map.get_map_levels(T.z)
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 625, 625, state = state)
|
||||
if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI.
|
||||
ui.auto_update_layout = 1
|
||||
// adding a template with the key "mapContent" enables the map ui functionality
|
||||
ui.add_template("mapContent", "atmos_control_map_content.tmpl")
|
||||
// adding a template with the key "mapHeader" replaces the map header content
|
||||
ui.add_template("mapHeader", "atmos_control_map_header.tmpl")
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(0)
|
||||
ui_ref = ui
|
||||
|
||||
/datum/nano_module/atmos_control/proc/generate_state(air_alarm)
|
||||
var/datum/topic_state/air_alarm/state = new()
|
||||
state.atmos_control = src
|
||||
state.air_alarm = air_alarm
|
||||
return state
|
||||
|
||||
/datum/topic_state/air_alarm
|
||||
var/datum/nano_module/atmos_control/atmos_control = null
|
||||
var/obj/machinery/alarm/air_alarm = null
|
||||
|
||||
/datum/topic_state/air_alarm/can_use_topic(var/src_object, var/mob/user)
|
||||
if(has_access(user))
|
||||
return STATUS_INTERACTIVE
|
||||
return STATUS_UPDATE
|
||||
|
||||
/datum/topic_state/air_alarm/href_list(var/mob/user)
|
||||
var/list/extra_href = list()
|
||||
extra_href["remote_connection"] = 1
|
||||
extra_href["remote_access"] = has_access(user)
|
||||
|
||||
return extra_href
|
||||
|
||||
/datum/topic_state/air_alarm/proc/has_access(var/mob/user)
|
||||
return user && (isAI(user) || atmos_control.access.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO))
|
||||
@@ -0,0 +1,113 @@
|
||||
/datum/computer_file/program/power_monitor
|
||||
filename = "powermonitor"
|
||||
filedesc = "Power Monitoring"
|
||||
nanomodule_path = /datum/nano_module/power_monitor/
|
||||
program_icon_state = "power_monitor"
|
||||
program_key_state = "power_key"
|
||||
program_menu_icon = "battery-3"
|
||||
extended_desc = "This program connects to sensors to provide information about electrical systems"
|
||||
ui_header = "power_norm.gif"
|
||||
required_access = access_engine
|
||||
requires_ntnet = 1
|
||||
network_destination = "power monitoring system"
|
||||
size = 9
|
||||
var/has_alert = 0
|
||||
|
||||
/datum/computer_file/program/power_monitor/process_tick()
|
||||
..()
|
||||
var/datum/nano_module/power_monitor/NMA = NM
|
||||
if(istype(NMA) && NMA.has_alarm())
|
||||
if(!has_alert)
|
||||
program_icon_state = "power_monitor_warn"
|
||||
ui_header = "power_warn.gif"
|
||||
update_computer_icon()
|
||||
has_alert = 1
|
||||
else
|
||||
if(has_alert)
|
||||
program_icon_state = "power_monitor"
|
||||
ui_header = "power_norm.gif"
|
||||
update_computer_icon()
|
||||
has_alert = 0
|
||||
|
||||
/datum/nano_module/power_monitor
|
||||
name = "Power monitor"
|
||||
var/list/grid_sensors
|
||||
var/active_sensor = null //name_tag of the currently selected sensor
|
||||
|
||||
/datum/nano_module/power_monitor/New()
|
||||
..()
|
||||
refresh_sensors()
|
||||
|
||||
// Checks whether there is an active alarm, if yes, returns 1, otherwise returns 0.
|
||||
/datum/nano_module/power_monitor/proc/has_alarm()
|
||||
for(var/obj/machinery/power/sensor/S in grid_sensors)
|
||||
if(S.check_grid_warning())
|
||||
return 1
|
||||
return 0
|
||||
|
||||
// If PC is not null header template is loaded. Use PC.get_header_data() to get relevant nanoui data from it. All data entries begin with "PC_...."
|
||||
// In future it may be expanded to other modular computer devices.
|
||||
/datum/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
var/list/sensors = list()
|
||||
// Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading.
|
||||
var/obj/machinery/power/sensor/focus = null
|
||||
var/turf/T = get_turf(nano_host())
|
||||
|
||||
// Build list of data from sensor readings.
|
||||
for(var/obj/machinery/power/sensor/S in grid_sensors)
|
||||
sensors.Add(list(list(
|
||||
"name" = S.name_tag,
|
||||
"alarm" = S.check_grid_warning()
|
||||
)))
|
||||
if(S.name_tag == active_sensor)
|
||||
focus = S
|
||||
|
||||
data["all_sensors"] = sensors
|
||||
if(focus)
|
||||
data["focus"] = focus.return_reading_data()
|
||||
data["map_levels"] = using_map.get_map_levels(T.z)
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "power_monitor.tmpl", "Power Monitoring Console", 800, 500, state = state)
|
||||
if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI.
|
||||
ui.auto_update_layout = 1
|
||||
// adding a template with the key "mapContent" enables the map ui functionality
|
||||
ui.add_template("mapContent", "power_monitor_map_content.tmpl")
|
||||
// adding a template with the key "mapHeader" replaces the map header content
|
||||
ui.add_template("mapHeader", "power_monitor_map_header.tmpl")
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
// Refreshes list of active sensors kept on this computer.
|
||||
/datum/nano_module/power_monitor/proc/refresh_sensors()
|
||||
grid_sensors = list()
|
||||
var/turf/T = get_turf(nano_host())
|
||||
var/list/levels = list()
|
||||
if(!T) // Safety check
|
||||
return
|
||||
if(T)
|
||||
levels += using_map.get_map_levels(T.z, FALSE)
|
||||
for(var/obj/machinery/power/sensor/S in machines)
|
||||
if(T && (S.loc.z == T.z) || (S.loc.z in levels) || (S.long_range)) // Consoles have range on their Z-Level. Sensors with long_range var will work between Z levels.
|
||||
if(S.name_tag == "#UNKN#") // Default name. Shouldn't happen!
|
||||
warning("Powernet sensor with unset ID Tag! [S.x]X [S.y]Y [S.z]Z")
|
||||
else
|
||||
grid_sensors += S
|
||||
|
||||
// Allows us to process UI clicks, which are relayed in form of hrefs.
|
||||
/datum/nano_module/power_monitor/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if( href_list["clear"] )
|
||||
active_sensor = null
|
||||
. = 1
|
||||
if( href_list["refresh"] )
|
||||
refresh_sensors()
|
||||
. = 1
|
||||
else if( href_list["setsensor"] )
|
||||
active_sensor = href_list["setsensor"]
|
||||
. = 1
|
||||
@@ -0,0 +1,132 @@
|
||||
/datum/computer_file/program/rcon_console
|
||||
filename = "rconconsole"
|
||||
filedesc = "RCON Remote Control"
|
||||
nanomodule_path = /datum/nano_module/rcon
|
||||
program_icon_state = "generic"
|
||||
program_key_state = "rd_key"
|
||||
program_menu_icon = "power"
|
||||
extended_desc = "This program allows remote control of power distribution systems. This program can not be run on tablet computers."
|
||||
required_access = access_engine
|
||||
requires_ntnet = 1
|
||||
network_destination = "RCON remote control system"
|
||||
requires_ntnet_feature = NTNET_SYSTEMCONTROL
|
||||
usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE
|
||||
size = 19
|
||||
|
||||
/datum/nano_module/rcon
|
||||
name = "Power RCON"
|
||||
var/list/known_SMESs = null
|
||||
var/list/known_breakers = null
|
||||
// Allows you to hide specific parts of the UI
|
||||
var/hide_SMES = 0
|
||||
var/hide_SMES_details = 0
|
||||
var/hide_breakers = 0
|
||||
|
||||
/datum/nano_module/rcon/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1, var/datum/topic_state/state = default_state)
|
||||
FindDevices() // Update our devices list
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
// SMES DATA (simplified view)
|
||||
var/list/smeslist[0]
|
||||
for(var/obj/machinery/power/smes/buildable/SMES in known_SMESs)
|
||||
smeslist.Add(list(list(
|
||||
"charge" = round(SMES.Percentage()),
|
||||
"input_set" = SMES.input_attempt,
|
||||
"input_val" = round(SMES.input_level/1000, 0.1),
|
||||
"output_set" = SMES.output_attempt,
|
||||
"output_val" = round(SMES.output_level/1000, 0.1),
|
||||
"output_load" = round(SMES.output_used/1000, 0.1),
|
||||
"RCON_tag" = SMES.RCon_tag
|
||||
)))
|
||||
|
||||
data["smes_info"] = sortByKey(smeslist, "RCON_tag")
|
||||
|
||||
// BREAKER DATA (simplified view)
|
||||
var/list/breakerlist[0]
|
||||
for(var/obj/machinery/power/breakerbox/BR in known_breakers)
|
||||
breakerlist.Add(list(list(
|
||||
"RCON_tag" = BR.RCon_tag,
|
||||
"enabled" = BR.on
|
||||
)))
|
||||
data["breaker_info"] = breakerlist
|
||||
data["hide_smes"] = hide_SMES
|
||||
data["hide_smes_details"] = hide_SMES_details
|
||||
data["hide_breakers"] = hide_breakers
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "rcon.tmpl", "RCON Console", 600, 400, state = state)
|
||||
if(host.update_layout()) // This is necessary to ensure the status bar remains updated along with rest of the UI.
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
// Proc: Topic()
|
||||
// Parameters: 2 (href, href_list - allows us to process UI clicks)
|
||||
// Description: Allows us to process UI clicks, which are relayed in form of hrefs.
|
||||
/datum/nano_module/rcon/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(href_list["smes_in_toggle"])
|
||||
var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_toggle"])
|
||||
if(SMES)
|
||||
SMES.toggle_input()
|
||||
if(href_list["smes_out_toggle"])
|
||||
var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_toggle"])
|
||||
if(SMES)
|
||||
SMES.toggle_output()
|
||||
if(href_list["smes_in_set"])
|
||||
var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_in_set"])
|
||||
if(SMES)
|
||||
var/inputset = (input(usr, "Enter new input level (0-[SMES.input_level_max/1000] kW)", "SMES Input Power Control", SMES.input_level/1000) as num) * 1000
|
||||
SMES.set_input(inputset)
|
||||
if(href_list["smes_out_set"])
|
||||
var/obj/machinery/power/smes/buildable/SMES = GetSMESByTag(href_list["smes_out_set"])
|
||||
if(SMES)
|
||||
var/outputset = (input(usr, "Enter new output level (0-[SMES.output_level_max/1000] kW)", "SMES Output Power Control", SMES.output_level/1000) as num) * 1000
|
||||
SMES.set_output(outputset)
|
||||
|
||||
if(href_list["toggle_breaker"])
|
||||
var/obj/machinery/power/breakerbox/toggle = null
|
||||
for(var/obj/machinery/power/breakerbox/breaker in known_breakers)
|
||||
if(breaker.RCon_tag == href_list["toggle_breaker"])
|
||||
toggle = breaker
|
||||
if(toggle)
|
||||
if(toggle.update_locked)
|
||||
to_chat(usr, "The breaker box was recently toggled. Please wait before toggling it again.")
|
||||
else
|
||||
toggle.auto_toggle()
|
||||
if(href_list["hide_smes"])
|
||||
hide_SMES = !hide_SMES
|
||||
if(href_list["hide_smes_details"])
|
||||
hide_SMES_details = !hide_SMES_details
|
||||
if(href_list["hide_breakers"])
|
||||
hide_breakers = !hide_breakers
|
||||
|
||||
|
||||
// Proc: GetSMESByTag()
|
||||
// Parameters: 1 (tag - RCON tag of SMES we want to look up)
|
||||
// Description: Looks up and returns SMES which has matching RCON tag
|
||||
/datum/nano_module/rcon/proc/GetSMESByTag(var/tag)
|
||||
if(!tag)
|
||||
return
|
||||
|
||||
for(var/obj/machinery/power/smes/buildable/S in known_SMESs)
|
||||
if(S.RCon_tag == tag)
|
||||
return S
|
||||
|
||||
// Proc: FindDevices()
|
||||
// Parameters: None
|
||||
// Description: Refreshes local list of known devices.
|
||||
/datum/nano_module/rcon/proc/FindDevices()
|
||||
known_SMESs = new /list()
|
||||
for(var/obj/machinery/power/smes/buildable/SMES in machines)
|
||||
if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon)
|
||||
known_SMESs.Add(SMES)
|
||||
|
||||
known_breakers = new /list()
|
||||
for(var/obj/machinery/power/breakerbox/breaker in machines)
|
||||
if(breaker.RCon_tag != "NO_TAG")
|
||||
known_breakers.Add(breaker)
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/datum/computer_file/program/supermatter_monitor
|
||||
filename = "supmon"
|
||||
filedesc = "Supermatter Monitoring"
|
||||
nanomodule_path = /datum/nano_module/supermatter_monitor/
|
||||
program_icon_state = "smmon_0"
|
||||
program_key_state = "tech_key"
|
||||
program_menu_icon = "notice"
|
||||
extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines."
|
||||
ui_header = "smmon_0.gif"
|
||||
required_access = access_engine
|
||||
requires_ntnet = 1
|
||||
network_destination = "supermatter monitoring system"
|
||||
size = 5
|
||||
var/last_status = 0
|
||||
|
||||
/datum/computer_file/program/supermatter_monitor/process_tick()
|
||||
..()
|
||||
var/datum/nano_module/supermatter_monitor/NMS = NM
|
||||
var/new_status = istype(NMS) ? NMS.get_status() : 0
|
||||
if(last_status != new_status)
|
||||
last_status = new_status
|
||||
ui_header = "smmon_[last_status].gif"
|
||||
program_icon_state = "smmon_[last_status]"
|
||||
if(istype(computer))
|
||||
computer.update_icon()
|
||||
|
||||
/datum/nano_module/supermatter_monitor
|
||||
name = "Supermatter monitor"
|
||||
var/list/supermatters
|
||||
var/obj/machinery/power/supermatter/active = null // Currently selected supermatter crystal.
|
||||
|
||||
/datum/nano_module/supermatter_monitor/Destroy()
|
||||
. = ..()
|
||||
active = null
|
||||
supermatters = null
|
||||
|
||||
/datum/nano_module/supermatter_monitor/New()
|
||||
..()
|
||||
refresh()
|
||||
|
||||
// Refreshes list of active supermatter crystals
|
||||
/datum/nano_module/supermatter_monitor/proc/refresh()
|
||||
supermatters = list()
|
||||
var/turf/T = get_turf(nano_host())
|
||||
if(!T)
|
||||
return
|
||||
var/valid_z_levels = (GetConnectedZlevels(T.z) & using_map.station_levels)
|
||||
for(var/obj/machinery/power/supermatter/S in machines)
|
||||
// Delaminating, not within coverage, not on a tile.
|
||||
if(S.grav_pulling || S.exploded || !(S.z in valid_z_levels) || !istype(S.loc, /turf/))
|
||||
continue
|
||||
supermatters.Add(S)
|
||||
|
||||
if(!(active in supermatters))
|
||||
active = null
|
||||
|
||||
/datum/nano_module/supermatter_monitor/proc/get_status()
|
||||
. = SUPERMATTER_INACTIVE
|
||||
for(var/obj/machinery/power/supermatter/S in supermatters)
|
||||
. = max(., S.get_status())
|
||||
|
||||
/datum/nano_module/supermatter_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
if(istype(active))
|
||||
var/turf/T = get_turf(active)
|
||||
if(!T)
|
||||
active = null
|
||||
return
|
||||
var/datum/gas_mixture/air = T.return_air()
|
||||
if(!istype(air))
|
||||
active = null
|
||||
return
|
||||
|
||||
data["active"] = 1
|
||||
data["SM_integrity"] = active.get_integrity()
|
||||
data["SM_power"] = active.power
|
||||
data["SM_ambienttemp"] = air.temperature
|
||||
data["SM_ambientpressure"] = air.return_pressure()
|
||||
//data["SM_EPR"] = active.get_epr()
|
||||
if(air.total_moles)
|
||||
data["SM_gas_O2"] = round(100*air.gas["oxygen"]/air.total_moles,0.01)
|
||||
data["SM_gas_CO2"] = round(100*air.gas["carbon_dioxide"]/air.total_moles,0.01)
|
||||
data["SM_gas_N2"] = round(100*air.gas["nitrogen"]/air.total_moles,0.01)
|
||||
data["SM_gas_PH"] = round(100*air.gas["phoron"]/air.total_moles,0.01)
|
||||
data["SM_gas_N2O"] = round(100*air.gas["sleeping_agent"]/air.total_moles,0.01)
|
||||
else
|
||||
data["SM_gas_O2"] = 0
|
||||
data["SM_gas_CO2"] = 0
|
||||
data["SM_gas_N2"] = 0
|
||||
data["SM_gas_PH"] = 0
|
||||
data["SM_gas_N2O"] = 0
|
||||
else
|
||||
var/list/SMS = list()
|
||||
for(var/obj/machinery/power/supermatter/S in supermatters)
|
||||
var/area/A = get_area(S)
|
||||
if(!A)
|
||||
continue
|
||||
|
||||
SMS.Add(list(list(
|
||||
"area_name" = A.name,
|
||||
"integrity" = S.get_integrity(),
|
||||
"uid" = S.uid
|
||||
)))
|
||||
|
||||
data["active"] = 0
|
||||
data["supermatters"] = SMS
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "supermatter_monitor.tmpl", "Supermatter Monitoring", 600, 400, state = state)
|
||||
if(host.update_layout())
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/datum/nano_module/supermatter_monitor/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if( href_list["clear"] )
|
||||
active = null
|
||||
return 1
|
||||
if( href_list["refresh"] )
|
||||
refresh()
|
||||
return 1
|
||||
if( href_list["set"] )
|
||||
var/newuid = text2num(href_list["set"])
|
||||
for(var/obj/machinery/power/supermatter/S in supermatters)
|
||||
if(S.uid == newuid)
|
||||
active = S
|
||||
return 1
|
||||
@@ -0,0 +1,186 @@
|
||||
// Returns which access is relevant to passed network. Used by the program.
|
||||
/proc/get_camera_access(var/network)
|
||||
if(!network)
|
||||
return 0
|
||||
. = using_map.get_network_access(network)
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(network)
|
||||
if(NETWORK_THUNDER)
|
||||
return 0
|
||||
if(NETWORK_ENGINE,NETWORK_ENGINEERING,NETWORK_ENGINEERING_OUTPOST,NETWORK_ALARM_ATMOS,NETWORK_ALARM_FIRE,NETWORK_ALARM_POWER)
|
||||
return access_engine
|
||||
if(NETWORK_MEDICAL)
|
||||
return access_medical
|
||||
if(NETWORK_RESEARCH,NETWORK_RESEARCH_OUTPOST)
|
||||
return access_research
|
||||
if(NETWORK_MINE,NETWORK_CARGO )
|
||||
return access_mailsorting // Cargo office - all cargo staff should have access here.
|
||||
if(NETWORK_COMMAND,NETWORK_TELECOM)
|
||||
return access_heads
|
||||
if(NETWORK_ERT)
|
||||
return access_cent_specops
|
||||
|
||||
return access_security // Default for all other networks
|
||||
|
||||
/datum/computer_file/program/camera_monitor
|
||||
filename = "cammon"
|
||||
filedesc = "Camera Monitoring"
|
||||
nanomodule_path = /datum/nano_module/camera_monitor
|
||||
program_icon_state = "cameras"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "search"
|
||||
extended_desc = "This program allows remote access to the camera system. Some camera networks may have additional access requirements."
|
||||
size = 12
|
||||
available_on_ntnet = 1
|
||||
requires_ntnet = 1
|
||||
|
||||
/datum/nano_module/camera_monitor
|
||||
name = "Camera Monitoring program"
|
||||
var/obj/machinery/camera/current_camera = null
|
||||
var/current_network = null
|
||||
|
||||
/datum/nano_module/camera_monitor/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
data["current_camera"] = current_camera ? current_camera.nano_structure() : null
|
||||
data["current_network"] = current_network
|
||||
|
||||
var/list/all_networks[0]
|
||||
for(var/network in using_map.station_networks)
|
||||
all_networks.Add(list(list(
|
||||
"tag" = network,
|
||||
"has_access" = can_access_network(user, get_camera_access(network))
|
||||
)))
|
||||
|
||||
all_networks = modify_networks_list(all_networks)
|
||||
|
||||
data["networks"] = all_networks
|
||||
|
||||
if(current_network)
|
||||
data["cameras"] = camera_repository.cameras_in_network(current_network)
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "mod_sec_camera.tmpl", "Camera Monitoring", 900, 800)
|
||||
// ui.auto_update_layout = 1 // Disabled as with suit sensors monitor - breaks the UI map. Re-enable once it's fixed somehow.
|
||||
|
||||
ui.add_template("mapContent", "sec_camera_map_content.tmpl")
|
||||
ui.add_template("mapHeader", "mod_sec_camera_map_header.tmpl")
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
// Intended to be overriden by subtypes to manually add non-station networks to the list.
|
||||
/datum/nano_module/camera_monitor/proc/modify_networks_list(var/list/networks)
|
||||
return networks
|
||||
|
||||
/datum/nano_module/camera_monitor/proc/can_access_network(var/mob/user, var/network_access)
|
||||
// No access passed, or 0 which is considered no access requirement. Allow it.
|
||||
if(!network_access)
|
||||
return 1
|
||||
|
||||
return check_access(user, access_security) || check_access(user, network_access)
|
||||
|
||||
/datum/nano_module/camera_monitor/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["switch_camera"])
|
||||
var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras
|
||||
if(!C)
|
||||
return
|
||||
if(!(current_network in C.network))
|
||||
return
|
||||
|
||||
switch_to_camera(usr, C)
|
||||
return 1
|
||||
|
||||
else if(href_list["switch_network"])
|
||||
// Either security access, or access to the specific camera network's department is required in order to access the network.
|
||||
if(can_access_network(usr, get_camera_access(href_list["switch_network"])))
|
||||
current_network = href_list["switch_network"]
|
||||
else
|
||||
to_chat(usr, "\The [nano_host()] shows an \"Network Access Denied\" error message.")
|
||||
return 1
|
||||
|
||||
else if(href_list["reset"])
|
||||
reset_current()
|
||||
usr.reset_view(current_camera)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/camera_monitor/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C)
|
||||
//don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras.
|
||||
if(isAI(user))
|
||||
var/mob/living/silicon/ai/A = user
|
||||
// Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise.
|
||||
if(!A.is_in_chassis())
|
||||
return 0
|
||||
|
||||
A.eyeobj.setLoc(get_turf(C))
|
||||
A.client.eye = A.eyeobj
|
||||
return 1
|
||||
|
||||
set_current(C)
|
||||
user.machine = nano_host()
|
||||
user.reset_view(C)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/camera_monitor/proc/set_current(var/obj/machinery/camera/C)
|
||||
if(current_camera == C)
|
||||
return
|
||||
|
||||
if(current_camera)
|
||||
reset_current()
|
||||
|
||||
current_camera = C
|
||||
if(current_camera)
|
||||
var/mob/living/L = current_camera.loc
|
||||
if(istype(L))
|
||||
L.tracking_initiated()
|
||||
|
||||
/datum/nano_module/camera_monitor/proc/reset_current()
|
||||
if(current_camera)
|
||||
var/mob/living/L = current_camera.loc
|
||||
if(istype(L))
|
||||
L.tracking_cancelled()
|
||||
current_camera = null
|
||||
|
||||
/datum/nano_module/camera_monitor/check_eye(var/mob/user as mob)
|
||||
if(!current_camera)
|
||||
return 0
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if ( viewflag < 0 ) //camera doesn't work
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
|
||||
// ERT Variant of the program
|
||||
/datum/computer_file/program/camera_monitor/ert
|
||||
filename = "ntcammon"
|
||||
filedesc = "Advanced Camera Monitoring"
|
||||
extended_desc = "This program allows remote access to the camera system. Some camera networks may have additional access requirements. This version has an integrated database with additional encrypted keys."
|
||||
size = 14
|
||||
nanomodule_path = /datum/nano_module/camera_monitor/ert
|
||||
available_on_ntnet = 0
|
||||
|
||||
/datum/nano_module/camera_monitor/ert
|
||||
name = "Advanced Camera Monitoring Program"
|
||||
//available_to_ai = FALSE
|
||||
|
||||
// The ERT variant has access to ERT and crescent cams, but still checks for accesses. ERT members should be able to use it.
|
||||
/datum/nano_module/camera_monitor/ert/modify_networks_list(var/list/networks)
|
||||
..()
|
||||
networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1)))
|
||||
networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1)))
|
||||
return networks
|
||||
|
||||
/datum/nano_module/camera_monitor/apply_visual(mob/M)
|
||||
if(current_camera)
|
||||
current_camera.apply_visual(M)
|
||||
else
|
||||
remove_visual(M)
|
||||
|
||||
/datum/nano_module/camera_monitor/remove_visual(mob/M)
|
||||
if(current_camera)
|
||||
current_camera.remove_visual(M)
|
||||
@@ -0,0 +1,64 @@
|
||||
// This is special hardware configuration program.
|
||||
// It is to be used only with modular computers.
|
||||
// It allows you to toggle components of your device.
|
||||
|
||||
/datum/computer_file/program/computerconfig
|
||||
filename = "compconfig"
|
||||
filedesc = "Computer Configuration Tool"
|
||||
extended_desc = "This program allows configuration of computer's hardware"
|
||||
program_icon_state = "generic"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "gear"
|
||||
unsendable = 1
|
||||
undeletable = 1
|
||||
size = 4
|
||||
available_on_ntnet = 0
|
||||
requires_ntnet = 0
|
||||
nanomodule_path = /datum/nano_module/program/computer_configurator/
|
||||
|
||||
/datum/nano_module/program/computer_configurator
|
||||
name = "NTOS Computer Configuration Tool"
|
||||
var/obj/item/modular_computer/movable = null
|
||||
|
||||
/datum/nano_module/program/computer_configurator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(program)
|
||||
movable = program.computer
|
||||
if(!istype(movable))
|
||||
movable = null
|
||||
|
||||
// No computer connection, we can't get data from that.
|
||||
if(!movable)
|
||||
return 0
|
||||
|
||||
var/list/data = list()
|
||||
|
||||
if(program)
|
||||
data = program.get_header_data()
|
||||
|
||||
var/list/hardware = movable.get_all_components()
|
||||
|
||||
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())
|
||||
|
||||
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
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "laptop_configuration.tmpl", "NTOS Configuration Utility", 575, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
@@ -0,0 +1,499 @@
|
||||
/datum/computer_file/program/email_client
|
||||
filename = "emailc"
|
||||
filedesc = "Email Client"
|
||||
extended_desc = "This program may be used to log in into your email account."
|
||||
program_icon_state = "generic"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "mail-closed"
|
||||
size = 7
|
||||
requires_ntnet = 1
|
||||
available_on_ntnet = 1
|
||||
var/stored_login = ""
|
||||
var/stored_password = ""
|
||||
|
||||
nanomodule_path = /datum/nano_module/email_client
|
||||
|
||||
// Persistency. Unless you log out, or unless your password changes, this will pre-fill the login data when restarting the program
|
||||
/datum/computer_file/program/email_client/kill_program()
|
||||
if(NM)
|
||||
var/datum/nano_module/email_client/NME = NM
|
||||
if(NME.current_account)
|
||||
stored_login = NME.stored_login
|
||||
stored_password = NME.stored_password
|
||||
else
|
||||
stored_login = ""
|
||||
stored_password = ""
|
||||
. = ..()
|
||||
|
||||
/datum/computer_file/program/email_client/run_program()
|
||||
. = ..()
|
||||
if(NM)
|
||||
var/datum/nano_module/email_client/NME = NM
|
||||
NME.stored_login = stored_login
|
||||
NME.stored_password = stored_password
|
||||
NME.log_in()
|
||||
NME.error = ""
|
||||
NME.check_for_new_messages(1)
|
||||
|
||||
/datum/computer_file/program/email_client/proc/new_mail_notify()
|
||||
computer.visible_message("\The [computer] beeps softly, indicating a new email has been received.", 1)
|
||||
|
||||
/datum/computer_file/program/email_client/process_tick()
|
||||
..()
|
||||
var/datum/nano_module/email_client/NME = NM
|
||||
if(!istype(NME))
|
||||
return
|
||||
NME.relayed_process(ntnet_speed)
|
||||
|
||||
var/check_count = NME.check_for_new_messages()
|
||||
if(check_count)
|
||||
if(check_count == 2)
|
||||
new_mail_notify()
|
||||
ui_header = "ntnrc_new.gif"
|
||||
else
|
||||
ui_header = "ntnrc_idle.gif"
|
||||
|
||||
/datum/nano_module/email_client/
|
||||
name = "Email Client"
|
||||
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/nano_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/nano_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/nano_module/email_client/proc/log_out()
|
||||
current_account = null
|
||||
downloading = null
|
||||
download_progress = 0
|
||||
last_message_count = 0
|
||||
read_message_count = 0
|
||||
|
||||
/datum/nano_module/email_client/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_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."
|
||||
|
||||
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)
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "email_client.tmpl", "Email Client", 600, 450, state = state)
|
||||
if(host.update_layout())
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_auto_update(1)
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
/datum/nano_module/email_client/proc/find_message_by_fuid(var/fuid)
|
||||
if(!istype(current_account))
|
||||
return
|
||||
|
||||
// href_list 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/nano_module/email_client/proc/clear_message()
|
||||
new_message = FALSE
|
||||
msg_title = ""
|
||||
msg_body = ""
|
||||
msg_recipient = ""
|
||||
msg_attachment = null
|
||||
current_message = null
|
||||
|
||||
/datum/nano_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 = nano_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/nano_module/email_client/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
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.
|
||||
if(href_list["login"])
|
||||
log_in()
|
||||
return 1
|
||||
|
||||
if(href_list["logout"])
|
||||
log_out()
|
||||
return 1
|
||||
|
||||
if(href_list["reset"])
|
||||
error = ""
|
||||
return 1
|
||||
|
||||
if(href_list["new_message"])
|
||||
new_message = TRUE
|
||||
return 1
|
||||
|
||||
if(href_list["cancel"])
|
||||
if(addressbook)
|
||||
addressbook = FALSE
|
||||
else
|
||||
clear_message()
|
||||
return 1
|
||||
|
||||
if(href_list["addressbook"])
|
||||
addressbook = TRUE
|
||||
return 1
|
||||
|
||||
if(href_list["set_recipient"])
|
||||
msg_recipient = sanitize(href_list["set_recipient"])
|
||||
addressbook = FALSE
|
||||
return 1
|
||||
|
||||
if(href_list["edit_title"])
|
||||
var/newtitle = sanitize(input(user,"Enter title for your message:", "Message title", msg_title), 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(href_list["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(href_list["edit_recipient"])
|
||||
var/newrecipient = sanitize(input(user,"Enter recipient's email address:", "Recipient", msg_recipient), 100)
|
||||
if(newrecipient)
|
||||
msg_recipient = newrecipient
|
||||
return 1
|
||||
|
||||
if(href_list["edit_login"])
|
||||
var/newlogin = sanitize(input(user,"Enter login", "Login", stored_login), 100)
|
||||
if(newlogin)
|
||||
stored_login = newlogin
|
||||
return 1
|
||||
|
||||
if(href_list["edit_password"])
|
||||
var/newpass = sanitize(input(user,"Enter password", "Password"), 100)
|
||||
if(newpass)
|
||||
stored_password = newpass
|
||||
return 1
|
||||
|
||||
if(href_list["delete"])
|
||||
if(!istype(current_account))
|
||||
return 1
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["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(href_list["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(href_list["set_folder"])
|
||||
folder = href_list["set_folder"]
|
||||
return 1
|
||||
|
||||
if(href_list["reply"])
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["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(href_list["view"])
|
||||
var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["view"])
|
||||
if(istype(M))
|
||||
current_message = M
|
||||
return 1
|
||||
|
||||
if(href_list["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(href_list["save"])
|
||||
// Fully dependant on modular computers here.
|
||||
var/obj/item/modular_computer/MC = nano_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(href_list["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(href_list["addattachment"])
|
||||
var/obj/item/modular_computer/MC = nano_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(href_list["downloadattachment"])
|
||||
if(!current_account || !current_message || !current_message.attachment)
|
||||
return 1
|
||||
var/obj/item/modular_computer/MC = nano_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(href_list["canceldownload"])
|
||||
downloading = null
|
||||
download_progress = 0
|
||||
return 1
|
||||
|
||||
if(href_list["remove_attachment"])
|
||||
msg_attachment = null
|
||||
return 1
|
||||
@@ -0,0 +1,207 @@
|
||||
/datum/computer_file/program/filemanager
|
||||
filename = "filemanager"
|
||||
filedesc = "NTOS File Manager"
|
||||
extended_desc = "This program allows management of files."
|
||||
program_icon_state = "generic"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "folder-collapsed"
|
||||
size = 8
|
||||
requires_ntnet = 0
|
||||
available_on_ntnet = 0
|
||||
undeletable = 1
|
||||
nanomodule_path = /datum/nano_module/program/computer_filemanager/
|
||||
var/open_file
|
||||
var/error
|
||||
|
||||
/datum/computer_file/program/filemanager/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_openfile"])
|
||||
. = 1
|
||||
open_file = href_list["PRG_openfile"]
|
||||
if(href_list["PRG_newtextfile"])
|
||||
. = 1
|
||||
var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename"))
|
||||
if(!newname)
|
||||
return 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/data/F = new/datum/computer_file/data()
|
||||
F.filename = newname
|
||||
F.filetype = "TXT"
|
||||
HDD.store_file(F)
|
||||
if(href_list["PRG_deletefile"])
|
||||
. = 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_deletefile"])
|
||||
if(!file || file.undeletable)
|
||||
return 1
|
||||
HDD.remove_file(file)
|
||||
if(href_list["PRG_usbdeletefile"])
|
||||
. = 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive
|
||||
if(!RHDD)
|
||||
return 1
|
||||
var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_usbdeletefile"])
|
||||
if(!file || file.undeletable)
|
||||
return 1
|
||||
RHDD.remove_file(file)
|
||||
if(href_list["PRG_closefile"])
|
||||
. = 1
|
||||
open_file = null
|
||||
error = null
|
||||
if(href_list["PRG_clone"])
|
||||
. = 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_clone"])
|
||||
if(!F || !istype(F))
|
||||
return 1
|
||||
var/datum/computer_file/C = F.clone(1)
|
||||
HDD.store_file(C)
|
||||
if(href_list["PRG_rename"])
|
||||
. = 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_rename"])
|
||||
if(!file || !istype(file))
|
||||
return 1
|
||||
var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename))
|
||||
if(file && newname)
|
||||
file.filename = newname
|
||||
if(href_list["PRG_edit"])
|
||||
. = 1
|
||||
if(!open_file)
|
||||
return 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/data/F = HDD.find_file_by_name(open_file)
|
||||
if(!F || !istype(F))
|
||||
return 1
|
||||
if(F.do_not_edit && (alert("WARNING: This file is not compatible with editor. Editing it may result in permanently corrupted formatting or damaged data consistency. Edit anyway?", "Incompatible File", "No", "Yes") == "No"))
|
||||
return 1
|
||||
|
||||
var/oldtext = html_decode(F.stored_data)
|
||||
oldtext = replacetext(oldtext, "\[br\]", "\n")
|
||||
|
||||
var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH)
|
||||
if(!newtext)
|
||||
return
|
||||
|
||||
if(F)
|
||||
var/datum/computer_file/data/backup = F.clone()
|
||||
HDD.remove_file(F)
|
||||
F.stored_data = newtext
|
||||
F.calculate_size()
|
||||
// We can't store the updated file, it's probably too large. Print an error and restore backed up version.
|
||||
// This is mostly intended to prevent people from losing texts they spent lot of time working on due to running out of space.
|
||||
// They will be able to copy-paste the text from error screen and store it in notepad or something.
|
||||
if(!HDD.store_file(F))
|
||||
error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:<br><br>[html_decode(F.stored_data)]<br><br>"
|
||||
HDD.store_file(backup)
|
||||
if(href_list["PRG_printfile"])
|
||||
. = 1
|
||||
if(!open_file)
|
||||
return 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/data/F = HDD.find_file_by_name(open_file)
|
||||
if(!F || !istype(F))
|
||||
return 1
|
||||
if(!computer.nano_printer)
|
||||
error = "Missing Hardware: Your computer does not have required hardware to complete this operation."
|
||||
return 1
|
||||
if(!computer.nano_printer.print_text(pencode2html(F.stored_data)))
|
||||
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
|
||||
return 1
|
||||
if(href_list["PRG_copytousb"])
|
||||
. = 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive
|
||||
if(!HDD || !RHDD)
|
||||
return 1
|
||||
var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_copytousb"])
|
||||
if(!F || !istype(F))
|
||||
return 1
|
||||
var/datum/computer_file/C = F.clone(0)
|
||||
RHDD.store_file(C)
|
||||
if(href_list["PRG_copyfromusb"])
|
||||
. = 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive
|
||||
if(!HDD || !RHDD)
|
||||
return 1
|
||||
var/datum/computer_file/F = RHDD.find_file_by_name(href_list["PRG_copyfromusb"])
|
||||
if(!F || !istype(F))
|
||||
return 1
|
||||
var/datum/computer_file/C = F.clone(0)
|
||||
HDD.store_file(C)
|
||||
if(.)
|
||||
SSnanoui.update_uis(NM)
|
||||
|
||||
/datum/nano_module/program/computer_filemanager
|
||||
name = "NTOS File Manager"
|
||||
|
||||
/datum/nano_module/program/computer_filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
var/datum/computer_file/program/filemanager/PRG
|
||||
PRG = program
|
||||
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD
|
||||
if(PRG.error)
|
||||
data["error"] = PRG.error
|
||||
if(PRG.open_file)
|
||||
var/datum/computer_file/data/file
|
||||
|
||||
if(!PRG.computer || !PRG.computer.hard_drive)
|
||||
data["error"] = "I/O ERROR: Unable to access hard drive."
|
||||
else
|
||||
HDD = PRG.computer.hard_drive
|
||||
file = HDD.find_file_by_name(PRG.open_file)
|
||||
if(!istype(file))
|
||||
data["error"] = "I/O ERROR: Unable to open file."
|
||||
else
|
||||
data["filedata"] = pencode2html(file.stored_data)
|
||||
data["filename"] = "[file.filename].[file.filetype]"
|
||||
else
|
||||
if(!PRG.computer || !PRG.computer.hard_drive)
|
||||
data["error"] = "I/O ERROR: Unable to access hard drive."
|
||||
else
|
||||
HDD = PRG.computer.hard_drive
|
||||
RHDD = PRG.computer.portable_drive
|
||||
var/list/files[0]
|
||||
for(var/datum/computer_file/F in HDD.stored_files)
|
||||
files.Add(list(list(
|
||||
"name" = F.filename,
|
||||
"type" = F.filetype,
|
||||
"size" = F.size,
|
||||
"undeletable" = F.undeletable
|
||||
)))
|
||||
data["files"] = files
|
||||
if(RHDD)
|
||||
data["usbconnected"] = 1
|
||||
var/list/usbfiles[0]
|
||||
for(var/datum/computer_file/F in RHDD.stored_files)
|
||||
usbfiles.Add(list(list(
|
||||
"name" = F.filename,
|
||||
"type" = F.filetype,
|
||||
"size" = F.size,
|
||||
"undeletable" = F.undeletable
|
||||
)))
|
||||
data["usbfiles"] = usbfiles
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file is used as a reference for Modular Computers Development guide on the wiki. It contains a lot of excess comments, as it is intended as explanation
|
||||
// for someone who may not be as experienced in coding. When making changes, please try to keep it this way.
|
||||
|
||||
// An actual program definition.
|
||||
/datum/computer_file/program/game
|
||||
filename = "arcadec" // File name, as shown in the file browser program.
|
||||
filedesc = "Unknown Game" // User-Friendly name. In this case, we will generate a random name in constructor.
|
||||
program_icon_state = "game" // Icon state of this program's screen.
|
||||
program_menu_icon = "script"
|
||||
extended_desc = "Fun for the whole family! Probably not an AAA title, but at least you can download it on the corporate network.." // A nice description.
|
||||
size = 5 // Size in GQ. Integers only. Smaller sizes should be used for utility/low use programs (like this one), while large sizes are for important programs.
|
||||
requires_ntnet = 0 // This particular program does not require NTNet network conectivity...
|
||||
available_on_ntnet = 1 // ... but we want it to be available for download.
|
||||
nanomodule_path = /datum/nano_module/arcade_classic/ // Path of relevant nano module. The nano module is defined further in the file.
|
||||
var/picked_enemy_name
|
||||
|
||||
// Blatantly stolen and shortened version from arcade machines. Generates a random enemy name
|
||||
/datum/computer_file/program/game/proc/random_enemy_name()
|
||||
var/name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ")
|
||||
var/name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "Slime", "Lizard Man", "Unicorn")
|
||||
return "[name_part1] [name_part2]"
|
||||
|
||||
// When the program is first created, we generate a new enemy name and name ourselves accordingly.
|
||||
/datum/computer_file/program/game/New()
|
||||
..()
|
||||
picked_enemy_name = random_enemy_name()
|
||||
filedesc = "Defeat [picked_enemy_name]"
|
||||
|
||||
// Important in order to ensure that copied versions will have the same enemy name.
|
||||
/datum/computer_file/program/game/clone()
|
||||
var/datum/computer_file/program/game/G = ..()
|
||||
G.picked_enemy_name = picked_enemy_name
|
||||
return G
|
||||
|
||||
// When running the program, we also want to pass our enemy name to the nano module.
|
||||
/datum/computer_file/program/game/run_program()
|
||||
. = ..()
|
||||
if(. && NM)
|
||||
var/datum/nano_module/arcade_classic/NMC = NM
|
||||
NMC.enemy_name = picked_enemy_name
|
||||
|
||||
|
||||
// Nano module the program uses.
|
||||
// This can be either /datum/nano_module/ or /datum/nano_module/program. The latter is intended for nano modules that are suposed to be exclusively used with modular computers,
|
||||
// and should generally not be used, as such nano modules are hard to use on other places.
|
||||
/datum/nano_module/arcade_classic/
|
||||
name = "Classic Arcade"
|
||||
var/player_mana // Various variables specific to the nano module. In this case, the nano module is a simple arcade game, so the variables store health and other stats.
|
||||
var/player_health
|
||||
var/enemy_mana
|
||||
var/enemy_health
|
||||
var/enemy_name = "Greytide Horde"
|
||||
var/gameover
|
||||
var/information
|
||||
|
||||
/datum/nano_module/arcade_classic/New()
|
||||
..()
|
||||
new_game()
|
||||
|
||||
// ui_interact handles transfer of data to NanoUI. Keep in mind that data you pass from here is actually sent to the client. In other words, don't send anything you don't want a client
|
||||
// to see, and don't send unnecessarily large amounts of data (due to laginess).
|
||||
/datum/nano_module/arcade_classic/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
data["player_health"] = player_health
|
||||
data["player_mana"] = player_mana
|
||||
data["enemy_health"] = enemy_health
|
||||
data["enemy_mana"] = enemy_mana
|
||||
data["enemy_name"] = enemy_name
|
||||
data["gameover"] = gameover
|
||||
data["information"] = information
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "arcade_classic.tmpl", "Defeat [enemy_name]", 500, 350, state = state)
|
||||
if(host.update_layout())
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
// Three helper procs i've created. These are unique to this particular nano module. If you are creating your own nano module, you'll most likely create similar procs too.
|
||||
/datum/nano_module/arcade_classic/proc/enemy_play()
|
||||
if((enemy_mana < 5) && prob(60))
|
||||
var/steal = rand(2, 3)
|
||||
player_mana -= steal
|
||||
enemy_mana += steal
|
||||
information += "[enemy_name] steals [steal] of your power!"
|
||||
else if((enemy_health < 15) && (enemy_mana > 3) && prob(80))
|
||||
var/healamt = min(rand(3, 5), enemy_mana)
|
||||
enemy_mana -= healamt
|
||||
enemy_health += healamt
|
||||
information += "[enemy_name] heals for [healamt] health!"
|
||||
else
|
||||
var/dam = rand(3,6)
|
||||
player_health -= dam
|
||||
information += "[enemy_name] attacks for [dam] damage!"
|
||||
|
||||
/datum/nano_module/arcade_classic/proc/check_gameover()
|
||||
if((player_health <= 0) || player_mana <= 0)
|
||||
if(enemy_health <= 0)
|
||||
information += "You have defeated [enemy_name], but you have died in the fight!"
|
||||
else
|
||||
information += "You have been defeated by [enemy_name]!"
|
||||
gameover = 1
|
||||
return TRUE
|
||||
else if(enemy_health <= 0)
|
||||
gameover = 1
|
||||
information += "Congratulations! You have defeated [enemy_name]!"
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/nano_module/arcade_classic/proc/new_game()
|
||||
player_mana = 10
|
||||
player_health = 30
|
||||
enemy_mana = 20
|
||||
enemy_health = 45
|
||||
gameover = FALSE
|
||||
information = "A new game has started!"
|
||||
|
||||
|
||||
|
||||
/datum/nano_module/arcade_classic/Topic(href, href_list)
|
||||
if(..()) // Always begin your Topic() calls with a parent call!
|
||||
return 1
|
||||
if(href_list["new_game"])
|
||||
new_game()
|
||||
return 1 // Returning 1 (TRUE) in Topic automatically handles UI updates.
|
||||
if(gameover) // If the game has already ended, we don't want the following three topic calls to be processed at all.
|
||||
return 1 // Instead of adding checks into each of those three, we can easily add this one check here to reduce on code copy-paste.
|
||||
if(href_list["attack"])
|
||||
var/damage = rand(2, 6)
|
||||
information = "You attack for [damage] damage."
|
||||
enemy_health -= damage
|
||||
enemy_play()
|
||||
check_gameover()
|
||||
return 1
|
||||
if(href_list["heal"])
|
||||
var/healfor = rand(6, 8)
|
||||
var/cost = rand(1, 3)
|
||||
information = "You heal yourself for [healfor] damage, using [cost] energy in the process."
|
||||
player_health += healfor
|
||||
player_mana -= cost
|
||||
enemy_play()
|
||||
check_gameover()
|
||||
return 1
|
||||
if(href_list["regain_mana"])
|
||||
var/regen = rand(4, 7)
|
||||
information = "You rest of a while, regaining [regen] energy."
|
||||
player_mana += regen
|
||||
enemy_play()
|
||||
check_gameover()
|
||||
return 1
|
||||
@@ -0,0 +1,131 @@
|
||||
/datum/computer_file/program/newsbrowser
|
||||
filename = "newsbrowser"
|
||||
filedesc = "NTNet/ExoNet News Browser"
|
||||
extended_desc = "This program may be used to view and download news articles from the network."
|
||||
program_icon_state = "generic"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "contact"
|
||||
size = 4
|
||||
requires_ntnet = 1
|
||||
available_on_ntnet = 1
|
||||
|
||||
nanomodule_path = /datum/nano_module/program/computer_newsbrowser/
|
||||
var/datum/computer_file/data/news_article/loaded_article
|
||||
var/download_progress = 0
|
||||
var/download_netspeed = 0
|
||||
var/downloading = 0
|
||||
var/message = ""
|
||||
var/show_archived = 0
|
||||
|
||||
/datum/computer_file/program/newsbrowser/process_tick()
|
||||
if(!downloading)
|
||||
return
|
||||
download_netspeed = 0
|
||||
// Speed defines are found in misc.dm
|
||||
switch(ntnet_status)
|
||||
if(1)
|
||||
download_netspeed = NTNETSPEED_LOWSIGNAL
|
||||
if(2)
|
||||
download_netspeed = NTNETSPEED_HIGHSIGNAL
|
||||
if(3)
|
||||
download_netspeed = NTNETSPEED_ETHERNET
|
||||
download_progress += download_netspeed
|
||||
if(download_progress >= loaded_article.size)
|
||||
downloading = 0
|
||||
requires_ntnet = 0 // Turn off NTNet requirement as we already loaded the file into local memory.
|
||||
SSnanoui.update_uis(NM)
|
||||
|
||||
/datum/computer_file/program/newsbrowser/kill_program()
|
||||
..()
|
||||
requires_ntnet = 1
|
||||
loaded_article = null
|
||||
download_progress = 0
|
||||
downloading = 0
|
||||
show_archived = 0
|
||||
|
||||
/datum/computer_file/program/newsbrowser/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["PRG_openarticle"])
|
||||
. = 1
|
||||
if(downloading || loaded_article)
|
||||
return 1
|
||||
|
||||
for(var/datum/computer_file/data/news_article/N in ntnet_global.available_news)
|
||||
if(N.uid == text2num(href_list["PRG_openarticle"]))
|
||||
loaded_article = N.clone()
|
||||
downloading = 1
|
||||
break
|
||||
if(href_list["PRG_reset"])
|
||||
. = 1
|
||||
downloading = 0
|
||||
download_progress = 0
|
||||
requires_ntnet = 1
|
||||
loaded_article = null
|
||||
if(href_list["PRG_clearmessage"])
|
||||
. = 1
|
||||
message = ""
|
||||
if(href_list["PRG_savearticle"])
|
||||
. = 1
|
||||
if(downloading || !loaded_article)
|
||||
return
|
||||
|
||||
var/savename = sanitize(input(usr, "Enter file name or leave blank to cancel:", "Save article", loaded_article.filename))
|
||||
if(!savename)
|
||||
return 1
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return 1
|
||||
var/datum/computer_file/data/news_article/N = loaded_article.clone()
|
||||
N.filename = savename
|
||||
HDD.store_file(N)
|
||||
if(href_list["PRG_toggle_archived"])
|
||||
. = 1
|
||||
show_archived = !show_archived
|
||||
if(.)
|
||||
SSnanoui.update_uis(NM)
|
||||
|
||||
|
||||
/datum/nano_module/program/computer_newsbrowser
|
||||
name = "NTNet/ExoNet News Browser"
|
||||
|
||||
/datum/nano_module/program/computer_newsbrowser/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
|
||||
var/datum/computer_file/program/newsbrowser/PRG
|
||||
var/list/data = list()
|
||||
if(program)
|
||||
data = program.get_header_data()
|
||||
PRG = program
|
||||
else
|
||||
return
|
||||
|
||||
data["message"] = PRG.message
|
||||
if(PRG.loaded_article && !PRG.downloading) // Viewing an article.
|
||||
data["title"] = PRG.loaded_article.filename
|
||||
data["cover"] = PRG.loaded_article.cover
|
||||
data["article"] = PRG.loaded_article.stored_data
|
||||
else if(PRG.downloading) // Downloading an article.
|
||||
data["download_running"] = 1
|
||||
data["download_progress"] = PRG.download_progress
|
||||
data["download_maxprogress"] = PRG.loaded_article.size
|
||||
data["download_rate"] = PRG.download_netspeed
|
||||
else // Viewing list of articles
|
||||
var/list/all_articles[0]
|
||||
for(var/datum/computer_file/data/news_article/F in ntnet_global.available_news)
|
||||
if(!PRG.show_archived && F.archived)
|
||||
continue
|
||||
all_articles.Add(list(list(
|
||||
"name" = F.filename,
|
||||
"size" = F.size,
|
||||
"uid" = F.uid,
|
||||
"archived" = F.archived
|
||||
)))
|
||||
data["all_articles"] = all_articles
|
||||
data["showing_archived"] = PRG.show_archived
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "news_browser.tmpl", "NTNet/ExoNet News Browser", 575, 750, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
@@ -0,0 +1,200 @@
|
||||
/datum/computer_file/program/ntnetdownload
|
||||
filename = "ntndownloader"
|
||||
filedesc = "NTNet Software Download Tool"
|
||||
program_icon_state = "generic"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "arrowthickstop-1-s"
|
||||
extended_desc = "This program allows downloads of software from official NT repositories"
|
||||
unsendable = 1
|
||||
undeletable = 1
|
||||
size = 4
|
||||
requires_ntnet = 1
|
||||
requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD
|
||||
available_on_ntnet = 0
|
||||
nanomodule_path = /datum/nano_module/program/computer_ntnetdownload/
|
||||
ui_header = "downloader_finished.gif"
|
||||
var/datum/computer_file/program/downloaded_file = null
|
||||
var/hacked_download = 0
|
||||
var/download_completion = 0 //GQ of downloaded data.
|
||||
var/download_netspeed = 0
|
||||
var/downloaderror = ""
|
||||
var/list/downloads_queue[0]
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/kill_program()
|
||||
..()
|
||||
downloaded_file = null
|
||||
download_completion = 0
|
||||
download_netspeed = 0
|
||||
downloaderror = ""
|
||||
ui_header = "downloader_finished.gif"
|
||||
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/proc/begin_file_download(var/filename)
|
||||
if(downloaded_file)
|
||||
return 0
|
||||
|
||||
var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename)
|
||||
|
||||
if(!check_file_download(filename))
|
||||
return 0
|
||||
|
||||
ui_header = "downloader_running.gif"
|
||||
|
||||
if(PRG in ntnet_global.available_station_software)
|
||||
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.")
|
||||
hacked_download = 0
|
||||
else if(PRG in ntnet_global.available_antag_software)
|
||||
generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.")
|
||||
hacked_download = 1
|
||||
else
|
||||
generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from unspecified server.")
|
||||
hacked_download = 0
|
||||
|
||||
downloaded_file = PRG.clone()
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/proc/check_file_download(var/filename)
|
||||
//returns 1 if file can be downloaded, returns 0 if download prohibited
|
||||
var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename)
|
||||
|
||||
if(!PRG || !istype(PRG))
|
||||
return 0
|
||||
|
||||
// Attempting to download antag only program, but without having emagged computer. No.
|
||||
if(PRG.available_on_syndinet && !computer_emagged)
|
||||
return 0
|
||||
|
||||
if(!computer || !computer.hard_drive || !computer.hard_drive.try_store_file(PRG))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/proc/abort_file_download()
|
||||
if(!downloaded_file)
|
||||
return
|
||||
generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].")
|
||||
downloaded_file = null
|
||||
download_completion = 0
|
||||
ui_header = "downloader_finished.gif"
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/proc/complete_file_download()
|
||||
if(!downloaded_file)
|
||||
return
|
||||
generate_network_log("Completed download of file [hacked_download ? "**ENCRYPTED**" : downloaded_file.filename].[downloaded_file.filetype].")
|
||||
if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(downloaded_file))
|
||||
// The download failed
|
||||
downloaderror = "I/O ERROR - Unable to save file. Check whether you have enough free space on your hard drive and whether your hard drive is properly connected. If the issue persists contact your system administrator for assistance."
|
||||
downloaded_file = null
|
||||
download_completion = 0
|
||||
ui_header = "downloader_finished.gif"
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/process_tick()
|
||||
if(!downloaded_file)
|
||||
return
|
||||
if(download_completion >= downloaded_file.size)
|
||||
complete_file_download()
|
||||
if(downloads_queue.len > 0)
|
||||
begin_file_download(downloads_queue[1])
|
||||
downloads_queue.Remove(downloads_queue[1])
|
||||
|
||||
// Download speed according to connectivity state. NTNet server is assumed to be on unlimited speed so we're limited by our local connectivity
|
||||
download_netspeed = 0
|
||||
// Speed defines are found in misc.dm
|
||||
switch(ntnet_status)
|
||||
if(1)
|
||||
download_netspeed = NTNETSPEED_LOWSIGNAL
|
||||
if(2)
|
||||
download_netspeed = NTNETSPEED_HIGHSIGNAL
|
||||
if(3)
|
||||
download_netspeed = NTNETSPEED_ETHERNET
|
||||
download_completion += download_netspeed
|
||||
|
||||
/datum/computer_file/program/ntnetdownload/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["PRG_downloadfile"])
|
||||
if(!downloaded_file)
|
||||
begin_file_download(href_list["PRG_downloadfile"])
|
||||
else if(check_file_download(href_list["PRG_downloadfile"]) && !downloads_queue.Find(href_list["PRG_downloadfile"]) && downloaded_file.filename != href_list["PRG_downloadfile"])
|
||||
downloads_queue += href_list["PRG_downloadfile"]
|
||||
return 1
|
||||
if(href_list["PRG_removequeued"])
|
||||
downloads_queue.Remove(href_list["PRG_removequeued"])
|
||||
return 1
|
||||
if(href_list["PRG_reseterror"])
|
||||
if(downloaderror)
|
||||
download_completion = 0
|
||||
download_netspeed = 0
|
||||
downloaded_file = null
|
||||
downloaderror = ""
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/nano_module/program/computer_ntnetdownload
|
||||
name = "Network Downloader"
|
||||
var/obj/item/modular_computer/my_computer = null
|
||||
|
||||
/datum/nano_module/program/computer_ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(program)
|
||||
my_computer = program.computer
|
||||
|
||||
if(!istype(my_computer))
|
||||
return
|
||||
|
||||
var/list/data = list()
|
||||
var/datum/computer_file/program/ntnetdownload/prog = program
|
||||
// For now limited to execution by the downloader program
|
||||
if(!prog || !istype(prog))
|
||||
return
|
||||
if(program)
|
||||
data = program.get_header_data()
|
||||
|
||||
// This IF cuts on data transferred to client, so i guess it's worth it.
|
||||
if(prog.downloaderror) // Download errored. Wait until user resets the program.
|
||||
data["error"] = prog.downloaderror
|
||||
if(prog.downloaded_file) // Download running. Wait please..
|
||||
data["downloadname"] = prog.downloaded_file.filename
|
||||
data["downloaddesc"] = prog.downloaded_file.filedesc
|
||||
data["downloadsize"] = prog.downloaded_file.size
|
||||
data["downloadspeed"] = prog.download_netspeed
|
||||
data["downloadcompletion"] = round(prog.download_completion, 0.1)
|
||||
|
||||
data["disk_size"] = my_computer.hard_drive.max_capacity
|
||||
data["disk_used"] = my_computer.hard_drive.used_capacity
|
||||
var/list/all_entries[0]
|
||||
for(var/datum/computer_file/program/P in ntnet_global.available_station_software)
|
||||
// Only those programs our user can run will show in the list
|
||||
if(!P.can_run(user) && P.requires_access_to_download)
|
||||
continue
|
||||
all_entries.Add(list(list(
|
||||
"filename" = P.filename,
|
||||
"filedesc" = P.filedesc,
|
||||
"fileinfo" = P.extended_desc,
|
||||
"size" = P.size,
|
||||
"icon" = P.program_menu_icon
|
||||
)))
|
||||
data["hackedavailable"] = 0
|
||||
if(prog.computer_emagged) // If we are running on emagged computer we have access to some "bonus" software
|
||||
var/list/hacked_programs[0]
|
||||
for(var/datum/computer_file/program/P in ntnet_global.available_antag_software)
|
||||
data["hackedavailable"] = 1
|
||||
hacked_programs.Add(list(list(
|
||||
"filename" = P.filename,
|
||||
"filedesc" = P.filedesc,
|
||||
"fileinfo" = P.extended_desc,
|
||||
"size" = P.size,
|
||||
"icon" = P.program_menu_icon
|
||||
)))
|
||||
data["hacked_programs"] = hacked_programs
|
||||
|
||||
data["downloadable_programs"] = all_entries
|
||||
|
||||
if(prog.downloads_queue.len > 0)
|
||||
data["downloads_queue"] = prog.downloads_queue
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
@@ -0,0 +1,232 @@
|
||||
/datum/computer_file/program/chatclient
|
||||
filename = "ntnrc_client"
|
||||
filedesc = "NTNet Relay Chat Client"
|
||||
program_icon_state = "command"
|
||||
program_key_state = "med_key"
|
||||
program_menu_icon = "comment"
|
||||
extended_desc = "This program allows communication over NTNRC network"
|
||||
size = 8
|
||||
requires_ntnet = 1
|
||||
requires_ntnet_feature = NTNET_COMMUNICATION
|
||||
network_destination = "NTNRC server"
|
||||
ui_header = "ntnrc_idle.gif"
|
||||
available_on_ntnet = 1
|
||||
nanomodule_path = /datum/nano_module/program/computer_chatclient/
|
||||
var/last_message = null // Used to generate the toolbar icon
|
||||
var/username
|
||||
var/datum/ntnet_conversation/channel = null
|
||||
var/operator_mode = 0 // Channel operator mode
|
||||
var/netadmin_mode = 0 // Administrator mode (invisible to other users + bypasses passwords)
|
||||
|
||||
/datum/computer_file/program/chatclient/New()
|
||||
username = "DefaultUser[rand(100, 999)]"
|
||||
|
||||
/datum/computer_file/program/chatclient/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_speak"])
|
||||
. = 1
|
||||
if(!channel)
|
||||
return 1
|
||||
var/mob/living/user = usr
|
||||
var/message = sanitize(input(user, "Enter message or leave blank to cancel: "), 512)
|
||||
if(!message || !channel)
|
||||
return
|
||||
channel.add_message(message, username)
|
||||
|
||||
if(href_list["PRG_joinchannel"])
|
||||
. = 1
|
||||
var/datum/ntnet_conversation/C
|
||||
for(var/datum/ntnet_conversation/chan in ntnet_global.chat_channels)
|
||||
if(chan.id == text2num(href_list["PRG_joinchannel"]))
|
||||
C = chan
|
||||
break
|
||||
|
||||
if(!C)
|
||||
return 1
|
||||
|
||||
if(netadmin_mode)
|
||||
channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others.
|
||||
return 1
|
||||
|
||||
if(C.password)
|
||||
var/mob/living/user = usr
|
||||
var/password = sanitize(input(user,"Access Denied. Enter password:"))
|
||||
if(C && (password == C.password))
|
||||
C.add_client(src)
|
||||
channel = C
|
||||
return 1
|
||||
C.add_client(src)
|
||||
channel = C
|
||||
if(href_list["PRG_leavechannel"])
|
||||
. = 1
|
||||
if(channel)
|
||||
channel.remove_client(src)
|
||||
channel = null
|
||||
if(href_list["PRG_newchannel"])
|
||||
. = 1
|
||||
var/mob/living/user = usr
|
||||
var/channel_title = sanitizeSafe(input(user,"Enter channel name or leave blank to cancel:"), 64)
|
||||
if(!channel_title)
|
||||
return
|
||||
var/datum/ntnet_conversation/C = new/datum/ntnet_conversation()
|
||||
C.add_client(src)
|
||||
C.operator = src
|
||||
channel = C
|
||||
C.title = channel_title
|
||||
if(href_list["PRG_toggleadmin"])
|
||||
. = 1
|
||||
if(netadmin_mode)
|
||||
netadmin_mode = 0
|
||||
if(channel)
|
||||
channel.remove_client(src) // We shouldn't be in channel's user list, but just in case...
|
||||
channel = null
|
||||
return 1
|
||||
var/mob/living/user = usr
|
||||
if(can_run(usr, 1, access_network))
|
||||
if(channel)
|
||||
var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
if(channel)
|
||||
channel.remove_client(src)
|
||||
channel = null
|
||||
else
|
||||
return
|
||||
netadmin_mode = 1
|
||||
if(href_list["PRG_changename"])
|
||||
. = 1
|
||||
var/mob/living/user = usr
|
||||
var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:"), 20)
|
||||
if(!newname)
|
||||
return 1
|
||||
if(channel)
|
||||
channel.add_status_message("[username] is now known as [newname].")
|
||||
username = newname
|
||||
|
||||
if(href_list["PRG_savelog"])
|
||||
. = 1
|
||||
if(!channel)
|
||||
return
|
||||
var/mob/living/user = usr
|
||||
var/logname = input(user,"Enter desired logfile name (.log) or leave blank to cancel:")
|
||||
if(!logname || !channel)
|
||||
return 1
|
||||
var/datum/computer_file/data/logfile = new/datum/computer_file/data/logfile()
|
||||
// Now we will generate HTML-compliant file that can actually be viewed/printed.
|
||||
logfile.filename = logname
|
||||
logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]"
|
||||
for(var/logstring in channel.messages)
|
||||
logfile.stored_data += "[logstring]\[BR\]"
|
||||
logfile.stored_data += "\[b\]Logfile dump completed.\[/b\]"
|
||||
logfile.calculate_size()
|
||||
if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(logfile))
|
||||
if(!computer)
|
||||
// This program shouldn't even be runnable without computer.
|
||||
CRASH("Var computer is null!")
|
||||
return 1
|
||||
if(!computer.hard_drive)
|
||||
computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.")
|
||||
else // In 99.9% cases this will mean our HDD is full
|
||||
computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.")
|
||||
if(href_list["PRG_renamechannel"])
|
||||
. = 1
|
||||
if(!operator_mode || !channel)
|
||||
return 1
|
||||
var/mob/living/user = usr
|
||||
var/newname = sanitize(input(user, "Enter new channel name or leave blank to cancel:"), 64)
|
||||
if(!newname || !channel)
|
||||
return
|
||||
channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.")
|
||||
channel.title = newname
|
||||
if(href_list["PRG_deletechannel"])
|
||||
. = 1
|
||||
if(channel && ((channel.operator == src) || netadmin_mode))
|
||||
qdel(channel)
|
||||
channel = null
|
||||
if(href_list["PRG_setpassword"])
|
||||
. = 1
|
||||
if(!channel || ((channel.operator != src) && !netadmin_mode))
|
||||
return 1
|
||||
|
||||
var/mob/living/user = usr
|
||||
var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:"))
|
||||
if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode))
|
||||
return 1
|
||||
|
||||
if(newpassword == "nopassword")
|
||||
channel.password = ""
|
||||
else
|
||||
channel.password = newpassword
|
||||
|
||||
/datum/computer_file/program/chatclient/process_tick()
|
||||
..()
|
||||
if(program_state != PROGRAM_STATE_KILLED)
|
||||
ui_header = "ntnrc_idle.gif"
|
||||
if(channel)
|
||||
// Remember the last message. If there is no message in the channel remember null.
|
||||
last_message = channel.messages.len ? channel.messages[channel.messages.len - 1] : null
|
||||
else
|
||||
last_message = null
|
||||
return 1
|
||||
if(channel && channel.messages && channel.messages.len)
|
||||
ui_header = last_message == channel.messages[channel.messages.len - 1] ? "ntnrc_idle.gif" : "ntnrc_new.gif"
|
||||
else
|
||||
ui_header = "ntnrc_idle.gif"
|
||||
|
||||
/datum/computer_file/program/chatclient/kill_program(var/forced = 0)
|
||||
if(channel)
|
||||
channel.remove_client(src)
|
||||
channel = null
|
||||
..(forced)
|
||||
|
||||
/datum/nano_module/program/computer_chatclient
|
||||
name = "NTNet Relay Chat Client"
|
||||
|
||||
/datum/nano_module/program/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(!ntnet_global || !ntnet_global.chat_channels)
|
||||
return
|
||||
|
||||
var/list/data = list()
|
||||
if(program)
|
||||
data = program.get_header_data()
|
||||
|
||||
var/datum/computer_file/program/chatclient/C = program
|
||||
if(!istype(C))
|
||||
return
|
||||
|
||||
data["adminmode"] = C.netadmin_mode
|
||||
if(C.channel)
|
||||
data["title"] = C.channel.title
|
||||
var/list/messages[0]
|
||||
for(var/M in C.channel.messages)
|
||||
messages.Add(list(list(
|
||||
"msg" = M
|
||||
)))
|
||||
data["messages"] = messages
|
||||
var/list/clients[0]
|
||||
for(var/datum/computer_file/program/chatclient/cl in C.channel.clients)
|
||||
clients.Add(list(list(
|
||||
"name" = cl.username
|
||||
)))
|
||||
data["clients"] = clients
|
||||
C.operator_mode = (C.channel.operator == C) ? 1 : 0
|
||||
data["is_operator"] = C.operator_mode || C.netadmin_mode
|
||||
|
||||
else // Channel selection screen
|
||||
var/list/all_channels[0]
|
||||
for(var/datum/ntnet_conversation/conv in ntnet_global.chat_channels)
|
||||
if(conv && conv.title)
|
||||
all_channels.Add(list(list(
|
||||
"chan" = conv.title,
|
||||
"id" = conv.id
|
||||
)))
|
||||
data["all_channels"] = all_channels
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
@@ -0,0 +1,185 @@
|
||||
var/global/nttransfer_uid = 0
|
||||
|
||||
/datum/computer_file/program/nttransfer
|
||||
filename = "nttransfer"
|
||||
filedesc = "NTNet P2P Transfer Client"
|
||||
extended_desc = "This program allows for simple file transfer via direct peer to peer connection."
|
||||
program_icon_state = "comm_logs"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "transferthick-e-w"
|
||||
size = 7
|
||||
requires_ntnet = 1
|
||||
requires_ntnet_feature = NTNET_PEERTOPEER
|
||||
network_destination = "other device via P2P tunnel"
|
||||
available_on_ntnet = 1
|
||||
nanomodule_path = /datum/nano_module/program/computer_nttransfer/
|
||||
|
||||
var/error = "" // Error screen
|
||||
var/server_password = "" // Optional password to download the file.
|
||||
var/datum/computer_file/provided_file = null // File which is provided to clients.
|
||||
var/datum/computer_file/downloaded_file = null // File which is being downloaded
|
||||
var/list/connected_clients = list() // List of connected clients.
|
||||
var/datum/computer_file/program/nttransfer/remote // Client var, specifies who are we downloading from.
|
||||
var/download_completion = 0 // Download progress in GQ
|
||||
var/actual_netspeed = 0 // Displayed in the UI, this is the actual transfer speed.
|
||||
var/unique_token // UID of this program
|
||||
var/upload_menu = 0 // Whether we show the program list and upload menu
|
||||
|
||||
/datum/computer_file/program/nttransfer/New()
|
||||
unique_token = nttransfer_uid
|
||||
nttransfer_uid++
|
||||
..()
|
||||
|
||||
/datum/computer_file/program/nttransfer/process_tick()
|
||||
..()
|
||||
// Server mode
|
||||
if(provided_file)
|
||||
for(var/datum/computer_file/program/nttransfer/C in connected_clients)
|
||||
// Transfer speed is limited by device which uses slower connectivity.
|
||||
// We can have multiple clients downloading at same time, but let's assume we use some sort of multicast transfer
|
||||
// so they can all run on same speed.
|
||||
C.actual_netspeed = min(C.ntnet_speed, ntnet_speed)
|
||||
C.download_completion += C.actual_netspeed
|
||||
if(C.download_completion >= provided_file.size)
|
||||
C.finish_download()
|
||||
else if(downloaded_file) // Client mode
|
||||
if(!remote)
|
||||
crash_download("Connection to remote server lost")
|
||||
|
||||
/datum/computer_file/program/nttransfer/kill_program(var/forced = 0)
|
||||
if(downloaded_file) // Client mode, clean up variables for next use
|
||||
finalize_download()
|
||||
|
||||
if(provided_file) // Server mode, disconnect all clients
|
||||
for(var/datum/computer_file/program/nttransfer/P in connected_clients)
|
||||
P.crash_download("Connection terminated by remote server")
|
||||
downloaded_file = null
|
||||
..(forced)
|
||||
|
||||
// Finishes download and attempts to store the file on HDD
|
||||
/datum/computer_file/program/nttransfer/proc/finish_download()
|
||||
if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(downloaded_file))
|
||||
error = "I/O Error: Unable to save file. Check your hard drive and try again."
|
||||
finalize_download()
|
||||
|
||||
// Crashes the download and displays specific error message
|
||||
/datum/computer_file/program/nttransfer/proc/crash_download(var/message)
|
||||
error = message ? message : "An unknown error has occured during download"
|
||||
finalize_download()
|
||||
|
||||
// Cleans up variables for next use
|
||||
/datum/computer_file/program/nttransfer/proc/finalize_download()
|
||||
if(remote)
|
||||
remote.connected_clients.Remove(src)
|
||||
downloaded_file = null
|
||||
remote = null
|
||||
download_completion = 0
|
||||
|
||||
|
||||
/datum/nano_module/program/computer_nttransfer
|
||||
name = "NTNet P2P Transfer Client"
|
||||
|
||||
/datum/nano_module/program/computer_nttransfer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(!program)
|
||||
return
|
||||
var/datum/computer_file/program/nttransfer/PRG = program
|
||||
if(!istype(PRG))
|
||||
return
|
||||
|
||||
var/list/data = program.get_header_data()
|
||||
|
||||
if(PRG.error)
|
||||
data["error"] = PRG.error
|
||||
else if(PRG.downloaded_file)
|
||||
data["downloading"] = 1
|
||||
data["download_size"] = PRG.downloaded_file.size
|
||||
data["download_progress"] = PRG.download_completion
|
||||
data["download_netspeed"] = PRG.actual_netspeed
|
||||
data["download_name"] = "[PRG.downloaded_file.filename].[PRG.downloaded_file.filetype]"
|
||||
else if (PRG.provided_file)
|
||||
data["uploading"] = 1
|
||||
data["upload_uid"] = PRG.unique_token
|
||||
data["upload_clients"] = PRG.connected_clients.len
|
||||
data["upload_haspassword"] = PRG.server_password ? 1 : 0
|
||||
data["upload_filename"] = "[PRG.provided_file.filename].[PRG.provided_file.filetype]"
|
||||
else if (PRG.upload_menu)
|
||||
var/list/all_files[0]
|
||||
for(var/datum/computer_file/F in PRG.computer.hard_drive.stored_files)
|
||||
all_files.Add(list(list(
|
||||
"uid" = F.uid,
|
||||
"filename" = "[F.filename].[F.filetype]",
|
||||
"size" = F.size
|
||||
)))
|
||||
data["upload_filelist"] = all_files
|
||||
else
|
||||
var/list/all_servers[0]
|
||||
for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers)
|
||||
if(!P.provided_file)
|
||||
continue
|
||||
all_servers.Add(list(list(
|
||||
"uid" = P.unique_token,
|
||||
"filename" = "[P.provided_file.filename].[P.provided_file.filetype]",
|
||||
"size" = P.provided_file.size,
|
||||
"haspassword" = P.server_password ? 1 : 0
|
||||
)))
|
||||
data["servers"] = all_servers
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "ntnet_transfer.tmpl", "NTNet P2P Transfer Client", 575, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/datum/computer_file/program/nttransfer/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["PRG_downloadfile"])
|
||||
for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers)
|
||||
if("[P.unique_token]" == href_list["PRG_downloadfile"])
|
||||
remote = P
|
||||
break
|
||||
if(!remote || !remote.provided_file)
|
||||
return
|
||||
if(remote.server_password)
|
||||
var/pass = sanitize(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required"))
|
||||
if(pass != remote.server_password)
|
||||
error = "Incorrect Password"
|
||||
return
|
||||
downloaded_file = remote.provided_file.clone()
|
||||
remote.connected_clients.Add(src)
|
||||
return 1
|
||||
if(href_list["PRG_reset"])
|
||||
error = ""
|
||||
upload_menu = 0
|
||||
finalize_download()
|
||||
if(src in ntnet_global.fileservers)
|
||||
ntnet_global.fileservers.Remove(src)
|
||||
for(var/datum/computer_file/program/nttransfer/T in connected_clients)
|
||||
T.crash_download("Remote server has forcibly closed the connection")
|
||||
provided_file = null
|
||||
return 1
|
||||
if(href_list["PRG_setpassword"])
|
||||
var/pass = sanitize(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none"))
|
||||
if(!pass)
|
||||
return
|
||||
if(pass == "none")
|
||||
server_password = ""
|
||||
return
|
||||
server_password = pass
|
||||
return 1
|
||||
if(href_list["PRG_uploadfile"])
|
||||
for(var/datum/computer_file/F in computer.hard_drive.stored_files)
|
||||
if("[F.uid]" == href_list["PRG_uploadfile"])
|
||||
if(F.unsendable)
|
||||
error = "I/O Error: File locked."
|
||||
return
|
||||
provided_file = F
|
||||
ntnet_global.fileservers.Add(src)
|
||||
return
|
||||
error = "I/O Error: Unable to locate file on hard drive."
|
||||
return 1
|
||||
if(href_list["PRG_uploadmenu"])
|
||||
upload_menu = 1
|
||||
return 0
|
||||
@@ -0,0 +1,235 @@
|
||||
/datum/computer_file/program/wordprocessor
|
||||
filename = "wordprocessor"
|
||||
filedesc = "NanoWord"
|
||||
extended_desc = "This program allows the editing and preview of text documents."
|
||||
program_icon_state = "word"
|
||||
program_key_state = "atmos_key"
|
||||
size = 4
|
||||
requires_ntnet = 0
|
||||
available_on_ntnet = 1
|
||||
nanomodule_path = /datum/nano_module/program/computer_wordprocessor/
|
||||
var/browsing
|
||||
var/open_file
|
||||
var/loaded_data
|
||||
var/error
|
||||
var/is_edited
|
||||
|
||||
/datum/computer_file/program/wordprocessor/proc/get_file(var/filename)
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return
|
||||
var/datum/computer_file/data/F = HDD.find_file_by_name(filename)
|
||||
if(!istype(F))
|
||||
return
|
||||
return F
|
||||
|
||||
/datum/computer_file/program/wordprocessor/proc/open_file(var/filename)
|
||||
var/datum/computer_file/data/F = get_file(filename)
|
||||
if(F)
|
||||
open_file = F.filename
|
||||
loaded_data = F.stored_data
|
||||
return 1
|
||||
|
||||
/datum/computer_file/program/wordprocessor/proc/save_file(var/filename)
|
||||
var/datum/computer_file/data/F = get_file(filename)
|
||||
if(!F) //try to make one if it doesn't exist
|
||||
F = create_file(filename, loaded_data)
|
||||
return !isnull(F)
|
||||
var/datum/computer_file/data/backup = F.clone()
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return
|
||||
HDD.remove_file(F)
|
||||
F.stored_data = loaded_data
|
||||
F.calculate_size()
|
||||
if(!HDD.store_file(F))
|
||||
HDD.store_file(backup)
|
||||
return 0
|
||||
is_edited = 0
|
||||
return 1
|
||||
|
||||
/datum/computer_file/program/wordprocessor/proc/create_file(var/newname, var/data = "")
|
||||
if(!newname)
|
||||
return
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return
|
||||
if(get_file(newname))
|
||||
return
|
||||
var/datum/computer_file/data/F = new/datum/computer_file/data()
|
||||
F.filename = newname
|
||||
F.filetype = "TXT"
|
||||
F.stored_data = data
|
||||
F.calculate_size()
|
||||
if(HDD.store_file(F))
|
||||
return F
|
||||
|
||||
/datum/computer_file/program/wordprocessor/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_txtrpeview"])
|
||||
show_browser(usr,"<HTML><HEAD><TITLE>[open_file]</TITLE></HEAD>[pencode2html(loaded_data)]</BODY></HTML>", "window=[open_file]")
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_taghelp"])
|
||||
to_chat(usr, "<span class='notice'>The hologram of a googly-eyed paper clip helpfully tells you:</span>")
|
||||
var/help = {"
|
||||
\[br\] : Creates a linebreak.
|
||||
\[center\] - \[/center\] : Centers the text.
|
||||
\[h1\] - \[/h1\] : First level heading.
|
||||
\[h2\] - \[/h2\] : Second level heading.
|
||||
\[h3\] - \[/h3\] : Third level heading.
|
||||
\[b\] - \[/b\] : Bold.
|
||||
\[i\] - \[/i\] : Italic.
|
||||
\[u\] - \[/u\] : Underlined.
|
||||
\[small\] - \[/small\] : Decreases the size of the text.
|
||||
\[large\] - \[/large\] : Increases the size of the text.
|
||||
\[field\] : Inserts a blank text field, which can be filled later. Useful for forms.
|
||||
\[date\] : Current station date.
|
||||
\[time\] : Current station time.
|
||||
\[list\] - \[/list\] : Begins and ends a list.
|
||||
\[*\] : A list item.
|
||||
\[hr\] : Horizontal rule.
|
||||
\[table\] - \[/table\] : Creates table using \[row\] and \[cell\] tags.
|
||||
\[grid\] - \[/grid\] : Table without visible borders, for layouts.
|
||||
\[row\] - New table row.
|
||||
\[cell\] - New table cell.
|
||||
\[logo\] - Inserts NT logo image.
|
||||
\[redlogo\] - Inserts red NT logo image.
|
||||
\[sglogo\] - Inserts Solgov insignia image."}
|
||||
|
||||
to_chat(usr, help)
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_closebrowser"])
|
||||
browsing = 0
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_backtomenu"])
|
||||
error = null
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_loadmenu"])
|
||||
browsing = 1
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_openfile"])
|
||||
. = 1
|
||||
if(is_edited)
|
||||
if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes")
|
||||
save_file(open_file)
|
||||
browsing = 0
|
||||
if(!open_file(href_list["PRG_openfile"]))
|
||||
error = "I/O error: Unable to open file '[href_list["PRG_openfile"]]'."
|
||||
|
||||
if(href_list["PRG_newfile"])
|
||||
. = 1
|
||||
if(is_edited)
|
||||
if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes")
|
||||
save_file(open_file)
|
||||
|
||||
var/newname = sanitize(input(usr, "Enter file name:", "New File") as text|null)
|
||||
if(!newname)
|
||||
return 1
|
||||
var/datum/computer_file/data/F = create_file(newname)
|
||||
if(F)
|
||||
open_file = F.filename
|
||||
loaded_data = ""
|
||||
return 1
|
||||
else
|
||||
error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'."
|
||||
|
||||
if(href_list["PRG_saveasfile"])
|
||||
. = 1
|
||||
var/newname = sanitize(input(usr, "Enter file name:", "Save As") as text|null)
|
||||
if(!newname)
|
||||
return 1
|
||||
var/datum/computer_file/data/F = create_file(newname, loaded_data)
|
||||
if(F)
|
||||
open_file = F.filename
|
||||
else
|
||||
error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'."
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_savefile"])
|
||||
. = 1
|
||||
if(!open_file)
|
||||
open_file = sanitize(input(usr, "Enter file name:", "Save As") as text|null)
|
||||
if(!open_file)
|
||||
return 0
|
||||
if(!save_file(open_file))
|
||||
error = "I/O error: Unable to save file '[open_file]'."
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_editfile"])
|
||||
var/oldtext = html_decode(loaded_data)
|
||||
oldtext = replacetext(oldtext, "\[br\]", "\n")
|
||||
|
||||
var/newtext = sanitize(replacetext(input(usr, "Editing file '[open_file]'. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH)
|
||||
if(!newtext)
|
||||
return
|
||||
loaded_data = newtext
|
||||
is_edited = 1
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_printfile"])
|
||||
. = 1
|
||||
if(!computer.nano_printer)
|
||||
error = "Missing Hardware: Your computer does not have the required hardware to complete this operation."
|
||||
return 1
|
||||
if(!computer.nano_printer.print_text(pencode2html(loaded_data)))
|
||||
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
|
||||
return 1
|
||||
|
||||
/datum/nano_module/program/computer_wordprocessor
|
||||
name = "Word Processor"
|
||||
|
||||
/datum/nano_module/program/computer_wordprocessor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
var/datum/computer_file/program/wordprocessor/PRG
|
||||
PRG = program
|
||||
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/HDD
|
||||
var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD
|
||||
if(PRG.error)
|
||||
data["error"] = PRG.error
|
||||
if(PRG.browsing)
|
||||
data["browsing"] = PRG.browsing
|
||||
if(!PRG.computer || !PRG.computer.hard_drive)
|
||||
data["error"] = "I/O ERROR: Unable to access hard drive."
|
||||
else
|
||||
HDD = PRG.computer.hard_drive
|
||||
var/list/files[0]
|
||||
for(var/datum/computer_file/F in HDD.stored_files)
|
||||
if(F.filetype == "TXT")
|
||||
files.Add(list(list(
|
||||
"name" = F.filename,
|
||||
"size" = F.size
|
||||
)))
|
||||
data["files"] = files
|
||||
|
||||
RHDD = PRG.computer.portable_drive
|
||||
if(RHDD)
|
||||
data["usbconnected"] = 1
|
||||
var/list/usbfiles[0]
|
||||
for(var/datum/computer_file/F in RHDD.stored_files)
|
||||
if(F.filetype == "TXT")
|
||||
usbfiles.Add(list(list(
|
||||
"name" = F.filename,
|
||||
"size" = F.size,
|
||||
)))
|
||||
data["usbfiles"] = usbfiles
|
||||
else if(PRG.open_file)
|
||||
data["filedata"] = pencode2html(PRG.loaded_data)
|
||||
data["filename"] = PRG.is_edited ? "[PRG.open_file]*" : PRG.open_file
|
||||
else
|
||||
data["filedata"] = pencode2html(PRG.loaded_data)
|
||||
data["filename"] = "UNNAMED"
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "word_processor.tmpl", "Word Processor", 575, 700, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
@@ -0,0 +1,69 @@
|
||||
/datum/computer_file/program/suit_sensors
|
||||
filename = "sensormonitor"
|
||||
filedesc = "Suit Sensors Monitoring"
|
||||
nanomodule_path = /datum/nano_module/crew_monitor
|
||||
program_icon_state = "crew"
|
||||
program_key_state = "med_key"
|
||||
program_menu_icon = "heart"
|
||||
extended_desc = "This program connects to life signs monitoring system to provide basic information on crew health."
|
||||
required_access = access_medical
|
||||
requires_ntnet = 1
|
||||
network_destination = "crew lifesigns monitoring system"
|
||||
size = 11
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/nano_module/crew_monitor
|
||||
name = "Crew monitor"
|
||||
|
||||
/datum/nano_module/crew_monitor/Topic(href, href_list)
|
||||
if(..()) return 1
|
||||
var/turf/T = get_turf(nano_host()) // TODO: Allow setting any using_map.contact_levels from the interface.
|
||||
if (!T || !(T.z in using_map.player_levels))
|
||||
usr << "<span class='warning'>Unable to establish a connection</span>: You're too far away from the station!"
|
||||
return 0
|
||||
if(href_list["track"])
|
||||
if(isAI(usr))
|
||||
var/mob/living/silicon/ai/AI = usr
|
||||
var/mob/living/carbon/human/H = locate(href_list["track"]) in mob_list
|
||||
if(hassensorlevel(H, SUIT_SENSOR_TRACKING))
|
||||
AI.ai_actual_track(H)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
var/turf/T = get_turf(nano_host())
|
||||
|
||||
data["isAI"] = isAI(user)
|
||||
data["map_levels"] = using_map.get_map_levels(T.z, FALSE)
|
||||
data["crewmembers"] = list()
|
||||
for(var/z in (data["map_levels"] | T.z)) // Always show crew from the current Z even if we can't show a map
|
||||
data["crewmembers"] += crew_repository.health_data(z)
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800, state = state)
|
||||
|
||||
// adding a template with the key "mapContent" enables the map ui functionality
|
||||
ui.add_template("mapContent", "crew_monitor_map_content.tmpl")
|
||||
// adding a template with the key "mapHeader" replaces the map header content
|
||||
ui.add_template("mapHeader", "crew_monitor_map_header.tmpl")
|
||||
if(!(ui.map_z_level in data["map_levels"]))
|
||||
ui.set_map_z_level(data["map_levels"][1])
|
||||
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
// should make the UI auto-update; doesn't seem to?
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/*/datum/nano_module/crew_monitor/proc/scan()
|
||||
for(var/mob/living/carbon/human/H in mob_list)
|
||||
if(istype(H.w_uniform, /obj/item/clothing/under))
|
||||
var/obj/item/clothing/under/C = H.w_uniform
|
||||
if (C.has_sensor)
|
||||
tracked |= C
|
||||
return 1
|
||||
*/
|
||||
@@ -0,0 +1,144 @@
|
||||
/datum/computer_file/program/email_administration
|
||||
filename = "emailadmin"
|
||||
filedesc = "Email Administration Utility"
|
||||
extended_desc = "This program may be used to administrate NTNet's emailing service."
|
||||
program_icon_state = "comm_monitor"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "mail-open"
|
||||
size = 12
|
||||
requires_ntnet = 1
|
||||
available_on_ntnet = 1
|
||||
nanomodule_path = /datum/nano_module/email_administration
|
||||
required_access = access_network
|
||||
|
||||
|
||||
|
||||
|
||||
/datum/nano_module/email_administration/
|
||||
name = "Email Client"
|
||||
var/datum/computer_file/data/email_account/current_account = null
|
||||
var/datum/computer_file/data/email_message/current_message = null
|
||||
var/error = ""
|
||||
|
||||
/datum/nano_module/email_administration/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
if(error)
|
||||
data["error"] = error
|
||||
else if(istype(current_message))
|
||||
data["msg_title"] = current_message.title
|
||||
data["msg_body"] = pencode2html(current_message.stored_data)
|
||||
data["msg_timestamp"] = current_message.timestamp
|
||||
data["msg_source"] = current_message.source
|
||||
else if(istype(current_account))
|
||||
data["current_account"] = current_account.login
|
||||
data["cur_suspended"] = current_account.suspended
|
||||
var/list/all_messages = list()
|
||||
for(var/datum/computer_file/data/email_message/message in (current_account.inbox | current_account.spam | current_account.deleted))
|
||||
all_messages.Add(list(list(
|
||||
"title" = message.title,
|
||||
"source" = message.source,
|
||||
"timestamp" = message.timestamp,
|
||||
"uid" = message.uid
|
||||
)))
|
||||
data["messages"] = all_messages
|
||||
data["messagecount"] = all_messages.len
|
||||
else
|
||||
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,
|
||||
"uid" = account.uid
|
||||
)))
|
||||
data["accounts"] = all_accounts
|
||||
data["accountcount"] = all_accounts.len
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "email_administration.tmpl", "Email Administration Utility", 600, 450, state = state)
|
||||
if(host.update_layout())
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_auto_update(1)
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
|
||||
/datum/nano_module/email_administration/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
var/mob/user = usr
|
||||
if(!istype(user))
|
||||
return 1
|
||||
|
||||
// High security - can only be operated when the user has an ID with access on them.
|
||||
var/obj/item/weapon/card/id/I = user.GetIdCard()
|
||||
if(!istype(I) || !(access_network in I.access))
|
||||
return 1
|
||||
|
||||
if(href_list["back"])
|
||||
if(error)
|
||||
error = ""
|
||||
else if(current_message)
|
||||
current_message = null
|
||||
else
|
||||
current_account = null
|
||||
return 1
|
||||
|
||||
if(href_list["ban"])
|
||||
if(!current_account)
|
||||
return 1
|
||||
|
||||
current_account.suspended = !current_account.suspended
|
||||
ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended by SA [I.registered_name] ([I.assignment]).")
|
||||
error = "Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended."
|
||||
return 1
|
||||
|
||||
if(href_list["changepass"])
|
||||
if(!current_account)
|
||||
return 1
|
||||
|
||||
var/newpass = sanitize(input(user,"Enter new password for account [current_account.login]", "Password"), 100)
|
||||
if(!newpass)
|
||||
return 1
|
||||
current_account.password = newpass
|
||||
ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Password for account [current_account.login] has been changed by SA [I.registered_name] ([I.assignment]).")
|
||||
return 1
|
||||
|
||||
if(href_list["viewmail"])
|
||||
if(!current_account)
|
||||
return 1
|
||||
|
||||
for(var/datum/computer_file/data/email_message/received_message in (current_account.inbox | current_account.spam | current_account.deleted))
|
||||
if(received_message.uid == text2num(href_list["viewmail"]))
|
||||
current_message = received_message
|
||||
break
|
||||
return 1
|
||||
|
||||
if(href_list["viewaccount"])
|
||||
for(var/datum/computer_file/data/email_account/email_account in ntnet_global.email_accounts)
|
||||
if(email_account.uid == text2num(href_list["viewaccount"]))
|
||||
current_account = email_account
|
||||
break
|
||||
return 1
|
||||
|
||||
if(href_list["newaccount"])
|
||||
var/newdomain = sanitize(input(user,"Pick domain:", "Domain name") as null|anything in using_map.usable_email_tlds)
|
||||
if(!newdomain)
|
||||
return 1
|
||||
var/newlogin = sanitize(input(user,"Pick account name (@[newdomain]):", "Account name"), 100)
|
||||
if(!newlogin)
|
||||
return 1
|
||||
|
||||
var/complete_login = "[newlogin]@[newdomain]"
|
||||
if(ntnet_global.does_email_exist(complete_login))
|
||||
error = "Error creating account: An account with same address already exists."
|
||||
return 1
|
||||
|
||||
var/datum/computer_file/data/email_account/new_account = new/datum/computer_file/data/email_account()
|
||||
new_account.login = complete_login
|
||||
new_account.password = GenerateKey()
|
||||
error = "Email [new_account.login] has been created, with generated password [new_account.password]"
|
||||
return 1
|
||||
@@ -0,0 +1,102 @@
|
||||
/datum/computer_file/program/ntnetmonitor
|
||||
filename = "ntmonitor"
|
||||
filedesc = "NTNet Diagnostics and Monitoring"
|
||||
program_icon_state = "comm_monitor"
|
||||
program_key_state = "generic_key"
|
||||
program_menu_icon = "wrench"
|
||||
extended_desc = "This program monitors the local NTNet network, provides access to logging systems, and allows for configuration changes"
|
||||
size = 12
|
||||
requires_ntnet = 1
|
||||
required_access = access_network
|
||||
available_on_ntnet = 1
|
||||
nanomodule_path = /datum/nano_module/computer_ntnetmonitor/
|
||||
|
||||
/datum/nano_module/computer_ntnetmonitor
|
||||
name = "NTNet Diagnostics and Monitoring"
|
||||
//available_to_ai = TRUE
|
||||
|
||||
/datum/nano_module/computer_ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
if(!ntnet_global)
|
||||
return
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
data["ntnetstatus"] = ntnet_global.check_function()
|
||||
data["ntnetrelays"] = ntnet_global.relays.len
|
||||
data["idsstatus"] = ntnet_global.intrusion_detection_enabled
|
||||
data["idsalarm"] = ntnet_global.intrusion_detection_alarm
|
||||
|
||||
data["config_softwaredownload"] = ntnet_global.setting_softwaredownload
|
||||
data["config_peertopeer"] = ntnet_global.setting_peertopeer
|
||||
data["config_communication"] = ntnet_global.setting_communication
|
||||
data["config_systemcontrol"] = ntnet_global.setting_systemcontrol
|
||||
|
||||
data["ntnetlogs"] = ntnet_global.logs
|
||||
data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount
|
||||
|
||||
data["banned_nids"] = list(ntnet_global.banned_nids)
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700, state = state)
|
||||
if(host.update_layout())
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/datum/nano_module/computer_ntnetmonitor/Topic(href, href_list, state)
|
||||
var/mob/user = usr
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["resetIDS"])
|
||||
if(ntnet_global)
|
||||
ntnet_global.resetIDS()
|
||||
return 1
|
||||
if(href_list["toggleIDS"])
|
||||
if(ntnet_global)
|
||||
ntnet_global.toggleIDS()
|
||||
return 1
|
||||
if(href_list["toggleWireless"])
|
||||
if(!ntnet_global)
|
||||
return 1
|
||||
|
||||
// NTNet is disabled. Enabling can be done without user prompt
|
||||
if(ntnet_global.setting_disabled)
|
||||
ntnet_global.setting_disabled = 0
|
||||
return 1
|
||||
|
||||
// NTNet is enabled and user is about to shut it down. Let's ask them if they really want to do it, as wirelessly connected computers won't connect without NTNet being enabled (which may prevent people from turning it back on)
|
||||
if(!user)
|
||||
return 1
|
||||
var/response = alert(user, "Really disable NTNet wireless? If your computer is connected wirelessly you won't be able to turn it back on! This will affect all connected wireless devices.", "NTNet shutdown", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
ntnet_global.setting_disabled = 1
|
||||
return 1
|
||||
if(href_list["purgelogs"])
|
||||
if(ntnet_global)
|
||||
ntnet_global.purge_logs()
|
||||
return 1
|
||||
if(href_list["updatemaxlogs"])
|
||||
var/logcount = text2num(input(user,"Enter amount of logs to keep in memory ([MIN_NTNET_LOGS]-[MAX_NTNET_LOGS]):"))
|
||||
if(ntnet_global)
|
||||
ntnet_global.update_max_log_count(logcount)
|
||||
return 1
|
||||
if(href_list["toggle_function"])
|
||||
if(!ntnet_global)
|
||||
return 1
|
||||
ntnet_global.toggle_function(href_list["toggle_function"])
|
||||
return 1
|
||||
if(href_list["ban_nid"])
|
||||
if(!ntnet_global)
|
||||
return 1
|
||||
var/nid = input(user,"Enter NID of device which you want to block from the network:", "Enter NID") as null|num
|
||||
if(nid && CanUseTopic(user, state))
|
||||
ntnet_global.banned_nids |= nid
|
||||
return 1
|
||||
if(href_list["unban_nid"])
|
||||
if(!ntnet_global)
|
||||
return 1
|
||||
var/nid = input(user,"Enter NID of device which you want to unblock from the network:", "Enter NID") as null|num
|
||||
if(nid && CanUseTopic(user, state))
|
||||
ntnet_global.banned_nids -= nid
|
||||
return 1
|
||||
@@ -0,0 +1,143 @@
|
||||
var/warrant_uid = 0
|
||||
/datum/datacore/var/list/warrants[] = list()
|
||||
/datum/data/record/warrant
|
||||
var/warrant_id
|
||||
|
||||
/datum/data/record/warrant/New()
|
||||
..()
|
||||
warrant_id = warrant_uid++
|
||||
|
||||
|
||||
/datum/computer_file/program/digitalwarrant
|
||||
filename = "digitalwarrant"
|
||||
filedesc = "Warrant Assistant"
|
||||
extended_desc = "Official NTsec program for creation and handling of warrants."
|
||||
size = 8
|
||||
program_icon_state = "warrant"
|
||||
program_key_state = "security_key"
|
||||
program_menu_icon = "star"
|
||||
requires_ntnet = 1
|
||||
available_on_ntnet = 1
|
||||
required_access = access_security
|
||||
usage_flags = PROGRAM_ALL
|
||||
nanomodule_path = /datum/nano_module/program/digitalwarrant/
|
||||
|
||||
/datum/nano_module/program/digitalwarrant/
|
||||
name = "Warrant Assistant"
|
||||
var/datum/data/record/warrant/activewarrant
|
||||
|
||||
/datum/nano_module/program/digitalwarrant/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
|
||||
var/list/data = host.initial_data()
|
||||
|
||||
if(activewarrant)
|
||||
data["warrantname"] = activewarrant.fields["namewarrant"]
|
||||
data["warrantcharges"] = activewarrant.fields["charges"]
|
||||
data["warrantauth"] = activewarrant.fields["auth"]
|
||||
data["type"] = activewarrant.fields["arrestsearch"]
|
||||
else
|
||||
var/list/allwarrants = list()
|
||||
for(var/datum/data/record/warrant/W in data_core.warrants)
|
||||
allwarrants.Add(list(list(
|
||||
"warrantname" = W.fields["namewarrant"],
|
||||
"charges" = "[copytext(W.fields["charges"],1,min(length(W.fields["charges"]) + 1, 50))]...",
|
||||
"auth" = W.fields["auth"],
|
||||
"id" = W.warrant_id,
|
||||
"arrestsearch" = W.fields["arrestsearch"]
|
||||
)))
|
||||
data["allwarrants"] = allwarrants
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "digitalwarrant.tmpl", name, 500, 350, state = state)
|
||||
ui.auto_update_layout = 1
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
|
||||
/datum/nano_module/program/digitalwarrant/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["sw_menu"])
|
||||
activewarrant = null
|
||||
|
||||
if(href_list["editwarrant"])
|
||||
. = 1
|
||||
for(var/datum/data/record/warrant/W in data_core.warrants)
|
||||
if(W.warrant_id == text2num(href_list["editwarrant"]))
|
||||
activewarrant = W
|
||||
break
|
||||
|
||||
// The following actions will only be possible if the user has an ID with security access equipped. This is in line with modular computer framework's authentication methods,
|
||||
// which also use RFID scanning to allow or disallow access to some functions. Anyone can view warrants, editing requires ID. This also prevents situations where you show a tablet
|
||||
// to someone who is to be arrested, which allows them to change the stuff there.
|
||||
|
||||
var/mob/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
var/obj/item/weapon/card/id/I = user.GetIdCard()
|
||||
if(!istype(I) || !I.registered_name || !(access_security in I.access))
|
||||
to_chat(user, "Authentication error: Unable to locate ID with apropriate access to allow this operation.")
|
||||
return
|
||||
|
||||
if(href_list["addwarrant"])
|
||||
. = 1
|
||||
var/datum/data/record/warrant/W = new()
|
||||
var/temp = sanitize(input(usr, "Do you want to create a search-, or an arrest warrant?") as null|anything in list("search","arrest"))
|
||||
if(CanInteract(user, default_state))
|
||||
if(temp == "arrest")
|
||||
W.fields["namewarrant"] = "Unknown"
|
||||
W.fields["charges"] = "No charges present"
|
||||
W.fields["auth"] = "Unauthorized"
|
||||
W.fields["arrestsearch"] = "arrest"
|
||||
if(temp == "search")
|
||||
W.fields["namewarrant"] = "No location given"
|
||||
W.fields["charges"] = "No reason given"
|
||||
W.fields["auth"] = "Unauthorized"
|
||||
W.fields["arrestsearch"] = "search"
|
||||
activewarrant = W
|
||||
|
||||
if(href_list["savewarrant"])
|
||||
. = 1
|
||||
data_core.warrants |= activewarrant
|
||||
activewarrant = null
|
||||
|
||||
if(href_list["deletewarrant"])
|
||||
. = 1
|
||||
data_core.warrants -= activewarrant
|
||||
activewarrant = null
|
||||
|
||||
if(href_list["editwarrantname"])
|
||||
. = 1
|
||||
var/namelist = list()
|
||||
for(var/datum/data/record/t in data_core.general)
|
||||
namelist += t.fields["name"]
|
||||
var/new_name = sanitize(input(usr, "Please input name") as null|anything in namelist)
|
||||
if(CanInteract(user, default_state))
|
||||
if (!new_name)
|
||||
return
|
||||
activewarrant.fields["namewarrant"] = new_name
|
||||
|
||||
if(href_list["editwarrantnamecustom"])
|
||||
. = 1
|
||||
var/new_name = sanitize(input("Please input name") as null|text)
|
||||
if(CanInteract(user, default_state))
|
||||
if (!new_name)
|
||||
return
|
||||
activewarrant.fields["namewarrant"] = new_name
|
||||
|
||||
if(href_list["editwarrantcharges"])
|
||||
. = 1
|
||||
var/new_charges = sanitize(input("Please input charges", "Charges", activewarrant.fields["charges"]) as null|text)
|
||||
if(CanInteract(user, default_state))
|
||||
if (!new_charges)
|
||||
return
|
||||
activewarrant.fields["charges"] = new_charges
|
||||
|
||||
if(href_list["editwarrantauth"])
|
||||
. = 1
|
||||
|
||||
activewarrant.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]"
|
||||
|
||||
if(href_list["back"])
|
||||
. = 1
|
||||
activewarrant = null
|
||||
Reference in New Issue
Block a user