From 2733b77b588801323917bf90f7eaab17f9b6f883 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sun, 19 Feb 2017 16:00:09 -0300 Subject: [PATCH 01/22] NTNet and Modular Computers defines. --- code/__DEFINES/machines.dm | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index c95c3bb5b55..23b44679a9d 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -31,4 +31,43 @@ // multitool_topic() shit #define MT_ERROR -1 #define MT_UPDATE 1 -#define MT_REINIT 2 \ No newline at end of file +#define MT_REINIT 2 + +//Modular computer/NTNet defines + +//Modular computer part defines +#define MC_CPU "CPU" +#define MC_HDD "HDD" +#define MC_SDD "SDD" +#define MC_CARD "CARD" +#define MC_NET "NET" +#define MC_PRINT "PRINT" +#define MC_CELL "CELL" +#define MC_CHARGE "CHARGE" +#define MC_AI "AI" + +//NTNet stuff, for modular computers + // NTNet module-configuration values. Do not change these. If you need to add another use larger number (5..6..7 etc) +#define NTNET_SOFTWAREDOWNLOAD 1 // Downloads of software from NTNet +#define NTNET_PEERTOPEER 2 // P2P transfers of files between devices +#define NTNET_COMMUNICATION 3 // Communication (messaging) +#define NTNET_SYSTEMCONTROL 4 // Control of various systems, RCon, air alarm control, etc. + +//NTNet transfer speeds, used when downloading/uploading a file/program. +#define NTNETSPEED_LOWSIGNAL 0.5 // GQ/s transfer speed when the device is wirelessly connected and on Low signal +#define NTNETSPEED_HIGHSIGNAL 1 // GQ/s transfer speed when the device is wirelessly connected and on High signal +#define NTNETSPEED_ETHERNET 2 // GQ/s transfer speed when the device is using wired connection + +//Caps for NTNet logging. Less than 10 would make logging useless anyway, more than 500 may make the log browser too laggy. Defaults to 100 unless user changes it. +#define MAX_NTNET_LOGS 300 +#define MIN_NTNET_LOGS 10 + +//Program bitflags +#define PROGRAM_ALL 7 +#define PROGRAM_CONSOLE 1 +#define PROGRAM_LAPTOP 2 +#define PROGRAM_TABLET 4 +//Program states +#define PROGRAM_STATE_KILLED 0 +#define PROGRAM_STATE_BACKGROUND 1 +#define PROGRAM_STATE_ACTIVE 2 From bfc130dafbebcefab8b2c2699964af6202f6524e Mon Sep 17 00:00:00 2001 From: davipatury Date: Sun, 19 Feb 2017 21:54:46 -0300 Subject: [PATCH 02/22] Base. --- code/__HELPERS/logging.dm | 6 +- code/__HELPERS/text.dm | 8 + code/game/jobs/access.dm | 9 +- code/modules/client/asset_cache.dm | 23 + .../NTNet/NTNRC/conversation.dm | 70 +++ code/modules/modular_computers/NTNet/NTNet.dm | 145 ++++++ .../modular_computers/NTNet/NTNet_relay.dm | 128 +++++ .../computers/item/computer.dm | 442 ++++++++++++++++++ .../computers/item/computer_components.dm | 54 +++ .../computers/item/computer_damage.dm | 52 +++ .../computers/item/computer_power.dm | 61 +++ .../computers/item/computer_ui.dm | 136 ++++++ .../computers/item/laptop.dm | 107 +++++ .../computers/item/laptop_presets.dm | 21 + .../computers/item/processor.dm | 74 +++ .../computers/item/tablet.dm | 12 + .../computers/item/tablet_presets.dm | 29 ++ .../computers/machinery/modular_computer.dm | 160 +++++++ .../modular_computers/documentation.md | 45 ++ .../file_system/computer_file.dm | 39 ++ .../modular_computers/file_system/data.dm | 20 + .../modular_computers/file_system/program.dm | 199 ++++++++ .../file_system/program_events.dm | 18 + .../file_system/programs/antagonist/dos.dm | 102 ++++ .../file_system/programs/configurator.dm | 74 +++ .../file_system/programs/file_browser.dm | 232 +++++++++ .../file_system/programs/ntdownloader.dm | 176 +++++++ .../file_system/programs/ntnrc_client.dm | 229 +++++++++ .../file_system/programs/nttransfer.dm | 194 ++++++++ .../modules/modular_computers/hardware/CPU.dm | 44 ++ .../modular_computers/hardware/_hardware.dm | 110 +++++ .../modular_computers/hardware/ai_slot.dm | 70 +++ .../hardware/battery_module.dm | 99 ++++ .../modular_computers/hardware/card_slot.dm | 105 +++++ .../modular_computers/hardware/hard_drive.dm | 173 +++++++ .../hardware/network_card.dm | 82 ++++ .../hardware/portable_disk.dm | 35 ++ .../modular_computers/hardware/printer.dm | 63 +++ .../modular_computers/hardware/recharger.dm | 93 ++++ icons/obj/modular_console.dmi | Bin 0 -> 26642 bytes icons/obj/modular_laptop.dmi | Bin 0 -> 12499 bytes icons/obj/modular_tablet.dmi | Bin 0 -> 2240 bytes icons/program_icons/alarm_green.gif | Bin 0 -> 107 bytes icons/program_icons/alarm_red.gif | Bin 0 -> 163 bytes icons/program_icons/batt_100.gif | Bin 0 -> 173 bytes icons/program_icons/batt_20.gif | Bin 0 -> 284 bytes icons/program_icons/batt_40.gif | Bin 0 -> 185 bytes icons/program_icons/batt_5.gif | Bin 0 -> 273 bytes icons/program_icons/batt_60.gif | Bin 0 -> 166 bytes icons/program_icons/batt_80.gif | Bin 0 -> 172 bytes icons/program_icons/charging.gif | Bin 0 -> 160 bytes icons/program_icons/downloader_finished.gif | Bin 0 -> 130 bytes icons/program_icons/downloader_running.gif | Bin 0 -> 574 bytes icons/program_icons/ntnrc_idle.gif | Bin 0 -> 113 bytes icons/program_icons/ntnrc_new.gif | Bin 0 -> 181 bytes icons/program_icons/power_norm.gif | Bin 0 -> 431 bytes icons/program_icons/power_warn.gif | Bin 0 -> 485 bytes icons/program_icons/sig_high.gif | Bin 0 -> 182 bytes icons/program_icons/sig_lan.gif | Bin 0 -> 191 bytes icons/program_icons/sig_low.gif | Bin 0 -> 179 bytes icons/program_icons/sig_none.gif | Bin 0 -> 202 bytes nano/templates/computer_main.tmpl | 44 ++ nano/templates/file_manager.tmpl | 106 +++++ nano/templates/laptop_configuration.tmpl | 102 ++++ nano/templates/ntnet_chat.tmpl | 93 ++++ nano/templates/ntnet_dos.tmpl | 52 +++ nano/templates/ntnet_downloader.tmpl | 137 ++++++ nano/templates/ntnet_relay.tmpl | 19 + nano/templates/ntnet_transfer.tmpl | 112 +++++ paradise.dme | 34 ++ 70 files changed, 4434 insertions(+), 4 deletions(-) create mode 100644 code/modules/modular_computers/NTNet/NTNRC/conversation.dm create mode 100644 code/modules/modular_computers/NTNet/NTNet.dm create mode 100644 code/modules/modular_computers/NTNet/NTNet_relay.dm create mode 100644 code/modules/modular_computers/computers/item/computer.dm create mode 100644 code/modules/modular_computers/computers/item/computer_components.dm create mode 100644 code/modules/modular_computers/computers/item/computer_damage.dm create mode 100644 code/modules/modular_computers/computers/item/computer_power.dm create mode 100644 code/modules/modular_computers/computers/item/computer_ui.dm create mode 100644 code/modules/modular_computers/computers/item/laptop.dm create mode 100644 code/modules/modular_computers/computers/item/laptop_presets.dm create mode 100644 code/modules/modular_computers/computers/item/processor.dm create mode 100644 code/modules/modular_computers/computers/item/tablet.dm create mode 100644 code/modules/modular_computers/computers/item/tablet_presets.dm create mode 100644 code/modules/modular_computers/computers/machinery/modular_computer.dm create mode 100644 code/modules/modular_computers/documentation.md create mode 100644 code/modules/modular_computers/file_system/computer_file.dm create mode 100644 code/modules/modular_computers/file_system/data.dm create mode 100644 code/modules/modular_computers/file_system/program.dm create mode 100644 code/modules/modular_computers/file_system/program_events.dm create mode 100644 code/modules/modular_computers/file_system/programs/antagonist/dos.dm create mode 100644 code/modules/modular_computers/file_system/programs/configurator.dm create mode 100644 code/modules/modular_computers/file_system/programs/file_browser.dm create mode 100644 code/modules/modular_computers/file_system/programs/ntdownloader.dm create mode 100644 code/modules/modular_computers/file_system/programs/ntnrc_client.dm create mode 100644 code/modules/modular_computers/file_system/programs/nttransfer.dm create mode 100644 code/modules/modular_computers/hardware/CPU.dm create mode 100644 code/modules/modular_computers/hardware/_hardware.dm create mode 100644 code/modules/modular_computers/hardware/ai_slot.dm create mode 100644 code/modules/modular_computers/hardware/battery_module.dm create mode 100644 code/modules/modular_computers/hardware/card_slot.dm create mode 100644 code/modules/modular_computers/hardware/hard_drive.dm create mode 100644 code/modules/modular_computers/hardware/network_card.dm create mode 100644 code/modules/modular_computers/hardware/portable_disk.dm create mode 100644 code/modules/modular_computers/hardware/printer.dm create mode 100644 code/modules/modular_computers/hardware/recharger.dm create mode 100644 icons/obj/modular_console.dmi create mode 100644 icons/obj/modular_laptop.dmi create mode 100644 icons/obj/modular_tablet.dmi create mode 100644 icons/program_icons/alarm_green.gif create mode 100644 icons/program_icons/alarm_red.gif create mode 100644 icons/program_icons/batt_100.gif create mode 100644 icons/program_icons/batt_20.gif create mode 100644 icons/program_icons/batt_40.gif create mode 100644 icons/program_icons/batt_5.gif create mode 100644 icons/program_icons/batt_60.gif create mode 100644 icons/program_icons/batt_80.gif create mode 100644 icons/program_icons/charging.gif create mode 100644 icons/program_icons/downloader_finished.gif create mode 100644 icons/program_icons/downloader_running.gif create mode 100644 icons/program_icons/ntnrc_idle.gif create mode 100644 icons/program_icons/ntnrc_new.gif create mode 100644 icons/program_icons/power_norm.gif create mode 100644 icons/program_icons/power_warn.gif create mode 100644 icons/program_icons/sig_high.gif create mode 100644 icons/program_icons/sig_lan.gif create mode 100644 icons/program_icons/sig_low.gif create mode 100644 icons/program_icons/sig_none.gif create mode 100644 nano/templates/computer_main.tmpl create mode 100644 nano/templates/file_manager.tmpl create mode 100644 nano/templates/laptop_configuration.tmpl create mode 100644 nano/templates/ntnet_chat.tmpl create mode 100644 nano/templates/ntnet_dos.tmpl create mode 100644 nano/templates/ntnet_downloader.tmpl create mode 100644 nano/templates/ntnet_relay.tmpl create mode 100644 nano/templates/ntnet_transfer.tmpl diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm index 3dd0f9235bd..de827703443 100644 --- a/code/__HELPERS/logging.dm +++ b/code/__HELPERS/logging.dm @@ -78,6 +78,10 @@ if(config.log_pda) diary << "\[[time_stamp()]]PDA: [text][log_end]" +/proc/log_chat(text) + if (config.log_pda) + diary << "\[[time_stamp()]]CHAT: [text]" + /proc/log_misc(text) diary << "\[[time_stamp()]]MISC: [text][log_end]" @@ -98,7 +102,7 @@ /proc/log_after_setup(var/message) if(ticker && ticker.current_state > GAME_STATE_SETTING_UP) to_chat(world, "[message]") - log_to_dd(message) + log_to_dd(message) // Helper procs for building detailed log lines diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 082af09f3f1..adff1623e12 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -64,6 +64,14 @@ /proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN) return copytext((sanitize(strip_html_simple(t))),1,limit) +// Used to get a properly sanitized multiline input, of max_length +/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = input(user, message, title, default) as message|null + if(no_trim) + return copytext(html_encode(name), 1, max_length) + else + return trim(html_encode(name), max_length) + //Runs byond's sanitization proc along-side strip_html_simple //I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause /proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 58d041b9442..e54a4ca7bf2 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -74,6 +74,7 @@ /var/const/access_magistrate = 74 /var/const/access_minisat = 75 /var/const/access_mineral_storeroom = 76 +/var/const/access_network = 77 /var/const/access_weapons = 99 //Weapon authorization for secbots @@ -241,10 +242,10 @@ var/const/access_trade_sol = 160 access_theatre, access_research, access_mining, access_mailsorting, access_heads_vault, access_mining_station, access_xenobiology, access_ce, access_hop, access_hos, access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_paramedic, access_blueshield, access_mechanic,access_weapons, - access_pilot, access_ntrep, access_magistrate, access_mineral_storeroom, access_minisat) + access_pilot, access_ntrep, access_magistrate, access_mineral_storeroom, access_minisat, access_network) /proc/get_all_centcom_access() - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_shuttles, access_cent_telecomms, access_cent_teleporter, access_cent_specops, access_cent_specops_commander, access_cent_blackops, access_cent_thunder, access_cent_bridge, access_cent_commander) + return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_shuttles, access_cent_telecomms, access_cent_teleporter, access_cent_specops, access_cent_specops_commander, access_cent_blackops, access_cent_thunder, access_cent_bridge, access_cent_commander, access_network) /proc/get_all_syndicate_access() return list(access_syndicate, access_syndicate_leader, access_vox) @@ -272,7 +273,7 @@ var/const/access_trade_sol = 160 if(REGION_SUPPLY) //supply return list(access_mailsorting, access_mining, access_mining_station, access_mineral_storeroom, access_cargo, access_qm) if(REGION_COMMAND) //command - return list(access_heads, access_RC_announce, access_keycard_auth, access_change_ids, access_ai_upload, access_teleporter, access_eva, access_tcomsat, access_gateway, access_all_personal_lockers, access_heads_vault, access_blueshield, access_ntrep, access_hop, access_captain) + return list(access_heads, access_RC_announce, access_keycard_auth, access_change_ids, access_ai_upload, access_teleporter, access_eva, access_tcomsat, access_network, access_gateway, access_all_personal_lockers, access_heads_vault, access_blueshield, access_ntrep, access_hop, access_captain) if(REGION_CENTCOMM) //because why the heck not return get_all_centcom_access() + get_all_accesses() @@ -426,6 +427,8 @@ var/const/access_trade_sol = 160 return "Keycode Auth. Device" if(access_tcomsat) return "Telecommunications" + if(access_network) + return "Network" if(access_gateway) return "Gateway" if(access_sec_doors) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index e02a1a8d810..72534169e6d 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -225,6 +225,29 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) "boardui.js" = 'html/browser/boardui.js' ) +/datum/asset/simple/headers + assets = list( + "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', + "alarm_red.gif" = 'icons/program_icons/alarm_red.gif', + "batt_5.gif" = 'icons/program_icons/batt_5.gif', + "batt_20.gif" = 'icons/program_icons/batt_20.gif', + "batt_40.gif" = 'icons/program_icons/batt_40.gif', + "batt_60.gif" = 'icons/program_icons/batt_60.gif', + "batt_80.gif" = 'icons/program_icons/batt_80.gif', + "batt_100.gif" = 'icons/program_icons/batt_100.gif', + "charging.gif" = 'icons/program_icons/charging.gif', + "downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif', + "downloader_running.gif" = 'icons/program_icons/downloader_running.gif', + "ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif', + "ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif', + "power_norm.gif" = 'icons/program_icons/power_norm.gif', + "power_warn.gif" = 'icons/program_icons/power_warn.gif', + "sig_high.gif" = 'icons/program_icons/sig_high.gif', + "sig_low.gif" = 'icons/program_icons/sig_low.gif', + "sig_lan.gif" = 'icons/program_icons/sig_lan.gif', + "sig_none.gif" = 'icons/program_icons/sig_none.gif', + ) + /datum/asset/nanoui var/list/common = list() diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm new file mode 100644 index 00000000000..55998071e71 --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm @@ -0,0 +1,70 @@ +var/global/static/ntnrc_uid = 0 + +/datum/ntnet_conversation + var/id = null + var/title = "Untitled Conversation" + var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator, + var/list/messages = list() + var/list/clients = list() + var/password + +/datum/ntnet_conversation/New() + id = ntnrc_uid + ntnrc_uid++ + if(ntnet_global) + ntnet_global.chat_channels.Add(src) + ..() + +/datum/ntnet_conversation/Destroy() + if(ntnet_global) + ntnet_global.chat_channels.Remove(src) + return ..() + +/datum/ntnet_conversation/proc/add_message(message, username) + message = "[worldtime2text()] [username]: [message]" + messages.Add(message) + trim_message_list() + +/datum/ntnet_conversation/proc/add_status_message(message) + messages.Add("[worldtime2text()] -!- [message]") + trim_message_list() + +/datum/ntnet_conversation/proc/trim_message_list() + if(messages.len <= 50) + return + messages = messages.Copy(messages.len-50 ,0) + +/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/C) + if(!istype(C)) + return + clients.Add(C) + add_status_message("[C.username] has joined the channel.") + // No operator, so we assume the channel was empty. Assign this user as operator. + if(!operator) + changeop(C) + +/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/C) + if(!istype(C) || !(C in clients)) + return + clients.Remove(C) + add_status_message("[C.username] has left the channel.") + + // Channel operator left, pick new operator + if(C == operator) + operator = null + if(clients.len) + var/datum/computer_file/program/chatclient/newop = pick(clients) + changeop(newop) + + +/datum/ntnet_conversation/proc/changeop(datum/computer_file/program/chatclient/newop) + if(istype(newop)) + operator = newop + add_status_message("Channel operator status transferred to [newop.username].") + +/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/client) + if(operator != client) + return 0 // Not Authorised + + add_status_message("[client.username] has changed channel title from [title] to [newtitle]") + title = newtitle diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm new file mode 100644 index 00000000000..ed69207f0ee --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNet.dm @@ -0,0 +1,145 @@ +var/global/datum/ntnet/ntnet_global = new() + + +// This is the NTNet datum. There can be only one NTNet datum in game at once. Modular computers read data from this. +/datum/ntnet + var/list/relays = list() + var/list/logs = list() + var/list/available_station_software = list() + var/list/available_antag_software = list() + var/list/chat_channels = list() + var/list/fileservers = list() + // Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly. + // High values make displaying logs much laggier. + var/setting_maxlogcount = 100 + + // These only affect wireless. LAN (consoles) are unaffected since it would be possible to create scenario where someone turns off NTNet, and is unable to turn it back on since it refuses connections + var/setting_softwaredownload = 1 + var/setting_peertopeer = 1 + var/setting_communication = 1 + var/setting_systemcontrol = 1 + var/setting_disabled = 0 // Setting to 1 will disable all wireless, independently on relays status. + + var/intrusion_detection_enabled = 1 // Whether the IDS warning system is enabled + var/intrusion_detection_alarm = 0 // Set when there is an IDS warning due to malicious (antag) software. + + +// If new NTNet datum is spawned, it replaces the old one. +/datum/ntnet/New() + if(ntnet_global && (ntnet_global != src)) + ntnet_global = src // There can be only one. + for(var/obj/machinery/ntnet_relay/R in machines) + relays.Add(R) + R.NTNet = src + build_software_lists() + add_log("NTNet logging system activated.") + +// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional. +/datum/ntnet/proc/add_log(log_string, obj/item/weapon/computer_hardware/network_card/source = null) + var/log_text = "[worldtime2text()] - " + if(source) + log_text += "[source.get_network_tag()] - " + else + log_text += "*SYSTEM* - " + log_text += log_string + logs.Add(log_text) + + + // We have too many logs, remove the oldest entries until we get into the limit + if(logs.len > setting_maxlogcount) + logs = logs.Copy(logs.len-setting_maxlogcount,0) + + +// Checks whether NTNet operates. If parameter is passed checks whether specific function is enabled. +/datum/ntnet/proc/check_function(specific_action = 0) + if(!relays || !relays.len) // No relays found. NTNet is down + return 0 + + var/operating = 0 + + // Check all relays. If we have at least one working relay, network is up. + for(var/M in relays) + var/obj/machinery/ntnet_relay/R = M + if(R.operable()) + operating = 1 + break + + if(setting_disabled) + return 0 + + switch(specific_action) + if(NTNET_SOFTWAREDOWNLOAD) + return (operating && setting_softwaredownload) + if(NTNET_PEERTOPEER) + return (operating && setting_peertopeer) + if(NTNET_COMMUNICATION) + return (operating && setting_communication) + if(NTNET_SYSTEMCONTROL) + return (operating && setting_systemcontrol) + return operating + +// Builds lists that contain downloadable software. +/datum/ntnet/proc/build_software_lists() + available_station_software = list() + available_antag_software = list() + for(var/F in typesof(/datum/computer_file/program)) + var/datum/computer_file/program/prog = new F + // Invalid type (shouldn't be possible but just in case), invalid filetype (not executable program) or invalid filename (unset program) + if(!prog || prog.filename == "UnknownProgram" || prog.filetype != "PRG") + continue + // Check whether the program should be available for station/antag download, if yes, add it to lists. + if(prog.available_on_ntnet) + available_station_software.Add(prog) + if(prog.available_on_syndinet) + available_antag_software.Add(prog) + +// Attempts to find a downloadable file according to filename var +/datum/ntnet/proc/find_ntnet_file_by_name(filename) + for(var/N in available_station_software) + var/datum/computer_file/program/P = N + if(filename == P.filename) + return P + for(var/N in available_antag_software) + var/datum/computer_file/program/P = N + if(filename == P.filename) + return P + +// Resets the IDS alarm +/datum/ntnet/proc/resetIDS() + intrusion_detection_alarm = 0 + +/datum/ntnet/proc/toggleIDS() + resetIDS() + intrusion_detection_enabled = !intrusion_detection_enabled + +// Removes all logs +/datum/ntnet/proc/purge_logs() + logs = list() + add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-") + +// Updates maximal amount of stored logs. Use this instead of setting the number, it performs required checks. +/datum/ntnet/proc/update_max_log_count(lognumber) + if(!lognumber) + return 0 + // Trim the value if necessary + lognumber = max(MIN_NTNET_LOGS, min(lognumber, MAX_NTNET_LOGS)) + setting_maxlogcount = lognumber + add_log("Configuration Updated. Now keeping [setting_maxlogcount] logs in system memory.") + +/datum/ntnet/proc/toggle_function(function) + if(!function) + return + function = text2num(function) + switch(function) + if(NTNET_SOFTWAREDOWNLOAD) + setting_softwaredownload = !setting_softwaredownload + add_log("Configuration Updated. Wireless network firewall now [setting_softwaredownload ? "allows" : "disallows"] connection to software repositories.") + if(NTNET_PEERTOPEER) + setting_peertopeer = !setting_peertopeer + add_log("Configuration Updated. Wireless network firewall now [setting_peertopeer ? "allows" : "disallows"] peer to peer network traffic.") + if(NTNET_COMMUNICATION) + setting_communication = !setting_communication + add_log("Configuration Updated. Wireless network firewall now [setting_communication ? "allows" : "disallows"] instant messaging and similar communication services.") + if(NTNET_SYSTEMCONTROL) + setting_systemcontrol = !setting_systemcontrol + add_log("Configuration Updated. Wireless network firewall now [setting_systemcontrol ? "allows" : "disallows"] remote control of station's systems.") diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm new file mode 100644 index 00000000000..5f5325e0364 --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -0,0 +1,128 @@ +// Relays don't handle any actual communication. Global NTNet datum does that, relays only tell the datum if it should or shouldn't work. +/obj/machinery/ntnet_relay + name = "NTNet Quantum Relay" + desc = "A very complex router and transmitter capable of connecting electronic devices together. Looks fragile." + use_power = 2 + active_power_usage = 10000 //10kW, apropriate for machine that keeps massive cross-Zlevel wireless network operational. Used to be 20 but that actually drained the smes one round + idle_power_usage = 100 + icon = 'icons/obj/stationobjs.dmi' + icon_state = "bus" + anchored = 1 + density = 1 + var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame. + var/enabled = 1 // Set to 0 if the relay was turned off + var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack + var/list/dos_sources = list() // Backwards reference for qdel() stuff + + // Denial of Service attack variables + var/dos_overload = 0 // Amount of DoS "packets" in this relay's buffer + var/dos_capacity = 500 // Amount of DoS "packets" in buffer required to crash the relay + var/dos_dissipate = 1 // Amount of DoS "packets" dissipated over time. + + +// TODO: Implement more logic here. For now it's only a placeholder. +/obj/machinery/ntnet_relay/inoperable(additional_flags = 0) + if(stat & (BROKEN | NOPOWER | EMPED | additional_flags)) + return 1 + if(dos_failure) + return 1 + if(!enabled) + return 1 + return 0 + +/obj/machinery/ntnet_relay/update_icon() + if(operable()) + icon_state = "bus" + else + icon_state = "bus_off" + +/obj/machinery/ntnet_relay/process() + if(operable()) + use_power = 2 + else + use_power = 1 + + update_icon() + + if(dos_overload) + dos_overload = max(0, dos_overload - dos_dissipate) + + // If DoS traffic exceeded capacity, crash. + if((dos_overload > dos_capacity) && !dos_failure) + dos_failure = 1 + update_icon() + ntnet_global.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") + // If the DoS buffer reaches 0 again, restart. + if((dos_overload == 0) && dos_failure) + dos_failure = 0 + update_icon() + ntnet_global.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") + ..() + +/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "ntnet_relay.tmpl", "NTNet Quantum Relay", 500, 300) + ui.open() + +/obj/machinery/ntnet_relay/ui_data(mob/user) + var/list/data = list() + data["enabled"] = enabled + data["dos_capacity"] = dos_capacity + data["dos_overload"] = dos_overload + data["dos_crashed"] = dos_failure + return data + + +/obj/machinery/ntnet_relay/Topic(href, list/href_list) + if(..()) + return + switch(href_list["action"]) + if("restart") + dos_overload = 0 + dos_failure = 0 + update_icon() + ntnet_global.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") + if("toggle") + enabled = !enabled + ntnet_global.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].") + update_icon() + return 1 + + +/obj/machinery/ntnet_relay/attack_hand(mob/living/user) + ui_interact(user) + +/obj/machinery/ntnet_relay/New() + uid = gl_uid + gl_uid++ + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/machine/ntnet_relay(null) + component_parts += new /obj/item/stack/cable_coil(null, 2) + component_parts += new /obj/item/weapon/stock_parts/subspace/filter(null) + + if(ntnet_global) + ntnet_global.relays.Add(src) + NTNet = ntnet_global + ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") + ..() + +/obj/machinery/ntnet_relay/Destroy() + if(ntnet_global) + ntnet_global.relays.Remove(src) + ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]") + NTNet = null + + for(var/datum/computer_file/program/ntnet_dos/D in dos_sources) + D.target = null + D.error = "Connection to quantum relay severed" + + return ..() + +/obj/item/weapon/circuitboard/machine/ntnet_relay + name = "NTNet Relay (Machine Board)" + build_path = /obj/machinery/ntnet_relay + origin_tech = "programming=3;bluespace=3;magnets=2" + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/weapon/stock_parts/subspace/filter = 1) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm new file mode 100644 index 00000000000..3410cc0d06c --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -0,0 +1,442 @@ +// This is the base type that does all the hardware stuff. +// Other types expand it - tablets use a direct subtypes, and +// consoles and laptops use "procssor" item that is held inside machinery piece +/obj/item/device/modular_computer + name = "modular microcomputer" + desc = "A small portable microcomputer." + + var/enabled = 0 // Whether the computer is turned on. + var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. + var/datum/computer_file/program/active_program = null // A currently active program running on the computer. + var/hardware_flag = 0 // A flag that describes this device type + var/last_power_usage = 0 + var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged + var/last_world_time = "00:00" + var/list/last_header_icons + var/emagged = 0 // Whether the computer is emagged. + + var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too. + var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops) + + // Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..) + // must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently + // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. + + icon = 'icons/obj/computer.dmi' + icon_state = "laptop-open" + var/icon_state_unpowered = null // Icon state when the computer is turned off. + var/icon_state_powered = null // Icon state when the computer is turned on. + var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. + var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4. + var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer. + + var/obj_integrity = 100 + var/integrity_failure = 50 + var/max_integrity = 100 + armor = list(melee = 0, bullet = 20, laser = 20, energy = 100, bomb = 0, bio = 100, rad = 100, fire = 0, acid = 0) + + // Important hardware (must be installed for computer to work) + + // Optional hardware (improves functionality, but is not critical for computer to work) + + var/list/all_components // List of "connection ports" in this computer and the components with which they are plugged + + var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with. + var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks. + + + +/obj/item/device/modular_computer/New() + update_icon() + if(!physical) + physical = src + ..() + processing_objects += src + all_components = list() + idle_threads = list() + +/obj/item/device/modular_computer/Destroy() + kill_program(forced = TRUE) + for(var/H in all_components) + var/obj/item/weapon/computer_hardware/CH = all_components[H] + if(CH.holder == src) + CH.on_remove(src) + CH.holder = null + all_components.Remove(CH.device_type) + qdel(CH) + physical = null + processing_objects -= src + return ..() + + +/obj/item/device/modular_computer/proc/add_verb(var/path) + switch(path) + if(MC_CARD) + verbs += /obj/item/device/modular_computer/proc/eject_id + if(MC_SDD) + verbs += /obj/item/device/modular_computer/proc/eject_disk + if(MC_AI) + verbs += /obj/item/device/modular_computer/proc/eject_card + +/obj/item/device/modular_computer/proc/remove_verb(path) + switch(path) + if(MC_CARD) + verbs -= /obj/item/device/modular_computer/proc/eject_id + if(MC_SDD) + verbs -= /obj/item/device/modular_computer/proc/eject_disk + if(MC_AI) + verbs -= /obj/item/device/modular_computer/proc/eject_card + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/device/modular_computer/proc/eject_id() + set name = "Eject ID" + set category = "Object" + set src in view(1) + + if(issilicon(usr)) + return + var/obj/item/weapon/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + if(!usr.incapacitated()) + card_slot.try_eject(null, usr) + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/device/modular_computer/proc/eject_card() + set name = "Eject Intellicard" + set category = "Object" + set src in view(1) + + if(issilicon(usr)) + return + var/obj/item/weapon/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] + if(!usr.incapacitated()) + ai_slot.try_eject(null, usr, 1) + + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/item/device/modular_computer/proc/eject_disk() + set name = "Eject Data Disk" + set category = "Object" + set src in view(1) + + if(issilicon(usr)) + return + + if(!usr.incapacitated()) + var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] + if(uninstall_component(portable_drive, usr)) + portable_drive.verb_pickup() + +/obj/item/device/modular_computer/AltClick(mob/user) + ..() + if(issilicon(user)) + return + + if(!user.incapacitated() && Adjacent(user)) + var/obj/item/weapon/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + var/obj/item/weapon/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] + var/obj/item/weapon/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] + if(portable_drive) + if(uninstall_component(portable_drive, user)) + portable_drive.verb_pickup() + else + if(card_slot && card_slot.try_eject(null, user)) + return + if(ai_slot) + ai_slot.try_eject(null, user) + + +// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. +/obj/item/device/modular_computer/GetAccess() + var/obj/item/weapon/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + if(card_slot) + return card_slot.GetAccess() + return ..() + +/obj/item/device/modular_computer/GetID() + var/obj/item/weapon/computer_hardware/card_slot/card_slot = all_components[MC_CARD] + if(card_slot) + return card_slot.GetID() + return ..() + +/obj/item/device/modular_computer/attack_ai(mob/user) + return attack_self(user) + +/obj/item/device/modular_computer/attack_ghost(mob/dead/observer/user) + if(enabled) + ui_interact(user) + else if(user.can_admin_interact()) + var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") + if(response == "Yes") + turn_on(user) + +/obj/item/device/modular_computer/emag_act(mob/user) + if(emagged) + to_chat(user, "\The [src] was already emagged.") + return 0 + else + emagged = 1 + to_chat(user, "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message.") + return 1 + +/obj/item/device/modular_computer/examine(mob/user) + ..() + if(obj_integrity <= integrity_failure) + to_chat(user, "It is heavily damaged!") + else if(obj_integrity < max_integrity) + to_chat(user, "It is damaged.") + +/obj/item/device/modular_computer/update_icon() + overlays.Cut() + if(!enabled) + icon_state = icon_state_unpowered + else + icon_state = icon_state_powered + if(active_program) + overlays += active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu + else + overlays += icon_state_menu + + if(obj_integrity <= integrity_failure) + overlays += "bsod" + overlays += "broken" + + +// On-click handling. Turns on the computer if it's off and opens the GUI. +/obj/item/device/modular_computer/attack_self(mob/user) + if(enabled) + ui_interact(user) + else + turn_on(user) + +/obj/item/device/modular_computer/proc/turn_on(mob/user) + var/issynth = issilicon(user) // Robots and AIs get different activation messages. + if(obj_integrity <= integrity_failure) + if(issynth) + to_chat(user, "You send an activation signal to \the [src], but it responds with an error code. It must be damaged.") + else + to_chat(user, "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.") + return + + // If we have a recharger, enable it automatically. Lets computer without a battery work. + var/obj/item/weapon/computer_hardware/recharger/recharger = all_components[MC_CHARGE] + if(recharger) + recharger.enabled = 1 + + if(all_components[MC_CPU] && use_power()) // use_power() checks if the PC is powered + if(issynth) + to_chat(user, "You send an activation signal to \the [src], turning it on.") + else + to_chat(user, "You press the power button and start up \the [src].") + enabled = 1 + update_icon() + ui_interact(user) + else // Unpowered + if(issynth) + to_chat(user, "You send an activation signal to \the [src] but it does not respond.") + else + to_chat(user, "You press the power button but \the [src] does not respond.") + +// Process currently calls handle_power(), may be expanded in future if more things are added. +/obj/item/device/modular_computer/process() + if(!enabled) // The computer is turned off + last_power_usage = 0 + return 0 + + if(obj_integrity <= integrity_failure) + shutdown_computer() + return 0 + + if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) + active_program.event_networkfailure(0) // Active program requires NTNet to run but we've just lost connection. Crash. + + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) + P.event_networkfailure(1) + + if(active_program) + if(active_program.program_state != PROGRAM_STATE_KILLED) + active_program.process_tick() + active_program.ntnet_status = get_ntnet_status() + else + active_program = null + + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(P.program_state != PROGRAM_STATE_KILLED) + P.process_tick() + P.ntnet_status = get_ntnet_status() + else + idle_threads.Remove(P) + + handle_power() // Handles all computer power interaction + //check_update_ui_need() + +// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" +/obj/item/device/modular_computer/proc/get_header_data() + var/list/data = list() + + var/obj/item/weapon/computer_hardware/battery/battery_module = all_components[MC_CELL] + var/obj/item/weapon/computer_hardware/recharger/recharger = all_components[MC_CHARGE] + + if(battery_module && battery_module.battery) + switch(battery_module.battery.percent()) + if(80 to 200) // 100 should be maximal but just in case.. + data["PC_batteryicon"] = "batt_100.gif" + if(60 to 80) + data["PC_batteryicon"] = "batt_80.gif" + if(40 to 60) + data["PC_batteryicon"] = "batt_60.gif" + if(20 to 40) + data["PC_batteryicon"] = "batt_40.gif" + if(5 to 20) + data["PC_batteryicon"] = "batt_20.gif" + else + data["PC_batteryicon"] = "batt_5.gif" + data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %" + data["PC_showbatteryicon"] = 1 + else + data["PC_batteryicon"] = "batt_5.gif" + data["PC_batterypercent"] = "N/C" + data["PC_showbatteryicon"] = battery_module ? 1 : 0 + + if(recharger && recharger.enabled && recharger.check_functionality() && recharger.use_power(0)) + data["PC_apclinkicon"] = "charging.gif" + + switch(get_ntnet_status()) + if(0) + data["PC_ntneticon"] = "sig_none.gif" + if(1) + data["PC_ntneticon"] = "sig_low.gif" + if(2) + data["PC_ntneticon"] = "sig_high.gif" + if(3) + data["PC_ntneticon"] = "sig_lan.gif" + + if(idle_threads.len) + var/list/program_headers = list() + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(!P.ui_header) + continue + program_headers.Add(list(list( + "icon" = P.ui_header + ))) + + data["PC_programheaders"] = program_headers + + data["PC_stationtime"] = worldtime2text() + data["PC_hasheader"] = 1 + data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen + return data + +// Relays kill program request to currently active program. Use this to quit current program. +/obj/item/device/modular_computer/proc/kill_program(forced = FALSE) + if(active_program) + active_program.kill_program(forced) + active_program = null + var/mob/user = usr + if(user && istype(user)) + ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + update_icon() + +// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) +/obj/item/device/modular_computer/proc/get_ntnet_status(specific_action = 0) + var/obj/item/weapon/computer_hardware/network_card/network_card = all_components[MC_NET] + if(network_card) + return network_card.get_signal(specific_action) + else + return 0 + +/obj/item/device/modular_computer/proc/add_log(text) + if(!get_ntnet_status()) + return FALSE + var/obj/item/weapon/computer_hardware/network_card/network_card = all_components[MC_NET] + return ntnet_global.add_log(text, network_card) + +/obj/item/device/modular_computer/proc/shutdown_computer(loud = 1) + kill_program(forced = TRUE) + for(var/datum/computer_file/program/P in idle_threads) + P.kill_program(forced = TRUE) + idle_threads.Remove(P) + if(loud) + physical.visible_message("\The [src] shuts down.") + enabled = 0 + update_icon() + + +/obj/item/device/modular_computer/attackby(obj/item/weapon/W as obj, mob/user as mob) + // Insert items into the components + for(var/h in all_components) + var/obj/item/weapon/computer_hardware/H = all_components[h] + if(H.try_insert(W, user)) + return + + // Insert new hardware + if(istype(W, /obj/item/weapon/computer_hardware)) + if(install_component(W, user)) + return + + if(istype(W, /obj/item/weapon/wrench)) + if(all_components.len) + to_chat(user, "Remove all components from \the [src] before disassembling it.") + return + new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost ) + physical.visible_message("\The [src] has been disassembled by [user].") + relay_qdel() + qdel(src) + return + + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(!WT.isOn()) + to_chat(user, "\The [W] is off.") + return + + if(obj_integrity == max_integrity) + to_chat(user, "\The [src] does not require repairs.") + return + + to_chat(user, "You begin repairing damage to \the [src]...") + var/dmg = round(max_integrity - obj_integrity) + if(WT.remove_fuel(round(dmg/75)) && do_after(usr, dmg/10)) + obj_integrity = max_integrity + to_chat(user, "You repair \the [src].") + return + + if(istype(W, /obj/item/weapon/screwdriver)) + if(!all_components.len) + to_chat(user, "This device doesn't have any components installed.") + return + var/list/component_names = list() + for(var/h in all_components) + var/obj/item/weapon/computer_hardware/H = all_components[h] + component_names.Add(H.name) + + var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names + + if(!choice) + return + + if(!Adjacent(user)) + return + + var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(choice) + + if(!H) + return + + uninstall_component(H, user) + return + + ..() + +// Used by processor to relay qdel() to machinery type. +/obj/item/device/modular_computer/proc/relay_qdel() + return + +// Perform adjacency checks on our physical counterpart, if any. +/obj/item/device/modular_computer/Adjacent(atom/neighbor) + if(physical && physical != src) + return physical.Adjacent(neighbor) + return ..() diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm new file mode 100644 index 00000000000..06cd3989e30 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_components.dm @@ -0,0 +1,54 @@ +/obj/item/device/modular_computer/proc/can_install_component(obj/item/weapon/computer_hardware/H, mob/living/user = null) + if(!H.can_install(src, user)) + return FALSE + + if(H.w_class > max_hardware_size) + user << "This component is too large for \the [src]!" + return FALSE + + if(all_components[H.device_type]) + user << "This computer's hardware slot is already occupied by \the [all_components[H.device_type]]." + return FALSE + return TRUE + + +// Installs component. +/obj/item/device/modular_computer/proc/install_component(obj/item/weapon/computer_hardware/H, mob/living/user = null) + if(!can_install_component(H, user)) + return FALSE + + if(user && !user.unEquip(H)) + return FALSE + H.forceMove(src) + + all_components[H.device_type] = H + + user << "You install \the [H] into \the [src]." + H.holder = src + H.on_install(src, user) + + +// Uninstalls component. +/obj/item/device/modular_computer/proc/uninstall_component(obj/item/weapon/computer_hardware/H, mob/living/user = null) + if(H.holder != src) // Not our component at all. + return FALSE + + all_components.Remove(H.device_type) + + user << "You remove \the [H] from \the [src]." + + H.forceMove(get_turf(src)) + H.holder = null + H.on_remove(src, user) + if(enabled && !use_power()) + shutdown_computer() + update_icon() + + +// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null +/obj/item/device/modular_computer/proc/find_hardware_by_name(name) + for(var/i in all_components) + var/obj/O = all_components[i] + if(O.name == name) + return O + return null diff --git a/code/modules/modular_computers/computers/item/computer_damage.dm b/code/modules/modular_computers/computers/item/computer_damage.dm new file mode 100644 index 00000000000..689e8c99ab7 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_damage.dm @@ -0,0 +1,52 @@ +/obj/item/device/modular_computer/emp_act(severity) + if(prob(20 / severity)) + take_damage(obj_integrity, BURN) + ..() + +/obj/item/device/modular_computer/ex_act(severity) + switch(severity) + if(1) + qdel(src) + if(2) + if(prob(25)) + qdel(src) + else if(prob(50)) + obj_integrity = 0 + if(3) + if(prob(25)) + obj_integrity = 0 + +/obj/item/device/modular_computer/bullet_act(obj/item/projectile/P) + take_damage(P.damage, P.damage_type) + ..() + +/obj/item/device/modular_computer/blob_act() + if(prob(75)) + take_damage(obj_integrity, BRUTE) + +/obj/item/device/modular_computer/proc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) + . = ..() + var/component_probability = min(50, max(damage_amount*0.1, 1 - obj_integrity/max_integrity)) + switch(damage_flag) + if("bullet") + component_probability = damage_amount * 0.5 + if("laser") + component_probability = damage_amount * 0.66 + if(component_probability) + for(var/I in all_components) + var/obj/item/weapon/computer_hardware/H = all_components[I] + if(prob(component_probability)) + H.take_damage(round(damage_amount*0.5)) + +/obj/item/device/modular_computer/proc/break_apart(damage = TRUE) + physical.visible_message("\The [src] breaks apart!") + var/turf/newloc = get_turf(src) + new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2)) + for(var/C in all_components) + var/obj/item/weapon/computer_hardware/H = all_components[C] + uninstall_component(H) + H.forceMove(newloc) + if(damage && prob(25)) + H.take_damage(rand(10, 30)) + relay_qdel() + qdel(src) diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm new file mode 100644 index 00000000000..c5aab99ce57 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -0,0 +1,61 @@ +// Tries to draw power from charger or, if no operational charger is present, from power cell. +/obj/item/device/modular_computer/proc/use_power(amount = 0) + if(check_power_override()) + return TRUE + + var/obj/item/weapon/computer_hardware/recharger/recharger = all_components[MC_CHARGE] + + if(recharger && recharger.check_functionality()) + if(recharger.use_power(amount)) + return TRUE + + var/obj/item/weapon/computer_hardware/battery/battery_module = all_components[MC_CELL] + + if(battery_module && battery_module.battery && battery_module.battery.charge) + var/obj/item/weapon/stock_parts/cell/cell = battery_module.battery + if(cell.use(amount * CELLRATE)) + return TRUE + else // Discharge the cell anyway. + cell.use(min(amount*CELLRATE, cell.charge)) + return FALSE + return FALSE + +/obj/item/device/modular_computer/proc/give_power(amount) + var/obj/item/weapon/computer_hardware/battery/battery_module = all_components[MC_CELL] + if(battery_module && battery_module.battery) + return battery_module.battery.give(amount) + return 0 + + +// Used in following function to reduce copypaste +/obj/item/device/modular_computer/proc/power_failure() + if(enabled) // Shut down the computer + if(active_program) + active_program.event_powerfailure(0) + for(var/I in idle_threads) + var/datum/computer_file/program/PRG = I + PRG.event_powerfailure(1) + shutdown_computer(0) + +// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged +/obj/item/device/modular_computer/proc/handle_power() + var/obj/item/weapon/computer_hardware/recharger/recharger = all_components[MC_CHARGE] + if(recharger) + recharger.process() + + var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage + + for(var/obj/item/weapon/computer_hardware/H in all_components) + if(H.enabled) + power_usage += H.power_usage + + if(use_power(power_usage)) + last_power_usage = power_usage + return TRUE + else + power_failure() + return FALSE + +// Used by child types if they have other power source than battery or recharger +/obj/item/device/modular_computer/proc/check_power_override() + return FALSE diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm new file mode 100644 index 00000000000..d7b5042d3b2 --- /dev/null +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -0,0 +1,136 @@ +// Operates NanoUI +/obj/item/device/modular_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!enabled) + if(ui) + ui.close() + return 0 + if(!use_power()) + if(ui) + ui.close() + return 0 + + // Robots don't really need to see the screen, their wireless connection works as long as computer is on. + if(!screen_on && !issilicon(user)) + if(ui) + ui.close() + return 0 + + // If we have an active program switch to it now. + if(active_program) + if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now. + ui.close() + active_program.ui_interact(user) + return + + // We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it. + // This screen simply lists available programs and user may select them. + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] + if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len) + to_chat(user, "\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning.") + return // No HDD, No HDD files list or no stored files. Something is very broken. + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "computer_main.tmpl", "NTOS Main menu", 400, 500) + ui.set_auto_update(1) + ui.open() + + +/obj/item/device/modular_computer/ui_data(mob/user) + var/list/data = get_header_data() + data["programs"] = list() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] + for(var/datum/computer_file/program/P in hard_drive.stored_files) + var/running = 0 + if(P in idle_threads) + running = 1 + + data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running)) + + return data + + +// Handles user's GUI input +/obj/item/device/modular_computer/Topic(href, list/href_list) + if(..()) + return + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] + switch(href_list["action"]) + if("PC_exit") + kill_program() + return 1 + if("PC_shutdown") + shutdown_computer() + return 1 + if("PC_minimize") + var/mob/user = usr + if(!active_program || !all_components[MC_CPU]) + return + + idle_threads.Add(active_program) + active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs + + active_program = null + update_icon() + if(user && istype(user)) + ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + + if("PC_killprogram") + var/prog = href_list["name"] + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) + + if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) + return + + P.kill_program(forced = TRUE) + user << "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed." + + if("PC_runprogram") + var/prog = href_list["name"] + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) + + if(!P || !istype(P)) // Program not found or it's not executable program. + user << "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning." + return + + P.computer = src + + if(!P.is_supported_by_hardware(hardware_flag, 1, user)) + return + + // The program is already running. Resume it. + if(P in idle_threads) + P.program_state = PROGRAM_STATE_ACTIVE + active_program = P + idle_threads.Remove(P) + update_icon() + return + + var/obj/item/weapon/computer_hardware/processor_unit/PU = all_components[MC_CPU] + + if(idle_threads.len > PU.max_idle_programs) + user << "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error." + return + + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. + user << "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning." + return + if(P.run_program(user)) + active_program = P + update_icon() + return 1 + else + return + +/obj/item/device/modular_computer/nano_host() + if(physical) + return physical + return src diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm new file mode 100644 index 00000000000..911e8ac685c --- /dev/null +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -0,0 +1,107 @@ +/obj/item/device/modular_computer/laptop + name = "laptop" + desc = "A portable laptop computer." + + icon = 'icons/obj/modular_laptop.dmi' + icon_state = "laptop-closed" + icon_state_powered = "laptop" + icon_state_unpowered = "laptop-off" + icon_state_menu = "menu" + + hardware_flag = PROGRAM_LAPTOP + max_hardware_size = 2 + w_class = WEIGHT_CLASS_NORMAL + + flags = HANDSLOW // No running around with open laptops in hands. + + screen_on = 0 // Starts closed + var/start_open = 1 // unless this var is set to 1 + var/icon_state_closed = "laptop-closed" + var/w_class_open = WEIGHT_CLASS_BULKY + var/slowdown_open = 1 + +/obj/item/device/modular_computer/laptop/New() + ..() + if(start_open && !screen_on) + toggle_open() + +/obj/item/device/modular_computer/laptop/update_icon() + if(screen_on) + ..() + else + overlays.Cut() + icon_state = icon_state_closed + +/obj/item/device/modular_computer/laptop/attack_self(mob/user) + if(!screen_on) + try_toggle_open(user) + else + return ..() + +/obj/item/device/modular_computer/laptop/verb/open_computer() + set name = "Toggle Open" + set category = "Object" + set src in view(1) + + try_toggle_open(usr) + +/obj/item/device/modular_computer/laptop/MouseDrop(obj/over_object, src_location, over_location) + if(over_object == usr || over_object == src) + try_toggle_open(usr) + if(ishuman(usr)) + if(!isturf(loc) || !Adjacent(usr)) + return + if(over_object && !usr.incapacitated()) + if(!usr.canUnEquip(src)) + to_chat(usr, "[src] appears stuck on you!") + return + switch(over_object.name) + if("r_hand") + usr.unEquip(src) + usr.put_in_r_hand(src) + if("l_hand") + usr.unEquip(src) + usr.put_in_l_hand(src) + +/obj/item/device/modular_computer/laptop/attack_hand(mob/user) + if(screen_on && isturf(loc)) + return attack_self(user) + + return ..() + + +/obj/item/device/modular_computer/laptop/proc/try_toggle_open(mob/living/user) + if(issilicon(user)) + return + if(!isturf(loc) && !ismob(loc)) // No opening it in backpack. + return + if(user.incapacitated() || !Adjacent(user)) + return + + toggle_open(user) + + +/obj/item/device/modular_computer/laptop/AltClick(mob/user) + if(screen_on) // Close it. + try_toggle_open(user) + else + return ..() + +/obj/item/device/modular_computer/laptop/proc/toggle_open(mob/living/user=null) + if(screen_on) + user << "You close \the [src]." + slowdown = initial(slowdown) + w_class = initial(w_class) + else + user << "You open \the [src]." + slowdown = slowdown_open + w_class = w_class_open + + screen_on = !screen_on + update_icon() + + + +// Laptop frame, starts empty and closed. +/obj/item/device/modular_computer/laptop/buildable + start_open = 0 diff --git a/code/modules/modular_computers/computers/item/laptop_presets.dm b/code/modules/modular_computers/computers/item/laptop_presets.dm new file mode 100644 index 00000000000..31519e70d6b --- /dev/null +++ b/code/modules/modular_computers/computers/item/laptop_presets.dm @@ -0,0 +1,21 @@ +/obj/item/device/modular_computer/laptop/preset/New() + . = ..() + install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + install_component(new /obj/item/weapon/computer_hardware/battery(src, /obj/item/weapon/stock_parts/cell/computer)) + install_component(new /obj/item/weapon/computer_hardware/hard_drive) + install_component(new /obj/item/weapon/computer_hardware/network_card) + install_programs() + + +/obj/item/device/modular_computer/laptop/preset/proc/install_programs() + return + + +/obj/item/device/modular_computer/laptop/preset/civillian + desc = "A low-end laptop often used for personal recreation." + + +/obj/item/device/modular_computer/laptop/preset/civillian/install_programs() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm new file mode 100644 index 00000000000..3c81c43e824 --- /dev/null +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -0,0 +1,74 @@ +// Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code. +/obj/item/device/modular_computer/processor + name = "processing unit" + desc = "You shouldn't see this. If you do, report it." + icon = null + icon_state = null + icon_state_unpowered = null + icon_state_menu = null + hardware_flag = 0 + + var/obj/machinery/modular_computer/machinery_computer = null + +/obj/item/device/modular_computer/processor/Destroy() + . = ..() + if(machinery_computer && (machinery_computer.cpu == src)) + machinery_computer.cpu = null + machinery_computer = null + +/obj/item/device/modular_computer/processor/New(comp) + if(!comp || !istype(comp, /obj/machinery/modular_computer)) + CRASH("Inapropriate type passed to obj/item/device/modular_computer/processor/New()! Aborting.") + return + // Obtain reference to machinery computer + all_components = list() + idle_threads = list() + machinery_computer = comp + machinery_computer.cpu = src + hardware_flag = machinery_computer.hardware_flag + max_hardware_size = machinery_computer.max_hardware_size + steel_sheet_cost = machinery_computer.steel_sheet_cost + obj_integrity = machinery_computer.obj_integrity + max_integrity = machinery_computer.max_integrity + integrity_failure = machinery_computer.integrity_failure + base_active_power_usage = machinery_computer.base_active_power_usage + base_idle_power_usage = machinery_computer.base_idle_power_usage + +/obj/item/device/modular_computer/processor/relay_qdel() + qdel(machinery_computer) + +/obj/item/device/modular_computer/processor/update_icon() + if(machinery_computer) + return machinery_computer.update_icon() + +// This thing is not meant to be used on it's own, get topic data from our machinery owner. +//obj/item/device/modular_computer/processor/canUseTopic(user, state) +// if(!machinery_computer) +// return 0 + +// return machinery_computer.canUseTopic(user, state) + +/obj/item/device/modular_computer/processor/shutdown_computer() + if(!machinery_computer) + return + ..() + machinery_computer.update_icon() + return + +/obj/item/device/modular_computer/processor/add_verb(path) + switch(path) + if(MC_CARD) + machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_id + if(MC_SDD) + machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_disk + if(MC_AI) + machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_card + +/obj/item/device/modular_computer/processor/remove_verb(path) + switch(path) + if(MC_CARD) + machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_id + if(MC_SDD) + machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk + if(MC_AI) + machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm new file mode 100644 index 00000000000..6a2b5674a5b --- /dev/null +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -0,0 +1,12 @@ +/obj/item/device/modular_computer/tablet //Its called tablet for theme of 90ies but actually its a "big smartphone" sized + name = "tablet computer" + icon = 'icons/obj/modular_tablet.dmi' + icon_state = "tablet" + icon_state_unpowered = "tablet" + icon_state_powered = "tablet" + icon_state_menu = "menu" + hardware_flag = PROGRAM_TABLET + max_hardware_size = 1 + w_class = WEIGHT_CLASS_SMALL + steel_sheet_cost = 1 + slot_flags = SLOT_ID | SLOT_BELT \ No newline at end of file diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm new file mode 100644 index 00000000000..6ac9ed4063b --- /dev/null +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -0,0 +1,29 @@ + +// This is literally the worst possible cheap tablet +/obj/item/device/modular_computer/tablet/preset/cheap + desc = "A low-end tablet often seen among low ranked station personnel." + +/obj/item/device/modular_computer/tablet/preset/cheap/New() + . = ..() + install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + install_component(new /obj/item/weapon/computer_hardware/battery(src, /obj/item/weapon/stock_parts/cell/computer/micro)) + install_component(new /obj/item/weapon/computer_hardware/hard_drive/small) + install_component(new /obj/item/weapon/computer_hardware/network_card) + +// Alternative version, an average one, for higher ranked positions mostly +/obj/item/device/modular_computer/tablet/preset/advanced/New() + . = ..() + install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + install_component(new /obj/item/weapon/computer_hardware/battery(src, /obj/item/weapon/stock_parts/cell/computer)) + install_component(new /obj/item/weapon/computer_hardware/hard_drive/small) + install_component(new /obj/item/weapon/computer_hardware/network_card) + install_component(new /obj/item/weapon/computer_hardware/card_slot) + install_component(new /obj/item/weapon/computer_hardware/printer/mini) + +/obj/item/device/modular_computer/tablet/preset/cargo/New() + . = ..() + install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + install_component(new /obj/item/weapon/computer_hardware/battery(src, /obj/item/weapon/stock_parts/cell/computer)) + install_component(new /obj/item/weapon/computer_hardware/hard_drive/small) + install_component(new /obj/item/weapon/computer_hardware/network_card) + install_component(new /obj/item/weapon/computer_hardware/printer/mini) diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm new file mode 100644 index 00000000000..a28184fed7a --- /dev/null +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -0,0 +1,160 @@ +// Global var to track modular computers +var/list/global_modular_computers = list() + +// Modular Computer - device that runs various programs and operates with hardware +// DO NOT SPAWN THIS TYPE. Use /laptop/ or /console/ instead. +/obj/machinery/modular_computer + name = "modular computer" + desc = "An advanced computer." + + use_power = 1 + idle_power_usage = 5 + var/hardware_flag = 0 // A flag that describes this device type + var/last_power_usage = 0 // Power usage during last tick + + // Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..) + // must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently + // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. + + icon = null + icon_state = null + var/icon_state_unpowered = null // Icon state when the computer is turned off. + var/icon_state_powered = null // Icon state when the computer is turned on. + var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. + var/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'. + var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed. + var/steel_sheet_cost = 10 // Amount of steel sheets refunded when disassembling an empty frame of this computer. + var/light_strength = 0 // Light luminosity when turned on + var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too. + var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops) + + var/obj_integrity = 300 + var/integrity_failure = 150 + var/max_integrity = 300 + + var/obj/item/device/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things. + +/obj/machinery/modular_computer/New() + ..() + cpu = new(src) + cpu.physical = src + global_modular_computers.Add(src) + +/obj/machinery/modular_computer/Destroy() + if(cpu) + qdel(cpu) + cpu = null + return ..() + +/obj/machinery/modular_computer/attack_ghost(mob/dead/observer/user) + if(cpu) + cpu.attack_ghost(user) + +/obj/machinery/modular_computer/emag_act(mob/user) + return cpu ? cpu.emag_act(user) : 1 + +/obj/machinery/modular_computer/update_icon() + overlays.Cut() + icon_state = icon_state_powered + + if(!cpu || !cpu.enabled) + if (!(stat & NOPOWER) && (cpu && cpu.use_power())) + overlays += screen_icon_screensaver + else + icon_state = icon_state_unpowered + set_light(0) + else + set_light(light_strength) + if(cpu.active_program) + overlays += cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu + else + overlays += screen_icon_state_menu + + if(cpu && cpu.obj_integrity <= cpu.integrity_failure) + overlays += "bsod" + overlays += "broken" + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/machinery/modular_computer/proc/eject_id() + set name = "Eject ID" + set category = "Object" + + if(cpu) + cpu.eject_id() + +// Eject ID card from computer, if it has ID slot with card inside. +/obj/machinery/modular_computer/proc/eject_disk() + set name = "Eject Data Disk" + set category = "Object" + + if(cpu) + cpu.eject_disk() + +/obj/machinery/modular_computer/proc/eject_card() + set name = "Eject Intellicard" + set category = "Object" + set src in view(1) + + if(cpu) + cpu.eject_card() + +/obj/machinery/modular_computer/AltClick(mob/user) + if(cpu) + cpu.AltClick(user) + +// On-click handling. Turns on the computer if it's off and opens the GUI. +/obj/machinery/modular_computer/attack_hand(mob/user) + if(cpu) + cpu.attack_self(user) // CPU is an item, that's why we route attack_hand to attack_self + +// Process currently calls handle_power(), may be expanded in future if more things are added. +/obj/machinery/modular_computer/process() + if(cpu) + // Keep names in sync. + cpu.name = src.name + cpu.process() + +// Used in following function to reduce copypaste +/obj/machinery/modular_computer/proc/power_failure(malfunction = 0) + var/obj/item/weapon/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL] + if(cpu && cpu.enabled) // Shut down the computer + visible_message("\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.") + if(cpu) + cpu.shutdown_computer(0) + stat |= NOPOWER + update_icon() + + +// Modular computers can have battery in them, we handle power in previous proc, so prevent this from messing it up for us. +/obj/machinery/modular_computer/power_change() + if(cpu && cpu.use_power()) // If MC_CPU still has a power source, PC wouldn't go offline. + stat &= ~NOPOWER + update_icon() + return + ..() + update_icon() + +/obj/machinery/modular_computer/attackby(obj/item/weapon/W, mob/user) + if(cpu) + return cpu.attackby(W, user) + return ..() + + +// Stronger explosions cause serious damage to internal components +// Minor explosions are mostly mitigitated by casing. +/obj/machinery/modular_computer/ex_act(severity) + if(cpu) + cpu.ex_act(severity) + ..() + +// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components +/obj/machinery/modular_computer/emp_act(severity) + if(cpu) + cpu.emp_act(severity) + +// "Stun" weapons can cause minor damage to components (short-circuits?) +// "Burn" damage is equally strong against internal components and exterior casing +// "Brute" damage mostly damages the casing. +/obj/machinery/modular_computer/bullet_act(obj/item/projectile/Proj) + if(cpu) + cpu.bullet_act(Proj) diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md new file mode 100644 index 00000000000..88d8602729d --- /dev/null +++ b/code/modules/modular_computers/documentation.md @@ -0,0 +1,45 @@ + + +#Modular computer programs +Ok. so a quick rundown on how to make a program. This is kind of a shitty documentation, but oh well I was asked to. + + +## Base setup +This is how the base program is setup. the rest is mostly tgui stuff. I'll use the ntnetmonitor as a base + + +```DM +/datum/computer_file/program/ntnetmonitor + filename = "ntmonitor" //This is obviously the name of the file itself. not much to be said + filedesc = "NTNet Diagnostics and Monitoring" // This is sort of the official name. it's what shows up on the main menu + program_icon_state = "comm_monitor" // This is what the screen will look like when the program is active + extended_desc = "This program is a dummy. // This is a sort of a description, visible when looking on the ntnet + size = 12 // size of the program. Big programs need more hard drive space. Don't make it too big though. + requires_ntnet = 1 // If this is set, the program will not run without an ntnet connection, and will close if the connection is lost. Mainly for primarily online programs. + required_access = access_network //This is access required to run the program itself. ONLY SET THIS FOR SUPER SECURE SHIT. This also acts as transfer_access as well. + transfer_access = access_change_ids // This is the access needed to download from ntnet or host on the ptp program. This is what you want to use most of the time. + available_on_ntnet = 1 //If it's available to download on ntnet. pretty self explanatory. + available_on_syndinet = 0 // ditto but on emagged syndie net. Use this for antag programs + usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL + //^^- The comment above sorta explains it. Use this to limit what kind of machines can run the program. For example, comms program should be limited to consoles and laptops. + var/ui_header = "downloader_finished.gif" //This one is kinda cool. If you have the program minimized, this will show up in the header of the computer screen. + //you can even have the program change what the header is based on the situation! see alarm.dm for an example. +``` + +##Preinstalls +Now. for pre-installing stuff. + +Primarily done for consoles, there's an install_programs() proc in the console presets file in the machines folder. + +for example, the command console one. + +```DM +/obj/machinery/modular_computer/console/preset/command/install_programs() + cpu.hard_drive.store_file(new/datum/computer_file/program/chatclient()) + cpu.hard_drive.store_file(new/datum/computer_file/program/card_mod()) +``` +Basically, you want to do cpu.hard_drive.store_file(new/*program path here*()) and put it in the subtype's install_programs(). +Probably pretty self explanatory, but just in case. + +Will probably be expanded when new features come around or I get asked to mention something. + diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm new file mode 100644 index 00000000000..15ae12a2859 --- /dev/null +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -0,0 +1,39 @@ +var/global/file_uid = 0 + +/datum/computer_file + var/filename = "NewFile" // Placeholder. No spacebars + var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case + var/size = 1 // File size in GQ. Integers only! + var/obj/item/weapon/computer_hardware/hard_drive/holder // Holder that contains this file. + var/unsendable = 0 // Whether the file may be sent to someone via NTNet transfer or other means. + var/undeletable = 0 // Whether the file may be deleted. Setting to 1 prevents deletion/renaming/etc. + var/uid // UID of this file + +/datum/computer_file/New() + ..() + uid = file_uid + file_uid++ + +/datum/computer_file/Destroy() + if(!holder) + return ..() + + holder.remove_file(src) + // holder.holder is the computer that has drive installed. If we are Destroy()ing program that's currently running kill it. + if(holder.holder && holder.holder.active_program == src) + holder.holder.kill_program(forced = TRUE) + holder = null + ..() + +// Returns independent copy of this file. +/datum/computer_file/proc/clone(rename = 0) + var/datum/computer_file/temp = new type + temp.unsendable = unsendable + temp.undeletable = undeletable + temp.size = size + if(rename) + temp.filename = filename + "(Copy)" + else + temp.filename = filename + temp.filetype = filetype + return temp \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/data.dm b/code/modules/modular_computers/file_system/data.dm new file mode 100644 index 00000000000..32ef6f53dd1 --- /dev/null +++ b/code/modules/modular_computers/file_system/data.dm @@ -0,0 +1,20 @@ +// /data/ files store data in string format. +// They don't contain other logic for now. +/datum/computer_file/data + var/stored_data = "" // Stored data in string format. + filetype = "DAT" + var/block_size = 250 + var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited. + +/datum/computer_file/data/clone() + var/datum/computer_file/data/temp = ..() + temp.stored_data = stored_data + return temp + +// Calculates file size from amount of characters in saved string +/datum/computer_file/data/proc/calculate_size() + size = max(1, round(length(stored_data) / block_size)) + +/datum/computer_file/data/logfile + filetype = "LOG" + diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm new file mode 100644 index 00000000000..35d067c3ba3 --- /dev/null +++ b/code/modules/modular_computers/file_system/program.dm @@ -0,0 +1,199 @@ +// /program/ files are executable programs that do things. +/datum/computer_file/program + filetype = "PRG" + filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET! + var/required_access = null // List of required accesses to *run* the program. + var/transfer_access = null // List of required access to download or file host the program + var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. + var/obj/item/device/modular_computer/computer // Device that runs this program. + var/filedesc = "Unknown Program" // User-friendly name of this program. + var/extended_desc = "N/A" // Short description of this program's function. + var/program_icon_state = null // Program-specific screen icon state + var/requires_ntnet = 0 // Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes. + var/requires_ntnet_feature = 0 // Optional, if above is set to 1 checks for specific function of NTNet (currently NTNET_SOFTWAREDOWNLOAD, NTNET_PEERTOPEER, NTNET_SYSTEMCONTROL and NTNET_COMMUNICATION) + var/ntnet_status = 1 // NTNet status, updated every tick by computer running this program. Don't use this for checks if NTNet works, computers do that. Use this for calculations, etc. + var/usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL + var/network_destination = null // Optional string that describes what NTNet server/system this program connects to. Used in default logging. + var/available_on_ntnet = 1 // Whether the program can be downloaded from NTNet. Set to 0 to disable. + var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. + var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images! + +/datum/computer_file/program/New(obj/item/device/modular_computer/comp = null) + ..() + if(comp && istype(comp)) + computer = comp + +/datum/computer_file/program/Destroy() + computer = null + . = ..() + +/datum/computer_file/program/clone() + var/datum/computer_file/program/temp = ..() + temp.required_access = required_access + temp.filedesc = filedesc + temp.program_icon_state = program_icon_state + temp.requires_ntnet = requires_ntnet + temp.requires_ntnet_feature = requires_ntnet_feature + temp.usage_flags = usage_flags + return temp + +// Relays icon update to the computer. +/datum/computer_file/program/proc/update_computer_icon() + if(computer) + computer.update_icon() + +// Attempts to create a log in global ntnet datum. Returns 1 on success, 0 on fail. +/datum/computer_file/program/proc/generate_network_log(text) + if(computer) + return computer.add_log(text) + return 0 + +/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null) + if(!(hardware_flag & usage_flags)) + if(loud && computer && user) + user << "\The [computer] flashes an \"Hardware Error - Incompatible software\" warning." + return 0 + return 1 + +/datum/computer_file/program/proc/get_signal(specific_action = 0) + if(computer) + return computer.get_ntnet_status(specific_action) + return 0 + +// Called by Process() on device that runs us, once every tick. +/datum/computer_file/program/proc/process_tick() + return 1 + +// Check if the user can run program. Only humans can operate computer. Automatically called in run_program() +// User has to wear their ID for ID Scan to work. +// Can also be called manually, with optional parameter being access_to_check to scan the user's ID +/datum/computer_file/program/proc/can_run(mob/user, loud = 0, access_to_check, transfer = 0) + // Defaults to required_access + if(!access_to_check) + if(transfer && transfer_access) + access_to_check = transfer_access + else + access_to_check = required_access + if(!access_to_check) // No required_access, allow it. + return 1 + + if(!transfer && computer && computer.emagged) //emags can bypass the execution locks but not the download ones. + return 1 + + if(user.can_admin_interact()) + return 1 + + if(issilicon(user)) + return 1 + + if(ishuman(user)) + var/obj/item/weapon/card/id/D + var/obj/item/weapon/computer_hardware/card_slot/card_slot + if(computer && card_slot) + card_slot = computer.all_components[MC_CARD] + D = card_slot.GetID() + var/mob/living/carbon/human/h = user + var/obj/item/weapon/card/id/I = h.get_idcard() + var/obj/item/weapon/card/id/C = h.get_active_hand() + if(C) + C = C.GetID() + if(!(C && istype(C))) + C = null + + if(!I && !C && !D) + if(loud) + user << "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning." + return 0 + + if(I) + if(access_to_check in I.GetAccess()) + return 1 + else if(C) + if(access_to_check in C.GetAccess()) + return 1 + else if(D) + if(access_to_check in D.GetAccess()) + return 1 + if(loud) + user << "\The [computer] flashes an \"Access Denied\" warning." + return 0 + +// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones +// always include the device here in this proc. This proc basically relays the request to whatever is running the program. +/datum/computer_file/program/proc/get_header_data() + if(computer) + return computer.get_header_data() + return list() + +// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure. +// When implementing new program based device, use this to run the program. +/datum/computer_file/program/proc/run_program(mob/living/user) + if(can_run(user, 1)) + if(requires_ntnet && network_destination) + generate_network_log("Connection opened to [network_destination].") + program_state = PROGRAM_STATE_ACTIVE + return 1 + return 0 + +// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. +/datum/computer_file/program/proc/kill_program(forced = FALSE) + program_state = PROGRAM_STATE_KILLED + if(network_destination) + generate_network_log("Connection to [network_destination] closed.") + return 1 + +// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation. + +/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. + return computer.ui_interact(user) + return 1 + + +// CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: +// Topic calls are automagically forwarded from NanoModule this program contains. +// Calls beginning with "PRG_" are reserved for programs handling. +// Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program) +// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. +/datum/computer_file/program/Topic(href, list/href_list) + if(..()) + return 1 + + var/datum/nanoui/ui = nanomanager.get_open_ui(usr, src, "main") + + if(computer) + switch(href_list["action"]) + if("PC_exit") + computer.kill_program() + ui.close() + return 1 + if("PC_shutdown") + computer.shutdown_computer() + ui.close() + return 1 + if("PC_minimize") + var/mob/user = usr + if(!computer.active_program || !computer.all_components[MC_CPU]) + return + + computer.idle_threads.Add(computer.active_program) + program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs + + computer.active_program = null + computer.update_icon() + ui.close() + + if(user && istype(user)) + computer.ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + + +/datum/computer_file/program/nano_host() + if(computer.physical) + return computer.physical + else + return computer + +/datum/computer_file/program/CanUseTopic(mob/user) + if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. + return STATUS_CLOSE + return ..() diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm new file mode 100644 index 00000000000..279d646cfd7 --- /dev/null +++ b/code/modules/modular_computers/file_system/program_events.dm @@ -0,0 +1,18 @@ +// Events are sent to the program by the computer. +// Always include a parent call when overriding an event. + +// Called when the ID card is removed from computer. ID is removed AFTER this proc. +/datum/computer_file/program/proc/event_idremoved(background, slot) + return + +// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss. +/datum/computer_file/program/proc/event_powerfailure(background) + kill_program(forced = TRUE) + +// Called when the network connectivity fails. Computer does necessary checks and only calls this when requires_ntnet_feature and similar variables are not met. +/datum/computer_file/program/proc/event_networkfailure(background) + kill_program(forced = TRUE) + if(background) + computer.visible_message("\The [computer]'s screen displays an \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error") + else + computer.visible_message("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.") diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm new file mode 100644 index 00000000000..768ca343669 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -0,0 +1,102 @@ +/datum/computer_file/program/ntnet_dos + filename = "ntn_dos" + filedesc = "DoS Traffic Generator" + program_icon_state = "hostile" + 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 + 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 * 10 + if(2) + dos_speed = NTNETSPEED_HIGHSIGNAL * 10 + if(3) + dos_speed = NTNETSPEED_ETHERNET * 10 + if(target && executed) + target.dos_overload += dos_speed + if(target.inoperable()) + target.dos_sources.Remove(src) + target = null + error = "Connection to destination relay lost." + +/datum/computer_file/program/ntnet_dos/kill_program(forced = FALSE) + if(target) + target.dos_sources.Remove(src) + target = null + executed = 0 + ..() + +/datum/computer_file/program/ntnet_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 400, 250) + ui.set_auto_update(1) + ui.open() + + +/datum/computer_file/program/ntnet_dos/Topic(href, list/href_list) + if(..()) + return 1 + switch(href_list["action"]) + if("PRG_target_relay") + for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + if("[R.uid]" == href_list["targid"]) + target = R + return 1 + if("PRG_reset") + if(target) + target.dos_sources.Remove(src) + target = null + executed = 0 + error = "" + return 1 + if("PRG_execute") + if(target) + executed = 1 + target.dos_sources.Add(src) + if(ntnet_global.intrusion_detection_enabled) + var/obj/item/weapon/computer_hardware/network_card/network_card = computer.all_components[MC_NET] + ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") + ntnet_global.intrusion_detection_alarm = 1 + return 1 + +/datum/computer_file/program/ntnet_dos/ui_data(mob/user) + if(!ntnet_global) + return + + var/list/data = get_header_data() + + if(error) + data["error"] = error + else if(target && executed) + data["target"] = 1 + data["speed"] = 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 = target.dos_overload * 100 / target.dos_capacity + data["dos_strings"] = list() + for(var/j, j<10, j++) + var/string = "" + for(var/i, i<20, i++) + string = "[string][prob(percentage)]" + data["dos_strings"] += list(list("nums" = string)) + else + data["relays"] = list() + for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + data["relays"] += list(list("id" = R.uid)) + data["focus"] = target ? target.uid : null + + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm new file mode 100644 index 00000000000..e46b9e3d09c --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/configurator.dm @@ -0,0 +1,74 @@ +// 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" + unsendable = 1 + undeletable = 1 + size = 4 + available_on_ntnet = 0 + requires_ntnet = 0 + var/obj/item/device/modular_computer/movable = null + + +/datum/computer_file/program/computerconfig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "laptop_configuration.tmpl", "NTOS Configuration Utility", 575, 700) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/computerconfig/ui_data(mob/user) + movable = computer + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = movable.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/battery/battery_module = movable.all_components[MC_CELL] + if(!istype(movable)) + movable = null + + // No computer connection, we can't get data from that. + if(!movable) + return 0 + + var/list/data = get_header_data() + + data["disk_size"] = hard_drive.max_capacity + data["disk_used"] = hard_drive.used_capacity + data["power_usage"] = movable.last_power_usage + data["battery_exists"] = battery_module ? 1 : 0 + if(battery_module && battery_module.battery) + data["battery_rating"] = battery_module.battery.maxcharge + data["battery_percent"] = round(battery_module.battery.percent()) + + if(battery_module && battery_module.battery) + data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge)) + + var/list/all_entries[0] + for(var/I in movable.all_components) + var/obj/item/weapon/computer_hardware/H = movable.all_components[I] + all_entries.Add(list(list( + "name" = H.name, + "desc" = H.desc, + "enabled" = H.enabled, + "critical" = H.critical, + "powerusage" = H.power_usage + ))) + + data["hardware"] = all_entries + return data + + +/datum/computer_file/program/computerconfig/Topic(href, list/href_list) + if(..()) + return + switch(href_list["action"]) + if("PC_toggle_component") + var/obj/item/weapon/computer_hardware/H = movable.find_hardware_by_name(href_list["name"]) + if(H && istype(H)) + H.enabled = !H.enabled + . = TRUE diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/file_browser.dm new file mode 100644 index 00000000000..2f19757f2f4 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/file_browser.dm @@ -0,0 +1,232 @@ +/datum/computer_file/program/filemanager + filename = "filemanager" + filedesc = "NTOS File Manager" + extended_desc = "This program allows management of files." + program_icon_state = "generic" + size = 8 + requires_ntnet = 0 + available_on_ntnet = 0 + undeletable = 1 + var/open_file + var/error + +/datum/computer_file/program/filemanager/Topic(href, list/href_list) + if(..()) + return 1 + + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.all_components[MC_SDD] + var/obj/item/weapon/computer_hardware/printer/printer = computer.all_components[MC_PRINT] + + switch(href_list["action"]) + if("PRG_openfile") + . = 1 + open_file = href_list["name"] + if("PRG_newtextfile") + . = 1 + var/newname = stripped_input(usr, "Enter file name or leave blank to cancel:", "File rename", max_length=50) + if(!newname) + return 1 + 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("PRG_deletefile") + . = 1 + if(!HDD) + return 1 + var/datum/computer_file/file = HDD.find_file_by_name(href_list["name"]) + if(!file || file.undeletable) + return 1 + HDD.remove_file(file) + if("PRG_usbdeletefile") + . = 1 + if(!RHDD) + return 1 + var/datum/computer_file/file = RHDD.find_file_by_name(href_list["name"]) + if(!file || file.undeletable) + return 1 + RHDD.remove_file(file) + if("PRG_closefile") + . = 1 + open_file = null + error = null + if("PRG_clone") + . = 1 + if(!HDD) + return 1 + var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) + if(!F || !istype(F)) + return 1 + var/datum/computer_file/C = F.clone(1) + HDD.store_file(C) + if("PRG_rename") + . = 1 + if(!HDD) + return 1 + var/datum/computer_file/file = HDD.find_file_by_name(href_list["name"]) + if(!file || !istype(file)) + return 1 + var/newname = stripped_input(usr, "Enter new file name:", "File rename", file.filename, max_length=50) + if(file && newname) + file.filename = newname + if("PRG_edit") + . = 1 + if(!open_file) + return 1 + 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 + // 16384 is the limit for file length in characters. Currently, papers have value of 2048 so this is 8 times as long, since we can't edit parts of the file independently. + var/newtext = stripped_multiline_input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", html_decode(F.stored_data), 16384, TRUE) + 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:

[F.stored_data]

" + HDD.store_file(backup) + if("PRG_printfile") + . = 1 + if(!open_file) + return 1 + if(!HDD) + return 1 + var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) + if(!F || !istype(F)) + return 1 + if(!printer) + error = "Missing Hardware: Your computer does not have required hardware to complete this operation." + return 1 + if(!printer.print_text("" + prepare_printjob(F.stored_data) + "", open_file)) + error = "Hardware error: Printer was unable to print the file. It may be out of paper." + return 1 + if("PRG_copytousb") + . = 1 + if(!HDD || !RHDD) + return 1 + var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) + if(!F || !istype(F)) + return 1 + var/datum/computer_file/C = F.clone(0) + RHDD.store_file(C) + if("PRG_copyfromusb") + . = 1 + if(!HDD || !RHDD) + return 1 + var/datum/computer_file/F = RHDD.find_file_by_name(href_list["name"]) + if(!F || !istype(F)) + return 1 + var/datum/computer_file/C = F.clone(0) + HDD.store_file(C) + +/datum/computer_file/program/filemanager/proc/parse_tags(t) + t = replacetext(t, "\[center\]", "
") + t = replacetext(t, "\[/center\]", "
") + t = replacetext(t, "\[br\]", "
") + t = replacetext(t, "\n", "
") + t = replacetext(t, "\[b\]", "") + t = replacetext(t, "\[/b\]", "") + t = replacetext(t, "\[i\]", "") + t = replacetext(t, "\[/i\]", "") + t = replacetext(t, "\[u\]", "") + t = replacetext(t, "\[/u\]", "") + t = replacetext(t, "\[time\]", "[worldtime2text()]") + t = replacetext(t, "\[date\]", "[current_date_string] [worldtime2text()]") + t = replacetext(t, "\[large\]", "") + t = replacetext(t, "\[/large\]", "") + t = replacetext(t, "\[h1\]", "

") + t = replacetext(t, "\[/h1\]", "

") + t = replacetext(t, "\[h2\]", "

") + t = replacetext(t, "\[/h2\]", "

") + t = replacetext(t, "\[h3\]", "

") + t = replacetext(t, "\[/h3\]", "

") + t = replacetext(t, "\[*\]", "
  • ") + t = replacetext(t, "\[hr\]", "
    ") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + t = replacetext(t, "\[list\]", "
      ") + t = replacetext(t, "\[/list\]", "
    ") + t = replacetext(t, "\[table\]", "") + t = replacetext(t, "\[/table\]", "
    ") + t = replacetext(t, "\[grid\]", "") + t = replacetext(t, "\[/grid\]", "
    ") + t = replacetext(t, "\[row\]", "") + t = replacetext(t, "\[tr\]", "") + t = replacetext(t, "\[td\]", "") + t = replacetext(t, "\[cell\]", "") + t = replacetext(t, "\[tab\]", "    ") + return t + +/datum/computer_file/program/filemanager/proc/prepare_printjob(t) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. + t = replacetext(t, "\[field\]", "") + t = replacetext(t, "\[sign\]", "") + t = parse_tags(t) + return t + +/datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/filemanager/ui_data(mob/user) + var/list/data = get_header_data() + + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.all_components[MC_SDD] + if(error) + data["error"] = error + if(open_file) + var/datum/computer_file/data/file + + if(!computer || !HDD) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + file = HDD.find_file_by_name(open_file) + if(!istype(file)) + data["error"] = "I/O ERROR: Unable to open file." + else + data["filedata"] = parse_tags(file.stored_data) + data["filename"] = "[file.filename].[file.filetype]" + else + if(!computer || !HDD) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + 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 + + return data diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm new file mode 100644 index 00000000000..2a35dd7b8d8 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -0,0 +1,176 @@ +/datum/computer_file/program/ntnetdownload + filename = "ntndownloader" + filedesc = "NTNet Software Download Tool" + program_icon_state = "generic" + 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 + 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/obj/item/device/modular_computer/my_computer = null + +/datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename) + if(downloaded_file) + return 0 + + 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 + + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + + if(!computer || !hard_drive || !hard_drive.can_store_file(PRG)) + 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/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]"].") + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + if(!computer || !hard_drive || !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() + // 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, list/href_list) + if(..()) + return 1 + switch(href_list["action"]) + if("PRG_downloadfile") + if(!downloaded_file) + begin_file_download(href_list["filename"]) + return 1 + if("PRG_reseterror") + if(downloaderror) + download_completion = 0 + download_netspeed = 0 + downloaded_file = null + downloaderror = "" + return 1 + return 0 + +/datum/computer_file/program/ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/ntnetdownload/ui_data(mob/user) + my_computer = computer + + if(!istype(my_computer)) + return + + var/list/data = get_header_data() + + // This IF cuts on data transferred to client, so i guess it's worth it. + if(downloaderror) // Download errored. Wait until user resets the program. + data["error"] = downloaderror + else if(downloaded_file) // Download running. Wait please.. + data["downloadname"] = downloaded_file.filename + data["downloaddesc"] = downloaded_file.filedesc + data["downloadsize"] = downloaded_file.size + data["downloadspeed"] = download_netspeed + data["downloadcompletion"] = round(download_completion, 0.1) + else // No download running, pick file. + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD] + data["disk_size"] = hard_drive.max_capacity + data["disk_used"] = hard_drive.used_capacity + var/list/all_entries[0] + for(var/A in ntnet_global.available_station_software) + var/datum/computer_file/program/P = A + // Only those programs our user can run will show in the list + if(!P.can_run(user,transfer = 1)) + continue + all_entries.Add(list(list( + "filename" = P.filename, + "filedesc" = P.filedesc, + "fileinfo" = P.extended_desc, + "compatibility" = check_compatibility(P), + "size" = P.size + ))) + data["hackedavailable"] = 0 + if(computer.emagged) // If we are running on emagged computer we have access to some "bonus" software + var/list/hacked_programs[0] + for(var/S in ntnet_global.available_antag_software) + var/datum/computer_file/program/P = S + data["hackedavailable"] = 1 + hacked_programs.Add(list(list( + "filename" = P.filename, + "filedesc" = P.filedesc, + "fileinfo" = P.extended_desc, + "size" = P.size + ))) + data["hacked_programs"] = hacked_programs + + data["downloadable_programs"] = all_entries + + return data + +/datum/computer_file/program/ntnetdownload/proc/check_compatibility(datum/computer_file/program/P) + var/hardflag = computer.hardware_flag + + if(P && P.is_supported_by_hardware(hardflag,0)) + return "Compatible" + return "Incompatible!" + +/datum/computer_file/program/ntnetdownload/kill_program(forced) + abort_file_download() + return ..(forced) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm new file mode 100644 index 00000000000..aac18a2f53b --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -0,0 +1,229 @@ +/datum/computer_file/program/chatclient + filename = "ntnrc_client" + filedesc = "NTNet Relay Chat Client" + program_icon_state = "command" + 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 + 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, list/href_list) + if(..()) + return 1 + + switch(href_list["action"]) + if("PRG_speak") + . = 1 + if(!channel) + return 1 + var/mob/living/user = usr + var/message = reject_bad_text(input(user, "Enter message or leave blank to cancel: ")) + if(!message || !channel) + return + channel.add_message(message, username) + log_chat("[user]/([user.ckey]) as [username] sent to [channel.title]: [message]") + + if("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["id"])) + 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 = reject_bad_text(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("PRG_leavechannel") + . = 1 + if(channel) + channel.remove_client(src) + channel = null + if("PRG_newchannel") + . = 1 + var/mob/living/user = usr + var/channel_title = reject_bad_text(input(user,"Enter channel name or leave blank to cancel:")) + 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("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(user, 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("PRG_changename") + . = 1 + var/mob/living/user = usr + var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:")) + if(!newname) + return 1 + if(channel) + channel.add_status_message("[username] is now known as [newname].") + username = newname + + if("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() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + if(!computer || !hard_drive || !hard_drive.store_file(logfile)) + if(!computer) + // This program shouldn't even be runnable without computer. + CRASH("Var computer is null!") + return 1 + if(!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("PRG_renamechannel") + . = 1 + if(!operator_mode || !channel) + return 1 + var/mob/living/user = usr + var/newname = reject_bad_text(input(user, "Enter new channel name or leave blank to cancel:")) + if(!newname || !channel) + return + channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") + channel.title = newname + if("PRG_deletechannel") + . = 1 + if(channel && ((channel.operator == src) || netadmin_mode)) + qdel(channel) + channel = null + if("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(forced = FALSE) + if(channel) + channel.remove_client(src) + channel = null + ..() + +/datum/computer_file/program/chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700) + ui.set_auto_update(1) + ui.open() + + +/datum/computer_file/program/chatclient/ui_data(mob/user) + if(!ntnet_global || !ntnet_global.chat_channels) + return + + var/list/data = get_header_data() + + data["adminmode"] = netadmin_mode + if(channel) + data["title"] = channel.title + var/list/messages[0] + for(var/M in channel.messages) + messages.Add(list(list( + "msg" = M + ))) + data["messages"] = messages + var/list/clients[0] + for(var/C in channel.clients) + var/datum/computer_file/program/chatclient/cl = C + clients.Add(list(list( + "name" = cl.username + ))) + data["clients"] = clients + operator_mode = (channel.operator == src) ? 1 : 0 + data["is_operator"] = operator_mode || netadmin_mode + + else // Channel selection screen + var/list/all_channels[0] + for(var/C in ntnet_global.chat_channels) + var/datum/ntnet_conversation/conv = C + if(conv && conv.title) + all_channels.Add(list(list( + "chan" = conv.title, + "id" = conv.id + ))) + data["all_channels"] = all_channels + + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/nttransfer.dm b/code/modules/modular_computers/file_system/programs/nttransfer.dm new file mode 100644 index 00000000000..f647675c078 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/nttransfer.dm @@ -0,0 +1,194 @@ +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" + size = 7 + requires_ntnet = 1 + requires_ntnet_feature = NTNET_PEERTOPEER + network_destination = "other device via P2P tunnel" + available_on_ntnet = 1 + + 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/download_netspeed = 0 // Our connectivity speed in GQ/s + 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 + update_netspeed() + 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.download_netspeed, download_netspeed) + 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(forced = FALSE) + 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) + +/datum/computer_file/program/nttransfer/proc/update_netspeed() + download_netspeed = 0 + switch(ntnet_status) + if(1) + download_netspeed = NTNETSPEED_LOWSIGNAL + if(2) + download_netspeed = NTNETSPEED_HIGHSIGNAL + if(3) + download_netspeed = NTNETSPEED_ETHERNET + +// Finishes download and attempts to store the file on HDD +/datum/computer_file/program/nttransfer/proc/finish_download() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + if(!computer || !hard_drive || !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 occurred 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/computer_file/program/nttransfer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ntnet_transfer.tmpl", "NTNet P2P Transfer Client", 575, 700) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/nttransfer/Topic(href, list/href_list) + if(..()) + return 1 + switch(href_list["action"]) + if("PRG_downloadfile") + for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) + if("[P.unique_token]" == href_list["id"]) + remote = P + break + if(!remote || !remote.provided_file) + return + if(remote.server_password) + var/pass = reject_bad_text(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("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("PRG_setpassword") + var/pass = reject_bad_text(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("PRG_uploadfile") + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + for(var/datum/computer_file/F in hard_drive.stored_files) + if("[F.uid]" == href_list["id"]) + if(F.unsendable) + error = "I/O Error: File locked." + return + if(istype(F, /datum/computer_file/program)) + var/datum/computer_file/program/P = F + if(!P.can_run(usr,transfer = 1)) + error = "Access Error: Insufficient rights to upload file." + provided_file = F + ntnet_global.fileservers.Add(src) + return + error = "I/O Error: Unable to locate file on hard drive." + return 1 + if("PRG_uploadmenu") + upload_menu = 1 + + +/datum/computer_file/program/nttransfer/ui_data(mob/user) + + var/list/data = get_header_data() + + if(error) + data["error"] = error + else if(downloaded_file) + data["downloading"] = 1 + data["download_size"] = downloaded_file.size + data["download_progress"] = download_completion + data["download_netspeed"] = actual_netspeed + data["download_name"] = "[downloaded_file.filename].[downloaded_file.filetype]" + else if (provided_file) + data["uploading"] = 1 + data["upload_uid"] = unique_token + data["upload_clients"] = connected_clients.len + data["upload_haspassword"] = server_password ? 1 : 0 + data["upload_filename"] = "[provided_file.filename].[provided_file.filetype]" + else if (upload_menu) + var/list/all_files[0] + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + for(var/datum/computer_file/F in 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) + 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 + + return data \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/CPU.dm b/code/modules/modular_computers/hardware/CPU.dm new file mode 100644 index 00000000000..614ad35da9d --- /dev/null +++ b/code/modules/modular_computers/hardware/CPU.dm @@ -0,0 +1,44 @@ +// CPU that allows the computer to run programs. +// Better CPUs are obtainable via research and can run more programs on background. + +/obj/item/weapon/computer_hardware/processor_unit + name = "processor board" + desc = "A standard CPU board used in most computers. It can run up to three programs simultaneously." + icon_state = "cpuboard" + w_class = WEIGHT_CLASS_SMALL + power_usage = 50 + critical = 1 + malfunction_probability = 1 + origin_tech = "programming=3;engineering=2" + var/max_idle_programs = 2 // 2 idle, + 1 active = 3 as said in description. + device_type = MC_CPU + +/obj/item/weapon/computer_hardware/processor_unit/on_remove(obj/item/device/modular_computer/MC, mob/user) + MC.shutdown_computer() + +/obj/item/weapon/computer_hardware/processor_unit/small + name = "microprocessor" + desc = "A miniaturised CPU used in portable devices. It can run up to two programs simultaneously." + icon_state = "cpu" + w_class = WEIGHT_CLASS_TINY + power_usage = 25 + max_idle_programs = 1 + origin_tech = "programming=2;engineering=2" + +/obj/item/weapon/computer_hardware/processor_unit/photonic + name = "photonic processor board" + desc = "An advanced experimental CPU board that uses photonic core instead of regular circuitry. It can run up to five programs simultaneously, but uses a lot of power." + icon_state = "cpuboard_super" + w_class = WEIGHT_CLASS_SMALL + power_usage = 250 + max_idle_programs = 4 + origin_tech = "programming=5;engineering=4" + +/obj/item/weapon/computer_hardware/processor_unit/photonic/small + name = "photonic microprocessor" + desc = "An advanced miniaturised CPU for use in portable devices. It uses photonic core instead of regular circuitry. It can run up to three programs simultaneously." + icon_state = "cpu_super" + w_class = WEIGHT_CLASS_TINY + power_usage = 75 + max_idle_programs = 2 + origin_tech = "programming=4;engineering=3" \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm new file mode 100644 index 00000000000..e00c10e0135 --- /dev/null +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -0,0 +1,110 @@ +/obj/item/weapon/computer_hardware + name = "hardware" + desc = "Unknown Hardware." + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + + w_class = WEIGHT_CLASS_TINY // w_class limits which devices can contain this component. + // 1: PDAs/Tablets, 2: Laptops, 3-4: Consoles only + var/obj/item/device/modular_computer/holder = null + // Computer that holds this hardware, if any. + + var/obj_integrity = 200 + var/max_integrity = 200 + + var/power_usage = 0 // If the hardware uses extra power, change this. + var/enabled = 1 // If the hardware is turned off set this to 0. + var/critical = 0 // Prevent disabling for important component, like the CPU. + var/can_install = 1 // Prevents direct installation of removable media. + var/damage = 0 // Current damage level + var/max_damage = 100 // Maximal damage level. + var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things + var/damage_failure = 50 // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all. + var/malfunction_probability = 10// Chance of malfunction when the component is damaged + var/device_type + +/obj/item/weapon/computer_hardware/New(var/obj/L) + ..() + pixel_x = rand(-8, 8) + pixel_y = rand(-8, 8) + +/obj/item/weapon/computer_hardware/Destroy() + if(holder) + holder.uninstall_component(src) + return ..() + + +/obj/item/weapon/computer_hardware/attackby(obj/item/I, mob/living/user) + // Multitool. Runs diagnostics + if(istype(I, /obj/item/device/multitool)) + user << "***** DIAGNOSTICS REPORT *****" + diagnostics(user) + user << "******************************" + return 1 + + // Cable coil. Works as repair method, but will probably require multiple applications and more cable. + if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/S = I + if(obj_integrity == max_integrity) + user << "\The [src] doesn't seem to require repairs." + return 1 + if(S.use(1)) + user << "You patch up \the [src] with a bit of \the [I]." + obj_integrity = min(obj_integrity + 10, max_integrity) + return 1 + + if(try_insert(I, user)) + return 1 + + return ..() + +// Called on multitool click, prints diagnostic information to the user. +/obj/item/weapon/computer_hardware/proc/diagnostics(var/mob/user) + user << "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]" + +// Handles damage checks +/obj/item/weapon/computer_hardware/proc/check_functionality() + if(!enabled) // Disabled. + return FALSE + + if(damage > damage_failure) // Too damaged to work at all. + return FALSE + + if(damage > damage_malfunction) // Still working. Well, sometimes... + if(prob(malfunction_probability)) + return FALSE + + return TRUE // Good to go. + +// Handles damage checks +/obj/item/weapon/computer_hardware/proc/take_damage(damage_amount) + obj_integrity = max(obj_integrity - damage_amount, 0) + +/obj/item/weapon/computer_hardware/examine(var/mob/user) + . = ..() + if(damage > damage_failure) + user << "It seems to be severely damaged!" + else if(damage > damage_malfunction) + user << "It seems to be damaged!" + else if(damage) + user << "It seems to be slightly damaged." + +// Component-side compatibility check. +/obj/item/weapon/computer_hardware/proc/can_install(obj/item/device/modular_computer/M, mob/living/user = null) + return can_install + +// Called when component is installed into PC. +/obj/item/weapon/computer_hardware/proc/on_install(obj/item/device/modular_computer/M, mob/living/user = null) + return + +// Called when component is removed from PC. +/obj/item/weapon/computer_hardware/proc/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) + try_eject(forced = 1) + +// Called when someone tries to insert something in it - paper in printer, card in card reader, etc. +/obj/item/weapon/computer_hardware/proc/try_insert(obj/item/I, mob/living/user = null) + return FALSE + +// Called when someone tries to eject something from it - card from card reader, etc. +/obj/item/weapon/computer_hardware/proc/try_eject(slot=0, mob/living/user = null, forced = 0) + return FALSE diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm new file mode 100644 index 00000000000..e08517c8e48 --- /dev/null +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -0,0 +1,70 @@ +/obj/item/weapon/computer_hardware/ai_slot + name = "intelliCard interface slot" + desc = "A module allowing this computer to interface with most common intelliCard modules. Necessary for some programs to run properly." + power_usage = 100 //W + icon_state = "card_mini" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "programming=2" + device_type = MC_AI + + var/obj/item/device/aicard/stored_card = null + var/locked = FALSE + + +/obj/item/weapon/computer_hardware/ai_slot/examine(mob/user) + ..() + if(stored_card) + user << "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it" + +/obj/item/weapon/computer_hardware/ai_slot/on_install(obj/item/device/modular_computer/M, mob/living/user = null) + M.add_verb(device_type) + +/obj/item/weapon/computer_hardware/ai_slot/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) + M.remove_verb(device_type) + +/obj/item/weapon/computer_hardware/ai_slot/try_insert(obj/item/I, mob/living/user = null) + if(!holder) + return FALSE + + if(!istype(I, /obj/item/device/aicard)) + return FALSE + + if(stored_card) + user << "You try to insert \the [I] into \the [src], but the slot is occupied." + return FALSE + if(user && !user.unEquip(I)) + return FALSE + + I.forceMove(src) + stored_card = I + user << "You insert \the [I] into \the [src]." + + return TRUE + + +/obj/item/weapon/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0) + if(!stored_card) + user << "There is no card in \the [src]." + return FALSE + + if(locked && !forced) + user << "Safeties prevent you from removing the card until reconstruction is complete..." + return FALSE + + if(stored_card) + stored_card.forceMove(get_turf(src)) + locked = FALSE + stored_card.verb_pickup() + stored_card = null + + user << "You remove the card from \the [src]." + return TRUE + return FALSE + +/obj/item/weapon/computer_hardware/ai_slot/attackby(obj/item/I, mob/living/user) + if(..()) + return + if(istype(I, /obj/item/weapon/screwdriver)) + user << "You press down on the manual eject button with \the [I]." + try_eject(,user,1) + return \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm new file mode 100644 index 00000000000..1edb2c4df7d --- /dev/null +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -0,0 +1,99 @@ +/obj/item/weapon/computer_hardware/battery + name = "power cell controller" + desc = "A charge controller for standard power cells, used in all kinds of modular computers." + icon_state = "cell_con" + critical = 1 + malfunction_probability = 1 + origin_tech = "powerstorage=1;engineering=1" + var/obj/item/weapon/stock_parts/cell/battery = null + device_type = MC_CELL + +/obj/item/weapon/computer_hardware/battery/New(loc, battery_type = null) + if(battery_type) + battery = new battery_type(src) + ..() + +/obj/item/weapon/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null) + if(!holder) + return FALSE + + if(!istype(I, /obj/item/weapon/stock_parts/cell)) + return FALSE + + if(battery) + user << "You try to connect \the [I] to \the [src], but its connectors are occupied." + return FALSE + + if(I.w_class > holder.max_hardware_size) + user << "This power cell is too large for \the [holder]!" + return FALSE + + if(user && !user.unEquip(I)) + return FALSE + + I.forceMove(src) + battery = I + user << "You connect \the [I] to \the [src]." + + return TRUE + + +/obj/item/weapon/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0) + if(!battery) + user << "There is no power cell connected to \the [src]." + return FALSE + else + battery.forceMove(get_turf(src)) + user << "You detach \the [battery] from \the [src]." + battery = null + + if(holder) + if(holder.enabled && !holder.use_power()) + holder.shutdown_computer() + + return TRUE + return FALSE + + + + + + + +/obj/item/weapon/stock_parts/cell/computer + name = "standard battery" + desc = "A standard power cell, commonly seen in high-end portable microcomputers or low-end laptops." + icon = 'icons/obj/module.dmi' + icon_state = "cell_mini" + origin_tech = "powerstorage=2;engineering=1" + w_class = WEIGHT_CLASS_TINY + maxcharge = 750 + + +/obj/item/weapon/stock_parts/cell/computer/advanced + name = "advanced battery" + desc = "An advanced power cell, often used in most laptops. It is too large to be fitted into smaller devices." + icon_state = "cell" + origin_tech = "powerstorage=2;engineering=2" + w_class = WEIGHT_CLASS_SMALL + maxcharge = 1500 + +/obj/item/weapon/stock_parts/cell/computer/super + name = "super battery" + desc = "An advanced power cell, often used in high-end laptops." + icon_state = "cell" + origin_tech = "powerstorage=3;engineering=3" + w_class = WEIGHT_CLASS_SMALL + maxcharge = 2000 + +/obj/item/weapon/stock_parts/cell/computer/micro + name = "micro battery" + desc = "A small power cell, commonly seen in most portable microcomputers." + icon_state = "cell_micro" + maxcharge = 500 + +/obj/item/weapon/stock_parts/cell/computer/nano + name = "nano battery" + desc = "A tiny power cell, commonly seen in low-end portable microcomputers." + icon_state = "cell_micro" + maxcharge = 300 \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm new file mode 100644 index 00000000000..9a08ed0445d --- /dev/null +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -0,0 +1,105 @@ +/obj/item/weapon/computer_hardware/card_slot + name = "identification card authentication module" // \improper breaks the find_hardware_by_name proc + desc = "A module allowing this computer to read or write data on ID cards. Necessary for some programs to run properly." + power_usage = 10 //W + icon_state = "card_mini" + w_class = WEIGHT_CLASS_TINY + origin_tech = "programming=2" + device_type = MC_CARD + + var/obj/item/weapon/card/id/stored_card = null + var/obj/item/weapon/card/id/stored_card2 = null + +/obj/item/weapon/computer_hardware/card_slot/Destroy() + try_eject() + return ..() + +/obj/item/weapon/computer_hardware/card_slot/GetAccess() + if(stored_card && stored_card2) // Best of both worlds + return (stored_card.GetAccess() | stored_card2.GetAccess()) + else if(stored_card) + return stored_card.GetAccess() + else if(stored_card2) + return stored_card2.GetAccess() + return ..() + +/obj/item/weapon/computer_hardware/card_slot/GetID() + if(stored_card) + return stored_card + else if(stored_card2) + return stored_card2 + return ..() + +/obj/item/weapon/computer_hardware/card_slot/on_install(obj/item/device/modular_computer/M, mob/living/user = null) + M.add_verb(device_type) + +/obj/item/weapon/computer_hardware/card_slot/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) + M.remove_verb(device_type) + +/obj/item/weapon/computer_hardware/card_slot/try_insert(obj/item/I, mob/living/user = null) + if(!holder) + return FALSE + + if(!istype(I, /obj/item/weapon/card/id)) + return FALSE + + if(stored_card && stored_card2) + user << "You try to insert \the [I] into \the [src], but its slots are occupied." + return FALSE + if(user && !user.unEquip(I)) + return FALSE + + I.forceMove(src) + + if(!stored_card) + stored_card = I + else + stored_card2 = I + user << "You insert \the [I] into \the [src]." + + return TRUE + + +/obj/item/weapon/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0) + if(!stored_card && !stored_card2) + user << "There are no cards in \the [src]." + return FALSE + + var/ejected = 0 + if(stored_card && (!slot || slot == 1)) + stored_card.forceMove(get_turf(src)) + stored_card.verb_pickup() + stored_card = null + ejected++ + + if(stored_card2 && (!slot || slot == 2)) + stored_card2.forceMove(get_turf(src)) + stored_card2.verb_pickup() + stored_card2 = null + ejected++ + + if(ejected) + if(holder) + if(holder.active_program) + holder.active_program.event_idremoved(0, slot) + + for(var/I in holder.idle_threads) + var/datum/computer_file/program/P = I + P.event_idremoved(1, slot) + + user << "You remove the card[ejected>1 ? "s" : ""] from \the [src]." + return TRUE + return FALSE + +/obj/item/weapon/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) + if(..()) + return + if(istype(I, /obj/item/weapon/screwdriver)) + user << "You press down on the manual eject button with \the [I]." + try_eject(0,user) + return + +/obj/item/weapon/computer_hardware/card_slot/examine(mob/user) + ..() + if(stored_card || stored_card2) + user << "There appears to be something loaded in the card slots." diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm new file mode 100644 index 00000000000..4a770c27be1 --- /dev/null +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -0,0 +1,173 @@ +/obj/item/weapon/computer_hardware/hard_drive + name = "hard disk drive" + desc = "A small HDD, for use in basic computers where power efficiency is desired." + power_usage = 25 + icon_state = "harddisk_mini" + critical = 1 + w_class = WEIGHT_CLASS_TINY + origin_tech = "programming=1;engineering=1" + device_type = MC_HDD + var/max_capacity = 128 + var/used_capacity = 0 + var/list/stored_files = list() // List of stored files on this drive. DO NOT MODIFY DIRECTLY! + +/obj/item/weapon/computer_hardware/hard_drive/on_remove(obj/item/device/modular_computer/MC, mob/user) + MC.shutdown_computer() + +/obj/item/weapon/computer_hardware/hard_drive/proc/install_default_programs() + store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar + store_file(new/datum/computer_file/program/ntnetdownload(src)) // NTNet Downloader Utility, allows users to download more software from NTNet repository + store_file(new/datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. + +/obj/item/weapon/computer_hardware/hard_drive/examine(user) + ..() + user << "It has [max_capacity] GQ of storage capacity." + +/obj/item/weapon/computer_hardware/hard_drive/diagnostics(var/mob/user) + ..() + // 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space. + user << "NT-NFS File Table Status: [stored_files.len]/999" + user << "Storage capacity: [used_capacity]/[max_capacity]GQ" + +// Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. +/obj/item/weapon/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F) + if(!F || !istype(F)) + return 0 + + if(!can_store_file(F)) + return 0 + + if(!check_functionality()) + return 0 + + if(!stored_files) + return 0 + + // This file is already stored. Don't store it again. + if(F in stored_files) + return 0 + + F.holder = src + stored_files.Add(F) + recalculate_size() + return 1 + +// Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. +/obj/item/weapon/computer_hardware/hard_drive/proc/remove_file(var/datum/computer_file/F) + if(!F || !istype(F)) + return 0 + + if(!stored_files) + return 0 + + if(!check_functionality()) + return 0 + + if(F in stored_files) + stored_files -= F + recalculate_size() + return 1 + else + return 0 + +// Loops through all stored files and recalculates used_capacity of this drive +/obj/item/weapon/computer_hardware/hard_drive/proc/recalculate_size() + var/total_size = 0 + for(var/datum/computer_file/F in stored_files) + total_size += F.size + + used_capacity = total_size + +// Checks whether file can be stored on the hard drive. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file. +/obj/item/weapon/computer_hardware/hard_drive/proc/can_store_file(var/datum/computer_file/F) + if(!F || !istype(F)) + return 0 + + if(F in stored_files) + return 0 + + var/name = F.filename + "." + F.filetype + for(var/datum/computer_file/file in stored_files) + if((file.filename + "." + file.filetype) == name) + return 0 + + // In the unlikely event someone manages to create that many files. + // BYOND is acting weird with numbers above 999 in loops (infinite loop prevention) + if(stored_files.len >= 999) + return 0 + if((used_capacity + F.size) > max_capacity) + return 0 + else + return 1 + + +// Tries to find the file by filename. Returns null on failure +/obj/item/weapon/computer_hardware/hard_drive/proc/find_file_by_name(var/filename) + if(!check_functionality()) + return null + + if(!filename) + return null + + if(!stored_files) + return null + + for(var/datum/computer_file/F in stored_files) + if(F.filename == filename) + return F + return null + +/obj/item/weapon/computer_hardware/hard_drive/Destroy() + stored_files = null + return ..() + +/obj/item/weapon/computer_hardware/hard_drive/New() + install_default_programs() + ..() + + +/obj/item/weapon/computer_hardware/hard_drive/advanced + name = "advanced hard disk drive" + desc = "A hybrid HDD, for use in higher grade computers where balance between power efficiency and capacity is desired." + max_capacity = 256 + origin_tech = "programming=2;engineering=2" + power_usage = 50 // Hybrid, medium capacity and medium power storage + icon_state = "harddisk_mini" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/weapon/computer_hardware/hard_drive/super + name = "super hard disk drive" + desc = "A high capacity HDD, for use in cluster storage solutions where capacity is more important than power efficiency." + max_capacity = 512 + origin_tech = "programming=3;engineering=3" + power_usage = 100 // High-capacity but uses lots of power, shortening battery life. Best used with APC link. + icon_state = "harddisk_mini" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/weapon/computer_hardware/hard_drive/cluster + name = "cluster hard disk drive" + desc = "A large storage cluster consisting of multiple HDDs for usage in dedicated storage systems." + power_usage = 500 + origin_tech = "programming=4;engineering=4" + max_capacity = 2048 + icon_state = "harddisk" + w_class = WEIGHT_CLASS_NORMAL + +// For tablets, etc. - highly power efficient. +/obj/item/weapon/computer_hardware/hard_drive/small + name = "solid state drive" + desc = "An efficient SSD for portable devices." + power_usage = 10 + origin_tech = "programming=2;engineering=2" + max_capacity = 64 + icon_state = "ssd_mini" + w_class = WEIGHT_CLASS_TINY + +/obj/item/weapon/computer_hardware/hard_drive/micro + name = "micro solid state drive" + desc = "A highly efficient SSD chip for portable devices." + power_usage = 2 + origin_tech = "programming=1;engineering=1" + max_capacity = 32 + icon_state = "ssd_micro" + w_class = WEIGHT_CLASS_TINY \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm new file mode 100644 index 00000000000..3385ac01df5 --- /dev/null +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -0,0 +1,82 @@ +var/global/ntnet_card_uid = 1 + +/obj/item/weapon/computer_hardware/network_card + name = "network card" + desc = "A basic wireless network card for usage with standard NTNet frequencies." + power_usage = 50 + origin_tech = "programming=2;engineering=1" + icon_state = "radio_mini" + var/identification_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user. + var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user. + var/long_range = 0 + var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks. + malfunction_probability = 1 + device_type = MC_NET + +/obj/item/weapon/computer_hardware/network_card/diagnostics(var/mob/user) + ..() + user << "NIX Unique ID: [identification_id]" + user << "NIX User Tag: [identification_string]" + user << "Supported protocols:" + user << "511.m SFS (Subspace) - Standard Frequency Spread" + if(long_range) + user << "511.n WFS/HB (Subspace) - Wide Frequency Spread/High Bandiwdth" + if(ethernet) + user << "OpenEth (Physical Connection) - Physical network connection port" + +/obj/item/weapon/computer_hardware/network_card/New(var/l) + ..(l) + identification_id = ntnet_card_uid + ntnet_card_uid++ + +// Returns a string identifier of this network card +/obj/item/weapon/computer_hardware/network_card/proc/get_network_tag() + return "[identification_string] (NID [identification_id])" + +// 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection +/obj/item/weapon/computer_hardware/network_card/proc/get_signal(var/specific_action = 0) + if(!holder) // Hardware is not installed in anything. No signal. How did this even get called? + return 0 + + if(!check_functionality()) + return 0 + + if(ethernet) // Computer is connected via wired connection. + return 3 + + //if(!ntnet_global || !ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. + // return 0 + + if(holder) + + var/turf/T = get_turf(holder) + if((T && istype(T)) && is_station_contact(T.z)) + // Computer is on station. Low/High signal depending on what type of network card you have + if(long_range) + return 2 + else + return 1 + + if(long_range) // Computer is not on station, but it has upgraded network card. Low signal. + return 1 + + return 0 // Computer is not on station and does not have upgraded network card. No signal. + + +/obj/item/weapon/computer_hardware/network_card/advanced + name = "advanced network card" + desc = "An advanced network card for usage with standard NTNet frequencies. Its transmitter is strong enough to connect even off-station." + long_range = 1 + origin_tech = "programming=4;engineering=2" + power_usage = 100 // Better range but higher power usage. + icon_state = "radio" + w_class = WEIGHT_CLASS_TINY + +/obj/item/weapon/computer_hardware/network_card/wired + name = "wired network card" + desc = "An advanced network card for usage with standard NTNet frequencies. This one also supports wired connection." + ethernet = 1 + origin_tech = "programming=5;engineering=3" + power_usage = 100 // Better range but higher power usage. + icon_state = "net_wired" + w_class = WEIGHT_CLASS_NORMAL diff --git a/code/modules/modular_computers/hardware/portable_disk.dm b/code/modules/modular_computers/hardware/portable_disk.dm new file mode 100644 index 00000000000..4bdf0b35493 --- /dev/null +++ b/code/modules/modular_computers/hardware/portable_disk.dm @@ -0,0 +1,35 @@ +/obj/item/weapon/computer_hardware/hard_drive/portable + name = "data disk" + desc = "Removable disk used to store data." + power_usage = 10 + icon_state = "datadisk6" + w_class = WEIGHT_CLASS_TINY + critical = 0 + max_capacity = 16 + origin_tech = "programming=1" + device_type = MC_SDD + +/obj/item/weapon/computer_hardware/hard_drive/portable/on_install(obj/item/device/modular_computer/M, mob/living/user = null) + M.add_verb(device_type) + +/obj/item/weapon/computer_hardware/hard_drive/portable/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) + ..() + M.remove_verb(device_type) + +/obj/item/weapon/computer_hardware/hard_drive/portable/install_default_programs() + return // Empty by default + +/obj/item/weapon/computer_hardware/hard_drive/portable/advanced + name = "advanced data disk" + power_usage = 20 + icon_state = "datadisk5" + max_capacity = 64 + origin_tech = "programming=2" + +/obj/item/weapon/computer_hardware/hard_drive/portable/super + name = "super data disk" + desc = "Removable disk used to store large amounts of data." + power_usage = 40 + icon_state = "datadisk3" + max_capacity = 256 + origin_tech = "programming=4" diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm new file mode 100644 index 00000000000..9595b7e5384 --- /dev/null +++ b/code/modules/modular_computers/hardware/printer.dm @@ -0,0 +1,63 @@ +/obj/item/weapon/computer_hardware/printer + name = "printer" + desc = "Computer-integrated printer with paper recycling module." + power_usage = 100 + origin_tech = "programming=2;engineering=2" + icon_state = "printer" + w_class = WEIGHT_CLASS_NORMAL + device_type = MC_PRINT + var/stored_paper = 20 + var/max_paper = 30 + +/obj/item/weapon/computer_hardware/printer/diagnostics(mob/living/user) + ..() + user << "Paper level: [stored_paper]/[max_paper]" + +/obj/item/weapon/computer_hardware/printer/examine(mob/user) + ..() + user << "Paper level: [stored_paper]/[max_paper]" + + +/obj/item/weapon/computer_hardware/printer/proc/print_text(var/text_to_print, var/paper_title = "") + if(!stored_paper) + return FALSE + if(!check_functionality()) + return FALSE + + var/obj/item/weapon/paper/P = new/obj/item/weapon/paper(get_turf(holder)) + + // Damaged printer causes the resulting paper to be somewhat harder to read. + if(damage > damage_malfunction) + P.info = stars(text_to_print, 100-malfunction_probability) + else + P.info = text_to_print + if(paper_title) + P.name = paper_title + P.update_icon() + stored_paper-- + P = null + return TRUE + +/obj/item/weapon/computer_hardware/printer/try_insert(obj/item/I, mob/living/user = null) + if(istype(I, /obj/item/weapon/paper)) + if(stored_paper >= max_paper) + user << "You try to add \the [I] into [src], but its paper bin is full!" + return FALSE + + if(user && !user.unEquip(I)) + return FALSE + + user << "You insert \the [I] into [src]'s paper recycler." + qdel(I) + stored_paper++ + return TRUE + return FALSE + +/obj/item/weapon/computer_hardware/printer/mini + name = "miniprinter" + desc = "A small printer with paper recycling module." + power_usage = 50 + icon_state = "printer_mini" + w_class = WEIGHT_CLASS_TINY + stored_paper = 5 + max_paper = 15 \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm new file mode 100644 index 00000000000..cb057c2775a --- /dev/null +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -0,0 +1,93 @@ +/obj/item/weapon/computer_hardware/recharger + critical = 1 + enabled = 1 + var/charge_rate = 100 + device_type = MC_CHARGE + +/obj/item/weapon/computer_hardware/recharger/proc/use_power(amount, charging=0) + if(charging) + return 1 + return 0 + +/obj/item/weapon/computer_hardware/recharger/process() + ..() + var/obj/item/weapon/computer_hardware/battery/battery_module = holder.all_components[MC_CELL] + if(!holder || !battery_module || !battery_module.battery) + return + + var/obj/item/weapon/stock_parts/cell/cell = battery_module.battery + if(cell.charge >= cell.maxcharge) + return + + if(use_power(charge_rate, charging=1)) + holder.give_power(charge_rate * CELLRATE) + + +/obj/item/weapon/computer_hardware/recharger/APC + name = "area power connector" + desc = "A device that wirelessly recharges connected device from nearby APC." + icon_state = "charger_APC" + w_class = WEIGHT_CLASS_SMALL // Can't be installed into tablets/PDAs + origin_tech = "programming=2;engineering=2;powerstorage=3" + +/obj/item/weapon/computer_hardware/recharger/APC/use_power(amount, charging=0) + if(istype(holder.physical, /obj/machinery)) + var/obj/machinery/M = holder.physical + if(M.powered()) + M.use_power(amount) + return 1 + + else + var/area/A = get_area(src) + if(!A || !isarea(A)) + return 0 + + if(A.powered(EQUIP)) + A.use_power(amount, EQUIP) + return 1 + return 0 + +/obj/item/weapon/computer_hardware/recharger/wired + name = "wired power connector" + desc = "A power connector that recharges connected device from nearby power wire. Incompatible with portable computers." + icon_state = "charger_wire" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "engineering=2;powerstorage=1" + +/obj/item/weapon/computer_hardware/recharger/wired/can_install(obj/item/device/modular_computer/M, mob/living/user = null) + if(istype(M.physical, /obj/machinery) && M.physical.anchored) + return ..() + user << "\The [src] is incompatible with portable computers!" + return 0 + +/obj/item/weapon/computer_hardware/recharger/wired/use_power(amount, charging=0) + if(istype(holder.physical, /obj/machinery) && holder.physical.anchored) + var/obj/machinery/M = holder.physical + var/turf/T = M.loc + if(!T || !istype(T)) + return 0 + + var/obj/structure/cable/C = T.get_cable_node() + if(!C || !C.powernet) + return 0 + + var/power_in_net = C.powernet.avail-C.powernet.load + + if(power_in_net && power_in_net > amount) + C.powernet.load += amount + return 1 + + return 0 + + + +// This is not intended to be obtainable in-game. Intended for adminbus and debugging purposes. +/obj/item/weapon/computer_hardware/recharger/lambda + name = "lambda coil" + desc = "A very complex device that draws power from its own bluespace dimension." + icon_state = "charger_lambda" + w_class = WEIGHT_CLASS_TINY + charge_rate = 100000 + +/obj/item/weapon/computer_hardware/recharger/lambda/use_power(amount, charging=0) + return 1 diff --git a/icons/obj/modular_console.dmi b/icons/obj/modular_console.dmi new file mode 100644 index 0000000000000000000000000000000000000000..85d6026617f0667c5e82e62a595429667ec64c18 GIT binary patch literal 26642 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 Gum%9P&mj8% literal 0 HcmV?d00001 diff --git a/icons/program_icons/alarm_red.gif b/icons/program_icons/alarm_red.gif new file mode 100644 index 0000000000000000000000000000000000000000..327d58e4db8acf03e219e09ace17c6f1f990f499 GIT binary patch literal 163 zcmZ?wbhEHb6k`x$SjfO&!NBl?f#JY`0}KodivPL&TtkAL9RpmA^bD98K|()SIE@&X z8FYYbpawAp1}4op{VPwu z9Uz+lq=kVgdrtq#({K3~&(XZmz4_ijR))4m9@89-*W5WZulLlknX~7~XS7<@-QM-< z)5p3a3k8=ph_;@YD-fCaZO)aKyj=lOOHa#wTkte*^$w}Z9NjwJhnsI~PrIE`a*N4r Z`pwD9_U->OX}Y^(L`Fr2MIZx%H2^2XMuq?Y literal 0 HcmV?d00001 diff --git a/icons/program_icons/batt_20.gif b/icons/program_icons/batt_20.gif new file mode 100644 index 0000000000000000000000000000000000000000..1d5e38fc888430a7bb2a1062468c7d822d7c66e1 GIT binary patch literal 284 zcmZ?wbhEHb)L;-}IK;rf@PmQD0tgun95?`E!hzy{Za>$MU}whwS0gveMHo^t=VfrUT;SOEL275g-IH&n1op^o z=9lV!P(86h^6mx>t7j(O%Z^Kid|P5Z$GPHY-im1Li@&d`EsQmCFt|K%`J3m`_rh+z z&%XGl`Q`8b4UOWQ*sKjFXl>T}cWXYI3Y~UjK2~sKzM#q(-TuQppDLZbR{Cf!?s%H9 d{c_%U&2Mf?S8e&VTesZc!;Tui{VEI$)&M66a}odm literal 0 HcmV?d00001 diff --git a/icons/program_icons/batt_40.gif b/icons/program_icons/batt_40.gif new file mode 100644 index 0000000000000000000000000000000000000000..d5cedb996759170d5e2477619ca9f40d66a18c6a GIT binary patch literal 185 zcmZ?wbhEHb)L;-}*u=oV@PmQDg5kh{13!QaB%t`8+s`#5*x50_)kx2PnGq+Z` zi-Cnf2c!(7oq;*qW7nO32B$n%@6~;AcK82A5r&k^c^OP*m|J z3#SnSD}xS51xPysb9ltAJO2z$d9L2e`{L~G{|zi0DVg&!v|283?E4_Kv*7N@w^{Gs zt@&&!blQ>mSizC`f+}Zp`w#bgs&w{R>7%{4<7vkB%X#NDzqu`4wdL1t-ExBuJ8Jy) Vs}Qg&7i3qn2@A+BDa!4#1^~mJZcP9H literal 0 HcmV?d00001 diff --git a/icons/program_icons/batt_60.gif b/icons/program_icons/batt_60.gif new file mode 100644 index 0000000000000000000000000000000000000000..d39534c8cb03fd6b7580267d38ebbed0e7d16433 GIT binary patch literal 166 zcmZ?wbhEHb)L;-}SjfP@@PmQDg5kh{13;$Ye{Mh5kYH!W09PYD17=2$&`%alE(T@> z9Uz+lq=kVgWlsOf({K3~&(XZmz4_ijR))4m9@89-*W5WZulLlknX~7~XS7<%UFKf1 z`(~4FeCi2qq3|8I_&BGU)(1We&T?Ux91|_L^hVh9IhhrArZ%15RF+p;vVC*Z z9Uz+lq=kVgYfk^l({K3~&(XZmz4_ijR))4m9@89-*W5WZulLlknX~7~XS7<@-8SCU z@UqJKc7ar_(qx0*Qj WFFrMOA(iFsh8__a6&8UE4AubO+dx48 literal 0 HcmV?d00001 diff --git a/icons/program_icons/charging.gif b/icons/program_icons/charging.gif new file mode 100644 index 0000000000000000000000000000000000000000..cf19ca72876b33bb683ea14aa4d9dc4c6c621bc8 GIT binary patch literal 160 zcmZ?wbhEHb)L;-}SjfP@V8L+UzyXFIKo*1Ie{Mh5kYH!W09PYD17=2$&`%alE(T@> z9gsXo3jy+yJb<5u_TVtSn;qvt0wAPESQ>533&YSr0)^5#} LkH0rFF<1itpY1(Q literal 0 HcmV?d00001 diff --git a/icons/program_icons/downloader_finished.gif b/icons/program_icons/downloader_finished.gif new file mode 100644 index 0000000000000000000000000000000000000000..f01b7c42af8ba61a23b3ba009a1f491922267479 GIT binary patch literal 130 zcmZ?wbhEHb6k`x$SjfP@V8L+UzyXFIKo*1Ie{Mh5kYH!W09PYD17=2$&`%alE(T@> z9gsXo3j>qGoc@)k-|laiBP6A;iLESQZs9fdE`gn;8fkWGK5X@y)0gnV&)mlFMPK)y dJkk3GD#0FQM`JW^Mtrhj=MufRCyI-~8UX$-DMkPQ literal 0 HcmV?d00001 diff --git a/icons/program_icons/downloader_running.gif b/icons/program_icons/downloader_running.gif new file mode 100644 index 0000000000000000000000000000000000000000..68fb977c86dda6273d7fbd853b86ca38375891b2 GIT binary patch literal 574 zcmZ?wbhEHb6k`x$SjfP@V8L+UzyXFIKo*1Ie{Mh5kYH!W09PYD17=2$&`%alE(T@> z9gsXo3j>qvoc@)k-|laiBP6A;iLET)R_h~=Y0o;By*jn+owOJ>9m=RW66O|OWA74h zEd}Y|KK{w)`nOoDHmPCNx3e^3MNlw1SYPk+U;jj~S*eXvYtx$-T+f^X?*g@+`~Hvr ziYQjA_3-Q8d}^(=waC@4|M+>h+K(hMVsnBCAtSzs1pCZ=|HsTbplx#B#||H@=_i@6 z8DvScK^{p97bLw{HAiD|-)GKVD{io9Ul>uM&z5A<0#03AzH(Mc%dwY>85pbqqOR3W literal 0 HcmV?d00001 diff --git a/icons/program_icons/ntnrc_idle.gif b/icons/program_icons/ntnrc_idle.gif new file mode 100644 index 0000000000000000000000000000000000000000..d47c01d580080e8e614de62448815b87705a122f GIT binary patch literal 113 zcmZ?wbhEHb6k`x$n8?7uV8L+UzyTl)1d9K;{aizWogD*Qjr0td89_onSvZXtm>6_G z@*ph?O!`y$SDt>$zc^;Wt!~{*{n8&Ar)bngYlhsO6~oo#^kK@TOEwR;%O1S>ei3K; KkwqIB7_0$IQY1tG literal 0 HcmV?d00001 diff --git a/icons/program_icons/ntnrc_new.gif b/icons/program_icons/ntnrc_new.gif new file mode 100644 index 0000000000000000000000000000000000000000..af72a8b332fc4983088ccafc64593bb04294e551 GIT binary patch literal 181 zcmZ?wbhEHb6k`x$SjfP@V8L+UzyXFIKo*1Ie{Mh5kYH!W09PYD17=2$&`%alBL-#$ z9gsXo3j>q>oc@)k-|{bxS#+yg_fo&~$HpldbQvgwk|!|k#MZ@yo| T*?wfvM*LGc*ia%L6 zH5gbJbU=zg+8LNVJ$Bvsr?~GV)811_tG)|2eeYD>$-vSu>$TUecap}dW?60X(Kg8X zZxDORUFMU7Vw_-vkx?=4giDbnCG)NxE1kLa#RdURiL4|QtfqV6GX2zwDX-^!UcJa@ zoxSedcLuJ09CwyVC_c)U%u$(n)L+QW)XXYWfupNQ!M=ouhCBcc!@ou<`_u6H! zYT3F)=M2|czt#M2GoQEPwXTzM?8YT3*ZNL8y>xLR(@H08mDepETZ|_wGh#CuPq?gG zzUrmqWxwp@8ok%K=B#(;F>*TiXv2|gJ=q0IHothGGoN?$q*-@kle;^f-ZrrmVz34P Dlu)Cy literal 0 HcmV?d00001 diff --git a/icons/program_icons/power_warn.gif b/icons/program_icons/power_warn.gif new file mode 100644 index 0000000000000000000000000000000000000000..0c85b3a85a1e46e7ea81d98562f1cf10509f13d4 GIT binary patch literal 485 zcmZ?wbhEHb6k`x$*u=oV@B;`Le*9oKaNq!t2M3D(x&2&2f}I@$T#fV$m>Gc*ia%L6 zH5gbJbU=zg+8LO`Ja*mrr?~GV)812Qz2ejFDse<>RK<8qJ9lFO!-0LPH=Qzme2d$! z!Q?haTh*Hi7Wshi)kljKh4uzSyY(Ku_T|@RnU%MFKK}mNvplu%q^f7RZf?*W7f-BK z#lWnJy>!#N==SvE98BtZOF8>8eyx>dWZ>dlD;+z%n6=5OX>I#W7nOsF8=8YvD!usw zmVeOXej-@Cu6mAVXw_>A8ZTFIkYL$G`gO`34U23&g|61AV zmstl>WoP^`48OSIedNi9Dp>9EhuO9FRMM*N!cE@;ABg&?x-c+QnMJ*}n)|0UM=Ww% z@Uw0S&h*t^CF~>HKYS8&?+W?ya*oo<4Xsl;*JjN2oEj>$Y@+k?tAR&67#OSp?%csB literal 0 HcmV?d00001 diff --git a/icons/program_icons/sig_high.gif b/icons/program_icons/sig_high.gif new file mode 100644 index 0000000000000000000000000000000000000000..efb20f6730654a761fa061b451efae0122d0aa71 GIT binary patch literal 182 zcmZ?wbhEHb)L;-}SjfP@V8L+UzyXFIKo*1Ie{Mh5kYH!W09PYD17=2$&`%alBL-#$ z9gsXo3jO_X57g-b8zbsXLHQ^1moO6yJZE zsA1B~Kl!3Vf!jT|?^ePgVB*KO^Q)UQp-((S@6Vw^UIER58u{+UcY pWZL=se|Wpurscs#79FY`73myDPRw|+k>h7VQ0L5*oskR-)&PE-NT2`! literal 0 HcmV?d00001 diff --git a/icons/program_icons/sig_low.gif b/icons/program_icons/sig_low.gif new file mode 100644 index 0000000000000000000000000000000000000000..08f98baea8e69b04e3b8a8f12f20ad4bb09b2ec8 GIT binary patch literal 179 zcmZ?wbhEHb)L;-}IK;qUVZm_VzyXFIKo-LfAPEDC|GE8KLxPk%Fip~B~z57xb zi)?z*Z*Xy0o|rpPO~Z+yA@$@>1_6#hWrL)ndfU%un{lK$aNjjtzs6(v{wv#ltnFR= VJZVQz-_mTu^o*>a79LIpYXEgwJ&phX literal 0 HcmV?d00001 diff --git a/icons/program_icons/sig_none.gif b/icons/program_icons/sig_none.gif new file mode 100644 index 0000000000000000000000000000000000000000..79840d8a2bd4b30c263cbc680aa2727dfeb27555 GIT binary patch literal 202 zcmZ?wbhEHb)L;-}IK;rfV8QSMNd5rQ3P*m|J z3#SnSD}xS51xPysb4|pqJO2!r%))zrFb0-CKQlph(PPc@iDJ$HNpG_owf1h(n=`wt zUenMc;IZ>XC+j6^CT7V^4LFneD~G3DvVYsFjzo7kzV8>k`g)oSEfegn*X=s^y{0?n tc4VnY$MhFJy)7CAO2cZKN~((6ntigmW7~}j6!Ut0@@CGO&CkhT4FK +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    + +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +No program loaded. Please select program from list below. +
    + + {{for data.programs}} +
    {{:helper.link(value.desc, null, {'action' : 'PC_runprogram', 'name' : value.name})}} + {{:helper.link('', 'times', {'action' : 'PC_killprogram', 'name' : value.name}, value.running ? null : 'disabled')}} + {{/for}} +
    +
    \ No newline at end of file diff --git a/nano/templates/file_manager.tmpl b/nano/templates/file_manager.tmpl new file mode 100644 index 00000000000..271bc8822b7 --- /dev/null +++ b/nano/templates/file_manager.tmpl @@ -0,0 +1,106 @@ +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +{{if data.error}} +

    An error has occurred and this program can not continue.

    + Additional information: {{:data.error}}
    + Please try again. If the problem persists contact your system administrator for assistance. + {{:helper.link('Restart program', null, {'action' : 'PRG_closefile'})}} +{{else}} + {{if data.filename}} +

    Viewing file {{:data.filename}}

    +
    + {{:helper.link('CLOSE', null, {'action' : 'PRG_closefile'})}} + {{:helper.link('EDIT', null, {'action' : 'PRG_edit'})}} + {{:helper.link('PRINT', null, {'action' : 'PRG_printfile'})}} +

    + {{:data.filedata}} + {{else}} +

    Available files (local):

    +
    + + + + + + + + {{for data.files}} + + + + + + + {{/for}} +
    File nameFile typeFile size (GQ)Operations
    {{:value.name}}.{{:value.type}}{{:value.size}}GQ + {{:helper.link('VIEW', null, {'action' : 'PRG_openfile', 'name' : value.name})}} + {{:helper.link('DELETE', null, {'action' : 'PRG_deletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('RENAME', null, {'action' : 'PRG_rename', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('CLONE', null, {'action' : 'PRG_clone', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{if data.usbconnected}} + {{:helper.link('EXPORT', null, {'action' : 'PRG_copytousb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{/if}} +
    +
    + {{if data.usbconnected}} +

    Available files (portable device):

    +
    + + + + + + + + {{for data.usbfiles}} + + + + + + + {{/for}} +
    File nameFile typeFile size (GQ)Operations
    {{:value.name}}.{{:value.type}}{{:value.size}}GQ + {{:helper.link('DELETE', null, {'action' : 'PRG_usbdeletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{if data.usbconnected}} + {{:helper.link('IMPORT', null, {'action' : 'PRG_copyfromusb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{/if}} +
    +
    + {{/if}} + {{:helper.link('NEW DATA FILE', null, {'action' : 'PRG_newtextfile'})}} + {{/if}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/laptop_configuration.tmpl b/nano/templates/laptop_configuration.tmpl new file mode 100644 index 00000000000..41ab8f74e02 --- /dev/null +++ b/nano/templates/laptop_configuration.tmpl @@ -0,0 +1,102 @@ +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +Welcome to computer configuration utility. Please consult your system administrator if you have any questions about your device.
    +
    +

    Power Supply

    + {{if data.battery}} +
    +
    Battery Status
    +
    Active
    +
    +
    +
    Battery Rating
    +
    {{:data.battery.max}}
    +
    +
    +
    Battery Charge
    + {{:helper.displayBar(data.battery.charge, 0, data.battery.max, (data.battery.charge > data.battery.max/2) ? 'good' : (data.battery.charge > data.battery.max/4) ? 'average' : 'bad')}} +
    + {{:helper.smoothRound(data.battery.charge, 1)}}/{{:data.battery.max}} +
    +
    + {{else}} +
    +
    Battery Status
    +
    Not Available
    +
    + {{/if}} + +
    +
    Power Usage
    +
    {{:data.power_usage}}W
    +
    +
    + +
    +

    File System

    +
    +
    Used Capacity
    + {{:helper.displayBar(data.disk_used, 0, data.disk_size, (data.disk_used > data.disk_size/2) ? 'bad' : (data.disk_used > data.disk_size/4) ? 'average' : 'good')}} +
    + {{:helper.smoothRound(data.disk_used, 1)}}/{{:data.disk_size}}GQ +
    +
    +
    + +
    +

    Computer Components

    + {{for data.hardware}} +

    {{:value.name}}

    + {{:value.desc}}
    +
    +
    State
    +
    {{:value.enabled ? "Enabled" : "Disabled"}}
    +
    + +
    +
    Power Usage
    +
    {{:value.powerusage}}W
    +
    + + {{if !value.critical}} +
    +
    Toggle Component
    +
    {{:helper.link(value.enabled ? 'On' : 'Off', value.enabled ? 'power-off' : 'close', {'action' : 'PC_toggle_component', 'name' : value.name})}}
    +
    + {{/if}} +

    + {{/for}} +
    \ No newline at end of file diff --git a/nano/templates/ntnet_chat.tmpl b/nano/templates/ntnet_chat.tmpl new file mode 100644 index 00000000000..02287508ab8 --- /dev/null +++ b/nano/templates/ntnet_chat.tmpl @@ -0,0 +1,93 @@ +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +{{if data.adminmode}} +

    ADMINISTRATIVE MODE

    +{{/if}} + +{{if data.title}} +
    +
    Current channel:
    +
    {{:data.title}}
    +
    +
    +
    Operator access:
    +
    {{if data.is_operator}}Enabled{{else}}Disabled{{/if}}
    +
    +
    +
    Controls:
    +
    + +
    {{:helper.link('Send message', 'comment-o', {'action' : 'PRG_speak'})}} +
    {{:helper.link('Change nickname', 'pencil', {'action' : 'PRG_changename'})}} +
    {{:helper.link('Toggle administration mode', 'lock', {'action' : 'PRG_toggleadmin'})}} +
    {{:helper.link('Leave channel', 'sign-out', {'action' : 'PRG_leavechannel'})}} +
    {{:helper.link('Save log to local drive', 'download', {'action' : 'PRG_savelog'})}} + {{if data.is_operator}} +
    {{:helper.link('Rename channel', 'pencil', {'action' : 'PRG_renamechannel'})}} +
    {{:helper.link('Set password', 'key', {'action' : 'PRG_setpassword'})}} +
    {{:helper.link('Delete channel', 'trash', {'action' : 'PRG_deletechannel'})}} + {{/if}} +
    +
    +
    + Chat Window +
    +
    +
    + {{for data.messages}} + {{:value.msg}}
    + {{/for}} +
    +
    +
    + Connected Users
    + {{for data.clients}} + {{:value.name}}
    + {{/for}} +{{else}} + Controls: + +
    {{:helper.link('Change nickname', 'pencil', {'action' : 'PRG_changename'})}} +
    {{:helper.link('New Channel', 'plus', {'action' : 'PRG_newchannel'})}} +
    {{:helper.link('Toggle administration mode', 'lock', {'action' : 'PRG_toggleadmin'})}} +
    + Available channels: + + {{for data.all_channels}} +
    {{:helper.link(value.chan, 'sign-in', {'action' : 'PRG_joinchannel', 'id' : value.id})}}
    + {{/for}} +
    +{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl new file mode 100644 index 00000000000..701a13e8aa9 --- /dev/null +++ b/nano/templates/ntnet_dos.tmpl @@ -0,0 +1,52 @@ +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +{{if data.error}} + ##SYSTEM ERROR: {{:data.error}}{{:helper.link('RESET', null, {'action' : 'PRG_reset'})}} +{{else data.target}} + ##DoS traffic generator active. Tx: {{:data.speed}}GQ/s
    + {{for data.dos_strings}} + {{:value.nums}}
    + {{/for}} + ABORT +{{else}} + ##DoS traffic generator ready. Select target device.
    + Targeted device ID: {{if data.focus}}{{:data.focus}}{{else}}None{{/if}} + {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
    + Detected devices on network:
    + {{for data.relays}} + {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} + {{/for}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_downloader.tmpl b/nano/templates/ntnet_downloader.tmpl new file mode 100644 index 00000000000..839347b5e06 --- /dev/null +++ b/nano/templates/ntnet_downloader.tmpl @@ -0,0 +1,137 @@ +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +Welcome to software download utility. Please select which software you wish to download.
    +{{if data.error}} +

    Download Error

    +
    +
    Information
    +
    {{:data.error}}
    +
    +
    +
    Reset Program
    +
    {{:helper.link('RESET', 'times', {'action' : 'PRG_reseterror'})}}
    +
    +{{else}} + {{if data.downloadname}} +

    Download Running

    +
    + Please wait... +
    +
    File name
    +
    {{:data.downloadname}}
    +
    +
    +
    File description
    +
    {{:data.downloaddesc}}
    +
    +
    +
    File size
    +
    {{:helper.smoothRound(data.downloadcompletion, 1)}}/{{:data.downloadsize}}GQ
    +
    +
    +
    Transfer Rate
    +
    {{:data.downloadspeed}} GQ/s
    +
    +
    +
    Download progress
    + {{:helper.displayBar(data.downloadcompletion, 0, data.downloadsize, (data.downloadcompletion > data.downloadsize/2) ? 'good' : (data.downloadcompletion > data.downloadsize/4) ? 'average' : 'bad')}} +
    + {{:helper.smoothRound(data.downloadcompletion, 1)}}/{{:data.downloadsize}}GQ +
    +
    +
    + {{else}} +

    Primary software repository

    +
    +
    +
    Hard drive
    + {{:helper.displayBar(data.disk_used, 0, data.disk_size, (data.disk_used > data.disk_size/2) ? 'bad' : (data.disk_used > data.disk_size/4) ? 'average' : 'good')}} +
    + {{:helper.smoothRound(data.disk_used, 1)}}/{{:data.disk_size}}GQ +
    +
    + {{for data.downloadable_programs}} +
    +
    +
    File name
    +
    {{:value.filename}} ({{:value.size}} GQ)
    +
    +
    +
    Program name
    +
    {{:value.filedesc}}
    +
    +
    +
    Description
    +
    {{:value.fileinfo}}
    +
    +
    +
    Compatibility
    +
    {{:value.compatibility}}
    +
    +
    +
    File controls
    +
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename})}}
    +
    + {{/for}} +
    + {{if data.hackedavailable}} +

    UNKNOWN software repository

    +
    + Please note that NanoTrasen does not recommend download of software from non-official servers. + {{for data.hacked_programs}} +
    +
    +
    File name
    +
    {{:value.filename}} ({{:value.size}} GQ)
    +
    +
    +
    Program name
    +
    {{:value.filedesc}}
    +
    +
    +
    Description
    +
    {{:value.fileinfo}}
    +
    +
    +
    File controls
    +
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename})}}
    +
    + {{/for}} + + {{/if}} + {{/if}} +{{/if}} +
    NTOS v2.0.4b Copyright NanoTrasen 2557 - 2559 \ No newline at end of file diff --git a/nano/templates/ntnet_relay.tmpl b/nano/templates/ntnet_relay.tmpl new file mode 100644 index 00000000000..8625f94dd14 --- /dev/null +++ b/nano/templates/ntnet_relay.tmpl @@ -0,0 +1,19 @@ +

    Relay

    +{{if data.dos_crashed}} +

    NETWORK BUFFERS OVERLOADED

    +

    Overload Recovery Mode

    + This system is suffering temporary outage due to overflow of traffic buffers. Until buffered traffic is processed, all further requests will be dropped. Frequent occurences of this error may indicate insufficient hardware capacity of your network. Please contact your network planning department for instructions on how to resolve this issue. +

    ADMINISTRATIVE OVERRIDE

    + CAUTION - Data loss may occur + {{:helper.link('Purge buffered traffic', 'signal', {'action' : 'restart'})}} +{{else}} +
    +
    Relay status
    +
    {{:helper.link(data.enabled ? "ENABLED" : "DISABLED", 'power-off', {'action' : 'toggle'})}}
    +
    + +
    +
    Network buffer status
    +
    {{:data.dos_overload}}/{{:data.dos_capacity}} GQ
    +
    +{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_transfer.tmpl b/nano/templates/ntnet_transfer.tmpl new file mode 100644 index 00000000000..9495df39bf1 --- /dev/null +++ b/nano/templates/ntnet_transfer.tmpl @@ -0,0 +1,112 @@ +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + +{{if data.error}} +
    +

    An error has occurred during operation...

    + Additional information: {{:data.error}}
    + {{:helper.link('Clear', null, {'action' : 'PRG_reset'})}} +
    +{{else data.downloading}} +

    Download in progress...

    +
    +
    Downloaded file:
    +
    {{:data.download_name}}
    +
    +
    +
    Download progress:
    +
    {{:helper.smoothRound(data.download_progress)}}/{{:data.download_size}}GQ
    +
    +
    +
    Transfer speed:
    +
    {{:data.download_netspeed}}GQ/s
    +
    +
    +
    Controls:
    +
    {{:helper.link('Abort download', 'arrow-left', {'action' : 'PRG_reset'})}}
    +
    +{{else data.uploading}} +

    Server enabled

    +
    +
    Connected clients:
    +
    {{:data.upload_clients}}
    +
    +
    +
    Provided file:
    +
    {{:data.upload_filename}}
    +
    +
    +
    Server password:
    +
    {{if data.upload_haspassword}}ENABLED{{else}}DISABLED{{/if}}
    +
    +
    +
    Commands:
    +
    + {{:helper.link('Set password', 'key', {'action' : 'PRG_setpassword'})}} + {{:helper.link('Exit server', 'arrow-left', {'action' : 'PRG_reset'})}} +
    +
    +{{else data.upload_filelist}} +

    File transfer server ready. Select file to upload:

    + +
    File nameFile sizeControls + {{for data.upload_filelist}} +
    {{:value.filename}} + {{:value.size}}GQ + {{:helper.link('Select', 'upload', {'action' : 'PRG_uploadfile', 'id' : value.uid})}} + {{/for}} +
    +
    + {{:helper.link('Set password', 'key', {'action' : 'PRG_setpassword'})}} + {{:helper.link('Return', 'arrow-left', {'action' : 'PRG_reset'})}} +{{else}} +

    Available files:

    +
    Server UIDFile NameFile SizePassword ProtectionOperations + {{for data.servers}} +
    {{:value.uid}} + {{:value.filename}} + {{:value.size}}GQ + {{if value.haspassword}} + Enabled + {{else}} + Disabled + {{/if}} + + {{:helper.link('Download', 'download', {'action' : 'PRG_downloadfile', 'id' : value.uid})}} + {{/for}} +
    +
    + {{:helper.link('Send file', 'upload', {'action' : 'PRG_uploadmenu'})}} +{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 566614547bc..09f8366c638 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1754,6 +1754,40 @@ #include "code\modules\mob\new_player\poll.dm" #include "code\modules\mob\new_player\preferences_setup.dm" #include "code\modules\mob\new_player\sprite_accessories.dm" +#include "code\modules\modular_computers\computers\item\computer.dm" +#include "code\modules\modular_computers\computers\item\computer_components.dm" +#include "code\modules\modular_computers\computers\item\computer_damage.dm" +#include "code\modules\modular_computers\computers\item\computer_power.dm" +#include "code\modules\modular_computers\computers\item\computer_ui.dm" +#include "code\modules\modular_computers\computers\item\laptop.dm" +#include "code\modules\modular_computers\computers\item\laptop_presets.dm" +#include "code\modules\modular_computers\computers\item\processor.dm" +#include "code\modules\modular_computers\computers\item\tablet.dm" +#include "code\modules\modular_computers\computers\item\tablet_presets.dm" +#include "code\modules\modular_computers\computers\machinery\modular_computer.dm" +#include "code\modules\modular_computers\file_system\computer_file.dm" +#include "code\modules\modular_computers\file_system\data.dm" +#include "code\modules\modular_computers\file_system\program.dm" +#include "code\modules\modular_computers\file_system\program_events.dm" +#include "code\modules\modular_computers\file_system\programs\configurator.dm" +#include "code\modules\modular_computers\file_system\programs\file_browser.dm" +#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" +#include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" +#include "code\modules\modular_computers\file_system\programs\nttransfer.dm" +#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm" +#include "code\modules\modular_computers\hardware\_hardware.dm" +#include "code\modules\modular_computers\hardware\ai_slot.dm" +#include "code\modules\modular_computers\hardware\battery_module.dm" +#include "code\modules\modular_computers\hardware\card_slot.dm" +#include "code\modules\modular_computers\hardware\CPU.dm" +#include "code\modules\modular_computers\hardware\hard_drive.dm" +#include "code\modules\modular_computers\hardware\network_card.dm" +#include "code\modules\modular_computers\hardware\portable_disk.dm" +#include "code\modules\modular_computers\hardware\printer.dm" +#include "code\modules\modular_computers\hardware\recharger.dm" +#include "code\modules\modular_computers\NTNet\NTNet.dm" +#include "code\modules\modular_computers\NTNet\NTNet_relay.dm" +#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm" #include "code\modules\nano\nanoexternal.dm" #include "code\modules\nano\nanomanager.dm" #include "code\modules\nano\nanomapgen.dm" From 295f35f29c2707e985b37025d5375961f1c34062 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sun, 19 Feb 2017 22:23:18 -0300 Subject: [PATCH 03/22] Tweaks. --- .../computers/item/computer_ui.dm | 12 +++++------ .../file_system/programs/antagonist/dos.dm | 2 +- .../file_system/programs/ntnrc_client.dm | 8 ++++---- .../file_system/programs/nttransfer.dm | 2 +- .../hardware/network_card.dm | 4 ++-- nano/templates/ntnet_dos.tmpl | 20 +++++++++++++------ nano/templates/ntnet_downloader.tmpl | 2 +- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index d7b5042d3b2..0e24c7a26f4 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -67,7 +67,7 @@ if("PC_minimize") var/mob/user = usr if(!active_program || !all_components[MC_CPU]) - return + return 1 idle_threads.Add(active_program) active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs @@ -85,7 +85,7 @@ P = hard_drive.find_file_by_name(prog) if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) - return + return 1 P.kill_program(forced = TRUE) user << "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed." @@ -99,12 +99,12 @@ if(!P || !istype(P)) // Program not found or it's not executable program. user << "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning." - return + return 1 P.computer = src if(!P.is_supported_by_hardware(hardware_flag, 1, user)) - return + return 1 // The program is already running. Resume it. if(P in idle_threads) @@ -118,11 +118,11 @@ if(idle_threads.len > PU.max_idle_programs) user << "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error." - return + return 1 if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. user << "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning." - return + return 1 if(P.run_program(user)) active_program = P update_icon() diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 768ca343669..17e2187eb81 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -40,7 +40,7 @@ if(!ui) var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) assets.send(user) - ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 400, 250) + ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 575, 250) ui.set_auto_update(1) ui.open() diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index aac18a2f53b..753eadbdb2a 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -68,7 +68,7 @@ var/mob/living/user = usr var/channel_title = reject_bad_text(input(user,"Enter channel name or leave blank to cancel:")) if(!channel_title) - return + return 1 var/datum/ntnet_conversation/C = new/datum/ntnet_conversation() C.add_client(src) C.operator = src @@ -91,7 +91,7 @@ channel.remove_client(src) channel = null else - return + return 1 netadmin_mode = 1 if("PRG_changename") . = 1 @@ -106,7 +106,7 @@ if("PRG_savelog") . = 1 if(!channel) - return + return 1 var/mob/living/user = usr var/logname = input(user,"Enter desired logfile name (.log) or leave blank to cancel:") if(!logname || !channel) @@ -136,7 +136,7 @@ var/mob/living/user = usr var/newname = reject_bad_text(input(user, "Enter new channel name or leave blank to cancel:")) if(!newname || !channel) - return + return 1 channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") channel.title = newname if("PRG_deletechannel") diff --git a/code/modules/modular_computers/file_system/programs/nttransfer.dm b/code/modules/modular_computers/file_system/programs/nttransfer.dm index f647675c078..d35a5127e18 100644 --- a/code/modules/modular_computers/file_system/programs/nttransfer.dm +++ b/code/modules/modular_computers/file_system/programs/nttransfer.dm @@ -109,7 +109,7 @@ var/global/nttransfer_uid = 0 var/pass = reject_bad_text(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required")) if(pass != remote.server_password) error = "Incorrect Password" - return + return 1 downloaded_file = remote.provided_file.clone() remote.connected_clients.Add(src) return 1 diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 3385ac01df5..9c25971c582 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -44,8 +44,8 @@ var/global/ntnet_card_uid = 1 if(ethernet) // Computer is connected via wired connection. return 3 - //if(!ntnet_global || !ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. - // return 0 + if(!ntnet_global || !ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. + return 0 if(holder) diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl index 701a13e8aa9..7243901cbd8 100644 --- a/nano/templates/ntnet_dos.tmpl +++ b/nano/templates/ntnet_dos.tmpl @@ -40,13 +40,21 @@ {{for data.dos_strings}} {{:value.nums}}
    {{/for}} - ABORT + {{:helper.link('ABORT', null, {'action' : 'PRG_reset'})}} {{else}} ##DoS traffic generator ready. Select target device.
    - Targeted device ID: {{if data.focus}}{{:data.focus}}{{else}}None{{/if}} - {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
    +
    +
    Targeted device ID:
    +
    {{if data.focus}}{{:data.focus}}{{else}}None{{/if}}
    +
    + +
    + {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
    +
    Detected devices on network:
    - {{for data.relays}} - {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} - {{/for}} + + {{for data.relays}} +
    {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} + {{/for}} +
    {{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_downloader.tmpl b/nano/templates/ntnet_downloader.tmpl index 839347b5e06..5a60e7d0556 100644 --- a/nano/templates/ntnet_downloader.tmpl +++ b/nano/templates/ntnet_downloader.tmpl @@ -130,7 +130,7 @@
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename})}}
    {{/for}} - +
    {{/if}} {{/if}} {{/if}} From 6186350ba7c138eb43b8cd289170a3df3aecd986 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sun, 19 Feb 2017 22:28:05 -0300 Subject: [PATCH 04/22] to_chat()' --- .../computers/item/computer_components.dm | 8 ++++---- .../computers/item/computer_ui.dm | 8 ++++---- .../modular_computers/computers/item/laptop.dm | 4 ++-- .../modular_computers/file_system/program.dm | 6 +++--- .../modular_computers/hardware/_hardware.dm | 16 ++++++++-------- .../modular_computers/hardware/ai_slot.dm | 14 +++++++------- .../modular_computers/hardware/battery_module.dm | 10 +++++----- .../modular_computers/hardware/card_slot.dm | 12 ++++++------ .../modular_computers/hardware/hard_drive.dm | 6 +++--- .../modular_computers/hardware/network_card.dm | 12 ++++++------ .../modular_computers/hardware/printer.dm | 8 ++++---- .../modular_computers/hardware/recharger.dm | 2 +- 12 files changed, 53 insertions(+), 53 deletions(-) diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm index 06cd3989e30..ad1f928d145 100644 --- a/code/modules/modular_computers/computers/item/computer_components.dm +++ b/code/modules/modular_computers/computers/item/computer_components.dm @@ -3,11 +3,11 @@ return FALSE if(H.w_class > max_hardware_size) - user << "This component is too large for \the [src]!" + to_chat(user, "This component is too large for \the [src]!") return FALSE if(all_components[H.device_type]) - user << "This computer's hardware slot is already occupied by \the [all_components[H.device_type]]." + to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].") return FALSE return TRUE @@ -23,7 +23,7 @@ all_components[H.device_type] = H - user << "You install \the [H] into \the [src]." + to_chat(user, "You install \the [H] into \the [src].") H.holder = src H.on_install(src, user) @@ -35,7 +35,7 @@ all_components.Remove(H.device_type) - user << "You remove \the [H] from \the [src]." + to_chat(user, "You remove \the [H] from \the [src].") H.forceMove(get_turf(src)) H.holder = null diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 0e24c7a26f4..4fb9615b061 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -88,7 +88,7 @@ return 1 P.kill_program(forced = TRUE) - user << "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed." + to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") if("PC_runprogram") var/prog = href_list["name"] @@ -98,7 +98,7 @@ P = hard_drive.find_file_by_name(prog) if(!P || !istype(P)) // Program not found or it's not executable program. - user << "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning." + to_chat(user, "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.") return 1 P.computer = src @@ -117,11 +117,11 @@ var/obj/item/weapon/computer_hardware/processor_unit/PU = all_components[MC_CPU] if(idle_threads.len > PU.max_idle_programs) - user << "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error." + to_chat(user, "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error.") return 1 if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. - user << "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning." + to_chat(user, "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.") return 1 if(P.run_program(user)) active_program = P diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm index 911e8ac685c..7a7d27acd45 100644 --- a/code/modules/modular_computers/computers/item/laptop.dm +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -89,11 +89,11 @@ /obj/item/device/modular_computer/laptop/proc/toggle_open(mob/living/user=null) if(screen_on) - user << "You close \the [src]." + to_chat(user, "You close \the [src].") slowdown = initial(slowdown) w_class = initial(w_class) else - user << "You open \the [src]." + to_chat(user, "You open \the [src].") slowdown = slowdown_open w_class = w_class_open diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 35d067c3ba3..b2158304d36 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -51,7 +51,7 @@ /datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null) if(!(hardware_flag & usage_flags)) if(loud && computer && user) - user << "\The [computer] flashes an \"Hardware Error - Incompatible software\" warning." + to_chat(user, "\The [computer] flashes an \"Hardware Error - Incompatible software\" warning.") return 0 return 1 @@ -102,7 +102,7 @@ if(!I && !C && !D) if(loud) - user << "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning." + to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.") return 0 if(I) @@ -115,7 +115,7 @@ if(access_to_check in D.GetAccess()) return 1 if(loud) - user << "\The [computer] flashes an \"Access Denied\" warning." + to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") return 0 // This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm index e00c10e0135..4f752fdac10 100644 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -37,19 +37,19 @@ /obj/item/weapon/computer_hardware/attackby(obj/item/I, mob/living/user) // Multitool. Runs diagnostics if(istype(I, /obj/item/device/multitool)) - user << "***** DIAGNOSTICS REPORT *****" + to_chat(user, "***** DIAGNOSTICS REPORT *****") diagnostics(user) - user << "******************************" + to_chat(user, "******************************") return 1 // Cable coil. Works as repair method, but will probably require multiple applications and more cable. if(istype(I, /obj/item/stack/cable_coil)) var/obj/item/stack/S = I if(obj_integrity == max_integrity) - user << "\The [src] doesn't seem to require repairs." + to_chat(user, "\The [src] doesn't seem to require repairs.") return 1 if(S.use(1)) - user << "You patch up \the [src] with a bit of \the [I]." + to_chat(user, "You patch up \the [src] with a bit of \the [I].") obj_integrity = min(obj_integrity + 10, max_integrity) return 1 @@ -60,7 +60,7 @@ // Called on multitool click, prints diagnostic information to the user. /obj/item/weapon/computer_hardware/proc/diagnostics(var/mob/user) - user << "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]" + to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]") // Handles damage checks /obj/item/weapon/computer_hardware/proc/check_functionality() @@ -83,11 +83,11 @@ /obj/item/weapon/computer_hardware/examine(var/mob/user) . = ..() if(damage > damage_failure) - user << "It seems to be severely damaged!" + to_chat(user, "It seems to be severely damaged!") else if(damage > damage_malfunction) - user << "It seems to be damaged!" + to_chat(user, "It seems to be damaged!") else if(damage) - user << "It seems to be slightly damaged." + to_chat(user, "It seems to be slightly damaged.") // Component-side compatibility check. /obj/item/weapon/computer_hardware/proc/can_install(obj/item/device/modular_computer/M, mob/living/user = null) diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index e08517c8e48..fd2d06bef27 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -14,7 +14,7 @@ /obj/item/weapon/computer_hardware/ai_slot/examine(mob/user) ..() if(stored_card) - user << "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it" + to_chat(user, "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it") /obj/item/weapon/computer_hardware/ai_slot/on_install(obj/item/device/modular_computer/M, mob/living/user = null) M.add_verb(device_type) @@ -30,25 +30,25 @@ return FALSE if(stored_card) - user << "You try to insert \the [I] into \the [src], but the slot is occupied." + to_chat(user, "You try to insert \the [I] into \the [src], but the slot is occupied.") return FALSE if(user && !user.unEquip(I)) return FALSE I.forceMove(src) stored_card = I - user << "You insert \the [I] into \the [src]." + to_chat(user, "You insert \the [I] into \the [src].") return TRUE /obj/item/weapon/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0) if(!stored_card) - user << "There is no card in \the [src]." + to_chat(user, "There is no card in \the [src].") return FALSE if(locked && !forced) - user << "Safeties prevent you from removing the card until reconstruction is complete..." + to_chat(user, "Safeties prevent you from removing the card until reconstruction is complete...") return FALSE if(stored_card) @@ -57,7 +57,7 @@ stored_card.verb_pickup() stored_card = null - user << "You remove the card from \the [src]." + to_chat(user, "You remove the card from \the [src].") return TRUE return FALSE @@ -65,6 +65,6 @@ if(..()) return if(istype(I, /obj/item/weapon/screwdriver)) - user << "You press down on the manual eject button with \the [I]." + to_chat(user, "You press down on the manual eject button with \the [I].") try_eject(,user,1) return \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 1edb2c4df7d..64558e5f46a 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -21,11 +21,11 @@ return FALSE if(battery) - user << "You try to connect \the [I] to \the [src], but its connectors are occupied." + to_chat(user, "You try to connect \the [I] to \the [src], but its connectors are occupied.") return FALSE if(I.w_class > holder.max_hardware_size) - user << "This power cell is too large for \the [holder]!" + to_chat(user, "This power cell is too large for \the [holder]!") return FALSE if(user && !user.unEquip(I)) @@ -33,18 +33,18 @@ I.forceMove(src) battery = I - user << "You connect \the [I] to \the [src]." + to_chat(user, "You connect \the [I] to \the [src].") return TRUE /obj/item/weapon/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0) if(!battery) - user << "There is no power cell connected to \the [src]." + to_chat(user, "There is no power cell connected to \the [src].") return FALSE else battery.forceMove(get_turf(src)) - user << "You detach \the [battery] from \the [src]." + to_chat(user, "You detach \the [battery] from \the [src].") battery = null if(holder) diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index 9a08ed0445d..76781828650 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -44,7 +44,7 @@ return FALSE if(stored_card && stored_card2) - user << "You try to insert \the [I] into \the [src], but its slots are occupied." + to_chat(user, "You try to insert \the [I] into \the [src], but its slots are occupied.") return FALSE if(user && !user.unEquip(I)) return FALSE @@ -55,14 +55,14 @@ stored_card = I else stored_card2 = I - user << "You insert \the [I] into \the [src]." + to_chat(user, "You insert \the [I] into \the [src].") return TRUE /obj/item/weapon/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0) if(!stored_card && !stored_card2) - user << "There are no cards in \the [src]." + to_chat(user, "There are no cards in \the [src].") return FALSE var/ejected = 0 @@ -87,7 +87,7 @@ var/datum/computer_file/program/P = I P.event_idremoved(1, slot) - user << "You remove the card[ejected>1 ? "s" : ""] from \the [src]." + to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].") return TRUE return FALSE @@ -95,11 +95,11 @@ if(..()) return if(istype(I, /obj/item/weapon/screwdriver)) - user << "You press down on the manual eject button with \the [I]." + to_chat(user, "You press down on the manual eject button with \the [I].") try_eject(0,user) return /obj/item/weapon/computer_hardware/card_slot/examine(mob/user) ..() if(stored_card || stored_card2) - user << "There appears to be something loaded in the card slots." + to_chat(user, "There appears to be something loaded in the card slots.") diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 4a770c27be1..9339f3d3c5d 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -21,13 +21,13 @@ /obj/item/weapon/computer_hardware/hard_drive/examine(user) ..() - user << "It has [max_capacity] GQ of storage capacity." + to_chat(user, "It has [max_capacity] GQ of storage capacity.") /obj/item/weapon/computer_hardware/hard_drive/diagnostics(var/mob/user) ..() // 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space. - user << "NT-NFS File Table Status: [stored_files.len]/999" - user << "Storage capacity: [used_capacity]/[max_capacity]GQ" + to_chat(user, "NT-NFS File Table Status: [stored_files.len]/999") + to_chat(user, "Storage capacity: [used_capacity]/[max_capacity]GQ") // Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. /obj/item/weapon/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F) diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 9c25971c582..bf918a86b99 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -15,14 +15,14 @@ var/global/ntnet_card_uid = 1 /obj/item/weapon/computer_hardware/network_card/diagnostics(var/mob/user) ..() - user << "NIX Unique ID: [identification_id]" - user << "NIX User Tag: [identification_string]" - user << "Supported protocols:" - user << "511.m SFS (Subspace) - Standard Frequency Spread" + to_chat(user, "NIX Unique ID: [identification_id]") + to_chat(user, "NIX User Tag: [identification_string]") + to_chat(user, "Supported protocols:") + to_chat(user, "511.m SFS (Subspace) - Standard Frequency Spread") if(long_range) - user << "511.n WFS/HB (Subspace) - Wide Frequency Spread/High Bandiwdth" + to_chat(user, "511.n WFS/HB (Subspace) - Wide Frequency Spread/High Bandiwdth") if(ethernet) - user << "OpenEth (Physical Connection) - Physical network connection port" + to_chat(user, "OpenEth (Physical Connection) - Physical network connection port") /obj/item/weapon/computer_hardware/network_card/New(var/l) ..(l) diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm index 9595b7e5384..611e2a8765e 100644 --- a/code/modules/modular_computers/hardware/printer.dm +++ b/code/modules/modular_computers/hardware/printer.dm @@ -11,11 +11,11 @@ /obj/item/weapon/computer_hardware/printer/diagnostics(mob/living/user) ..() - user << "Paper level: [stored_paper]/[max_paper]" + to_chat(user, "Paper level: [stored_paper]/[max_paper]") /obj/item/weapon/computer_hardware/printer/examine(mob/user) ..() - user << "Paper level: [stored_paper]/[max_paper]" + to_chat(user, "Paper level: [stored_paper]/[max_paper]") /obj/item/weapon/computer_hardware/printer/proc/print_text(var/text_to_print, var/paper_title = "") @@ -41,13 +41,13 @@ /obj/item/weapon/computer_hardware/printer/try_insert(obj/item/I, mob/living/user = null) if(istype(I, /obj/item/weapon/paper)) if(stored_paper >= max_paper) - user << "You try to add \the [I] into [src], but its paper bin is full!" + to_chat(user, "You try to add \the [I] into [src], but its paper bin is full!") return FALSE if(user && !user.unEquip(I)) return FALSE - user << "You insert \the [I] into [src]'s paper recycler." + to_chat(user, "You insert \the [I] into [src]'s paper recycler.") qdel(I) stored_paper++ return TRUE diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm index cb057c2775a..d1cd1b52948 100644 --- a/code/modules/modular_computers/hardware/recharger.dm +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -57,7 +57,7 @@ /obj/item/weapon/computer_hardware/recharger/wired/can_install(obj/item/device/modular_computer/M, mob/living/user = null) if(istype(M.physical, /obj/machinery) && M.physical.anchored) return ..() - user << "\The [src] is incompatible with portable computers!" + to_chat(user, "\The [src] is incompatible with portable computers!") return 0 /obj/item/weapon/computer_hardware/recharger/wired/use_power(amount, charging=0) From aa35c4cef3eca452e5236c8e0226db54c6410a55 Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 09:13:37 -0300 Subject: [PATCH 05/22] No copypasta' --- .../computers/item/computer.dm | 58 ------------------- .../computers/item/computer_ui.dm | 58 +++++++++++++++++++ .../file_system/programs/ntdownloader.dm | 2 +- nano/layouts/layout_default.tmpl | 36 ++++++++++++ nano/templates/computer_main.tmpl | 35 ----------- nano/templates/file_manager.tmpl | 35 ----------- nano/templates/laptop_configuration.tmpl | 37 +----------- nano/templates/ntnet_chat.tmpl | 35 ----------- nano/templates/ntnet_dos.tmpl | 41 +------------ nano/templates/ntnet_downloader.tmpl | 35 ----------- nano/templates/ntnet_transfer.tmpl | 35 ----------- 11 files changed, 99 insertions(+), 308 deletions(-) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 3410cc0d06c..2505024ff4a 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -272,64 +272,6 @@ handle_power() // Handles all computer power interaction //check_update_ui_need() -// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" -/obj/item/device/modular_computer/proc/get_header_data() - var/list/data = list() - - var/obj/item/weapon/computer_hardware/battery/battery_module = all_components[MC_CELL] - var/obj/item/weapon/computer_hardware/recharger/recharger = all_components[MC_CHARGE] - - if(battery_module && battery_module.battery) - switch(battery_module.battery.percent()) - if(80 to 200) // 100 should be maximal but just in case.. - data["PC_batteryicon"] = "batt_100.gif" - if(60 to 80) - data["PC_batteryicon"] = "batt_80.gif" - if(40 to 60) - data["PC_batteryicon"] = "batt_60.gif" - if(20 to 40) - data["PC_batteryicon"] = "batt_40.gif" - if(5 to 20) - data["PC_batteryicon"] = "batt_20.gif" - else - data["PC_batteryicon"] = "batt_5.gif" - data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %" - data["PC_showbatteryicon"] = 1 - else - data["PC_batteryicon"] = "batt_5.gif" - data["PC_batterypercent"] = "N/C" - data["PC_showbatteryicon"] = battery_module ? 1 : 0 - - if(recharger && recharger.enabled && recharger.check_functionality() && recharger.use_power(0)) - data["PC_apclinkicon"] = "charging.gif" - - switch(get_ntnet_status()) - if(0) - data["PC_ntneticon"] = "sig_none.gif" - if(1) - data["PC_ntneticon"] = "sig_low.gif" - if(2) - data["PC_ntneticon"] = "sig_high.gif" - if(3) - data["PC_ntneticon"] = "sig_lan.gif" - - if(idle_threads.len) - var/list/program_headers = list() - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(!P.ui_header) - continue - program_headers.Add(list(list( - "icon" = P.ui_header - ))) - - data["PC_programheaders"] = program_headers - - data["PC_stationtime"] = worldtime2text() - data["PC_hasheader"] = 1 - data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen - return data - // Relays kill program request to currently active program. Use this to quit current program. /obj/item/device/modular_computer/proc/kill_program(forced = FALSE) if(active_program) diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 4fb9615b061..7df664a1bb4 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -51,6 +51,64 @@ return data +// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" +/obj/item/device/modular_computer/proc/get_header_data() + var/list/data = list() + + var/obj/item/weapon/computer_hardware/battery/battery_module = all_components[MC_CELL] + var/obj/item/weapon/computer_hardware/recharger/recharger = all_components[MC_CHARGE] + + if(battery_module && battery_module.battery) + switch(battery_module.battery.percent()) + if(80 to 200) // 100 should be maximal but just in case.. + data["PC_batteryicon"] = "batt_100.gif" + if(60 to 80) + data["PC_batteryicon"] = "batt_80.gif" + if(40 to 60) + data["PC_batteryicon"] = "batt_60.gif" + if(20 to 40) + data["PC_batteryicon"] = "batt_40.gif" + if(5 to 20) + data["PC_batteryicon"] = "batt_20.gif" + else + data["PC_batteryicon"] = "batt_5.gif" + data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %" + data["PC_showbatteryicon"] = 1 + else + data["PC_batteryicon"] = "batt_5.gif" + data["PC_batterypercent"] = "N/C" + data["PC_showbatteryicon"] = battery_module ? 1 : 0 + + if(recharger && recharger.enabled && recharger.check_functionality() && recharger.use_power(0)) + data["PC_apclinkicon"] = "charging.gif" + + switch(get_ntnet_status()) + if(0) + data["PC_ntneticon"] = "sig_none.gif" + if(1) + data["PC_ntneticon"] = "sig_low.gif" + if(2) + data["PC_ntneticon"] = "sig_high.gif" + if(3) + data["PC_ntneticon"] = "sig_lan.gif" + + if(idle_threads.len) + var/list/program_headers = list() + for(var/I in idle_threads) + var/datum/computer_file/program/P = I + if(!P.ui_header) + continue + program_headers.Add(list(list( + "icon" = P.ui_header + ))) + + data["PC_programheaders"] = program_headers + + data["PC_stationtime"] = worldtime2text() + data["PC_hasheader"] = 1 + data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen + return data + // Handles user's GUI input /obj/item/device/modular_computer/Topic(href, list/href_list) diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index 2a35dd7b8d8..ca4ea4eace0 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -128,7 +128,7 @@ data["downloaddesc"] = downloaded_file.filedesc data["downloadsize"] = downloaded_file.size data["downloadspeed"] = download_netspeed - data["downloadcompletion"] = round(download_completion, 0.1) + data["downloadcompletion"] = round(min(download_completion, downloaded_file.size), 0.1) else // No download running, pick file. var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD] data["disk_size"] = hard_drive.max_capacity diff --git a/nano/layouts/layout_default.tmpl b/nano/layouts/layout_default.tmpl index 1c270ee4c18..c0832fb6803 100644 --- a/nano/layouts/layout_default.tmpl +++ b/nano/layouts/layout_default.tmpl @@ -31,6 +31,42 @@
    +{{if data.PC_hasheader}} +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    +{{/if}}
    Initiating...
    diff --git a/nano/templates/computer_main.tmpl b/nano/templates/computer_main.tmpl index 6dfaa26ff66..c11be44be5c 100644 --- a/nano/templates/computer_main.tmpl +++ b/nano/templates/computer_main.tmpl @@ -1,38 +1,3 @@ -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - No program loaded. Please select program from list below.
    diff --git a/nano/templates/file_manager.tmpl b/nano/templates/file_manager.tmpl index 271bc8822b7..7cf1dd8a676 100644 --- a/nano/templates/file_manager.tmpl +++ b/nano/templates/file_manager.tmpl @@ -1,38 +1,3 @@ -
    -
    -
    - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - {{if data.error}}

    An error has occurred and this program can not continue.

    Additional information: {{:data.error}}
    diff --git a/nano/templates/laptop_configuration.tmpl b/nano/templates/laptop_configuration.tmpl index 41ab8f74e02..07c499d7158 100644 --- a/nano/templates/laptop_configuration.tmpl +++ b/nano/templates/laptop_configuration.tmpl @@ -1,38 +1,3 @@ -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - Welcome to computer configuration utility. Please consult your system administrator if you have any questions about your device.

    Power Supply

    @@ -69,7 +34,7 @@

    File System

    Used Capacity
    - {{:helper.displayBar(data.disk_used, 0, data.disk_size, (data.disk_used > data.disk_size/2) ? 'bad' : (data.disk_used > data.disk_size/4) ? 'average' : 'good')}} + {{:helper.displayBar(data.disk_used, 0, data.disk_size, (data.disk_used > data.disk_size*0.75) ? 'bad' : (data.disk_used > data.disk_size/2) ? 'average' : 'good')}}
    {{:helper.smoothRound(data.disk_used, 1)}}/{{:data.disk_size}}GQ
    diff --git a/nano/templates/ntnet_chat.tmpl b/nano/templates/ntnet_chat.tmpl index 02287508ab8..c3f2573f8e2 100644 --- a/nano/templates/ntnet_chat.tmpl +++ b/nano/templates/ntnet_chat.tmpl @@ -1,38 +1,3 @@ -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - {{if data.adminmode}}

    ADMINISTRATIVE MODE

    {{/if}} diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl index 7243901cbd8..93faa096bfe 100644 --- a/nano/templates/ntnet_dos.tmpl +++ b/nano/templates/ntnet_dos.tmpl @@ -1,38 +1,4 @@ -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - +{{:helper.syndicateMode()}} {{if data.error}} ##SYSTEM ERROR: {{:data.error}}{{:helper.link('RESET', null, {'action' : 'PRG_reset'})}} {{else data.target}} @@ -47,14 +13,13 @@
    Targeted device ID:
    {{if data.focus}}{{:data.focus}}{{else}}None{{/if}}
    -
    - {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
    + {{:helper.link('EXECUTE', null, { 'PRG_execute' : 1 })}}
    Detected devices on network:
    {{for data.relays}} -
    {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} +
    {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} {{/for}}
    {{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_downloader.tmpl b/nano/templates/ntnet_downloader.tmpl index 5a60e7d0556..3582a48bf7c 100644 --- a/nano/templates/ntnet_downloader.tmpl +++ b/nano/templates/ntnet_downloader.tmpl @@ -1,38 +1,3 @@ -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - Welcome to software download utility. Please select which software you wish to download.
    {{if data.error}}

    Download Error

    diff --git a/nano/templates/ntnet_transfer.tmpl b/nano/templates/ntnet_transfer.tmpl index 9495df39bf1..660838b94dc 100644 --- a/nano/templates/ntnet_transfer.tmpl +++ b/nano/templates/ntnet_transfer.tmpl @@ -1,38 +1,3 @@ -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    - {{if data.error}}

    An error has occurred during operation...

    From 2c105a13b403e377405eadf657ab901d07a7e40b Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 09:57:06 -0300 Subject: [PATCH 06/22] NTMonitor and Revelation Virus. --- .../programs/antagonist/revelation.dm | 77 +++++++++++++++ .../file_system/programs/ntmonitor.dm | 90 +++++++++++++++++ nano/templates/computer_main.tmpl | 2 +- nano/templates/ntnet_monitor.tmpl | 98 +++++++++++++++++++ nano/templates/revelation.tmpl | 23 +++++ paradise.dme | 2 + 6 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 code/modules/modular_computers/file_system/programs/antagonist/revelation.dm create mode 100644 code/modules/modular_computers/file_system/programs/ntmonitor.dm create mode 100644 nano/templates/ntnet_monitor.tmpl create mode 100644 nano/templates/revelation.tmpl diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm new file mode 100644 index 00000000000..62a0c102b4a --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -0,0 +1,77 @@ +/datum/computer_file/program/revelation + filename = "revelation" + filedesc = "Revelation" + program_icon_state = "hostile" + 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 + 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) + computer.visible_message("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.") + computer.enabled = 0 + computer.update_icon() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/battery/battery_module = computer.all_components[MC_CELL] + var/obj/item/weapon/computer_hardware/recharger/recharger = computer.all_components[MC_CHARGE] + qdel(hard_drive) + computer.take_damage(25, BRUTE, 0, 0) + if(battery_module && prob(25)) + qdel(battery_module) + computer.visible_message("\The [computer]'s battery explodes in rain of sparks.") + var/datum/effect/system/spark_spread/spark_system = new /datum/effect/system/spark_spread() + spark_system.set_up(5, 0, computer.loc) + spark_system.start() + + if(recharger && prob(50)) + qdel(recharger) + computer.visible_message("\The [computer]'s recharger explodes in rain of sparks.") + var/datum/effect/system/spark_spread/spark_system = new /datum/effect/system/spark_spread() + spark_system.set_up(5, 0, computer.loc) + spark_system.start() + + +/datum/computer_file/program/revelation/Topic(href, list/href_list) + if(..()) + return 1 + switch(href_list["action"]) + if("PRG_arm") + armed = !armed + if("PRG_activate") + activate() + if("PRG_obfuscate") + var/mob/living/user = usr + var/newname = sanitize(input(user, "Enter new program name: ")) + if(!newname) + return + filedesc = newname + + +/datum/computer_file/program/revelation/clone() + var/datum/computer_file/program/revelation/temp = ..() + temp.armed = armed + return temp + +/datum/computer_file/program/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "revelation.tmpl", "Revelation Virus", 575, 250) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/revelation/ui_data(mob/user) + var/list/data = get_header_data() + + data["armed"] = armed + + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm new file mode 100644 index 00000000000..969a931ac71 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm @@ -0,0 +1,90 @@ +/datum/computer_file/program/ntnetmonitor + filename = "ntmonitor" + filedesc = "NTNet Diagnostics and Monitoring" + program_icon_state = "comm_monitor" + extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes" + size = 12 + requires_ntnet = 1 + required_access = access_network //Network control is a more secure program. + available_on_ntnet = 1 + +/datum/computer_file/program/ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700) + ui.set_auto_update(1) + ui.open() + + +/datum/computer_file/program/ntnetmonitor/Topic(href, list/href_list) + if(..()) + return 1 + switch(href_list["action"]) + if("resetIDS") + . = 1 + if(ntnet_global) + ntnet_global.resetIDS() + return 1 + if("toggleIDS") + . = 1 + if(ntnet_global) + ntnet_global.toggleIDS() + return 1 + if("toggleWireless") + . = 1 + 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) + var/mob/user = usr + 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("purgelogs") + . = 1 + if(ntnet_global) + ntnet_global.purge_logs() + if("updatemaxlogs") + . = 1 + var/mob/user = usr + 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) + if("toggle_function") + . = 1 + if(!ntnet_global) + return 1 + ntnet_global.toggle_function(text2num(href_list["id"])) + +/datum/computer_file/program/ntnetmonitor/ui_data(mob/user) + if(!ntnet_global) + return + var/list/data = get_header_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"] = list() + + for(var/i in ntnet_global.logs) + data["ntnetlogs"] += list(list("entry" = i)) + data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount + + return data \ No newline at end of file diff --git a/nano/templates/computer_main.tmpl b/nano/templates/computer_main.tmpl index c11be44be5c..9aad3d58609 100644 --- a/nano/templates/computer_main.tmpl +++ b/nano/templates/computer_main.tmpl @@ -3,7 +3,7 @@ {{for data.programs}}
    {{:helper.link(value.desc, null, {'action' : 'PC_runprogram', 'name' : value.name})}} - {{:helper.link('', 'times', {'action' : 'PC_killprogram', 'name' : value.name}, value.running ? null : 'disabled')}} + {{:helper.link('', null, {'action' : 'PC_killprogram', 'name' : value.name}, value.running ? null : 'disabled')}} {{/for}}
    \ No newline at end of file diff --git a/nano/templates/ntnet_monitor.tmpl b/nano/templates/ntnet_monitor.tmpl new file mode 100644 index 00000000000..c5b66e1102e --- /dev/null +++ b/nano/templates/ntnet_monitor.tmpl @@ -0,0 +1,98 @@ +

    WIRELESS CONNECTIVITY

    +
    +
    + Active NTNet Relays: +
    +
    + {{:data.ntnetrelays}} +
    + {{if data.ntnetrelays}} +
    + System status: +
    +
    + {{:data.ntnetstatus ? "ENABLED" : "DISABLED"}} +
    +
    + Control: +
    +
    + {{:helper.link('TOGGLE', null, {'action' : 'toggleWireless'})}} +


    + Caution - Disabling wireless transmitters when using wireless device may prevent you from re-enabling them again! + {{else}} +

    Wireless coverage unavailable, no relays are connected.

    + {{/if}} +
    + +

    FIREWALL CONFIGURATION

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PROTOCOLSTATUSCONTROL
    Software Downloads{{:data.config_softwaredownload ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 1})}}
    Peer to Peer Traffic{{:data.config_peertopeer ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 2})}}
    Communication Systems{{:data.config_communication ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 3})}}
    Remote System Control{{:data.config_systemcontrol ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 4})}}
    +
    + +

    SECURITY SYSTEMS

    +{{if data.idsalarm}} +
    +
    +
    +

    NETWORK INCURSION DETECTED

    + An abnormal activity has been detected in the network. Please verify system logs for more information +
    +
    +
    +{{/if}} +
    +
    Intrusion Detection System:
    +
    {{:data.idsstatus ? 'ENABLED' : 'DISABLED'}}
    +
    +
    +
    Maximal Log Count:
    +
    {{:data.ntnetmaxlogs}}
    +
    +
    +
    Controls:
    +
    + +
    {{:helper.link('RESET IDS', null, {'action' : 'resetIDS'})}} +
    {{:helper.link('TOGGLE IDS', null, {'action' : 'toggleIDS'})}} +
    {{:helper.link('SET LOG LIMIT', null, {'action' : 'updatemaxlogs'})}} +
    {{:helper.link('PURGE LOGS', null, {'action' : 'purgelogs'})}} +
    +
    +
    +System Logs +
    +
    +
    + {{for data.ntnetlogs}} + {{:value.entry}}
    + {{/for}} +
    +
    +
    \ No newline at end of file diff --git a/nano/templates/revelation.tmpl b/nano/templates/revelation.tmpl new file mode 100644 index 00000000000..5412c440caa --- /dev/null +++ b/nano/templates/revelation.tmpl @@ -0,0 +1,23 @@ +{{:helper.syndicateMode()}} +
    +
    + Payload status: +
    +
    + {{if data.armed}} + ARMED + {{else}} + DISARMED + {{/if}} +
    +
    + Controls: +
    +
    + +
    {{:helper.link('OBFUSCATE PROGRAM NAME', 'eye-slash', {'action' : 'PRG_obfuscate'})}} +
    {{:helper.link(data.armed ? 'DISARM' : 'ARM', data.armed ? 'crosshairs' : 'info', {'action' : 'PRG_arm'})}} + {{:helper.link('ACTIVATE', 'exclamation-triangle', {'action' : 'PRG_activate'}, data.armed ? null : 'disabled', data.armed ? 'redButton' : null)}} +
    +
    +
    \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 09f8366c638..996c56dedfe 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1772,9 +1772,11 @@ #include "code\modules\modular_computers\file_system\programs\configurator.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" +#include "code\modules\modular_computers\file_system\programs\ntmonitor.dm" #include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" #include "code\modules\modular_computers\file_system\programs\nttransfer.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm" +#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm" #include "code\modules\modular_computers\hardware\_hardware.dm" #include "code\modules\modular_computers\hardware\ai_slot.dm" #include "code\modules\modular_computers\hardware\battery_module.dm" From f25de59ecdfe795d682f744d220639b8f79c180d Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 11:44:34 -0300 Subject: [PATCH 07/22] AI Restorer and Alarm Monitor. --- code/modules/alarm/alarm_handler.dm | 5 + .../file_system/programs/airestorer.dm | 138 ++++++++++++++++++ .../file_system/programs/alarm.dm | 70 +++++++++ nano/templates/ai_restorer.tmpl | 45 ++++++ nano/templates/file_manager.tmpl | 16 +- nano/templates/ntnet_dos.tmpl | 4 +- paradise.dme | 2 + 7 files changed, 270 insertions(+), 10 deletions(-) create mode 100644 code/modules/modular_computers/file_system/programs/airestorer.dm create mode 100644 code/modules/modular_computers/file_system/programs/alarm.dm create mode 100644 nano/templates/ai_restorer.tmpl diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index 9878a4b5e5e..cd09ebe4427 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -47,6 +47,11 @@ existing.clear(source) return check_alarm_cleared(existing) +/datum/alarm_handler/proc/has_major_alarms() + if(alarms && alarms.len) + return 1 + return 0 + /datum/alarm_handler/proc/major_alarms() return alarms diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm new file mode 100644 index 00000000000..0f662b427ce --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -0,0 +1,138 @@ + + +/datum/computer_file/program/aidiag + filename = "aidiag" + filedesc = "AI Maintenance Utility" + program_icon_state = "generic" + extended_desc = "This program is capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot." + size = 12 + requires_ntnet = 0 + usage_flags = PROGRAM_CONSOLE + transfer_access = access_heads + available_on_ntnet = 1 + var/restoring = FALSE + +/datum/computer_file/program/aidiag/proc/get_ai(cardcheck) + + var/obj/item/weapon/computer_hardware/ai_slot/ai_slot + + if(computer) + ai_slot = computer.all_components[MC_AI] + + if(computer && ai_slot && ai_slot.check_functionality()) + if(cardcheck == 1) + return ai_slot + if(ai_slot.enabled && ai_slot.stored_card) + if(cardcheck == 2) + return ai_slot.stored_card + if(locate(/mob/living/silicon/ai) in ai_slot.stored_card) + return locate(/mob/living/silicon/ai) in ai_slot.stored_card + + return null + +/datum/computer_file/program/aidiag/Topic(href, list/href_list) + if(..()) + return TRUE + + var/mob/living/silicon/ai/A = get_ai() + if(!A) + restoring = FALSE + + switch(href_list["action"]) + if("PRG_beginReconstruction") + if(A && A.health < 100) + restoring = TRUE + return TRUE + if("PRG_eject") + if(computer.all_components[MC_AI]) + var/obj/item/weapon/computer_hardware/ai_slot/ai_slot = computer.all_components[MC_AI] + if(ai_slot && ai_slot.stored_card) + ai_slot.try_eject(0,usr) + return TRUE + +/datum/computer_file/program/aidiag/process_tick() + ..() + if(!restoring) //Put the check here so we don't check for an ai all the time + return + var/obj/item/device/aicard/cardhold = get_ai(2) + + var/obj/item/weapon/computer_hardware/ai_slot/ai_slot = get_ai(1) + + + var/mob/living/silicon/ai/A = get_ai() + if(!A || !cardhold) + restoring = FALSE // If the AI was removed, stop the restoration sequence. + if(ai_slot) + ai_slot.locked = FALSE + return + + if(cardhold.flush) + ai_slot.locked = FALSE + restoring = FALSE + return + ai_slot.locked = TRUE + A.adjustOxyLoss(-1) + A.adjustFireLoss(-1) + A.adjustToxLoss(-1) + A.adjustBruteLoss(-1) + A.updatehealth() + if(A.health >= 0 && A.stat == DEAD) + A.stat = CONSCIOUS + A.lying = 0 + dead_mob_list -= A + living_mob_list += A + // Finished restoring + if(A.health >= 100) + ai_slot.locked = FALSE + restoring = FALSE + + return TRUE + + +/datum/computer_file/program/aidiag/ui_data(mob/user) + var/list/data = get_header_data() + var/mob/living/silicon/ai/AI + // A shortcut for getting the AI stored inside the computer. The program already does necessary checks. + AI = get_ai() + + var/obj/item/device/aicard/aicard = get_ai(2) + + if(!aicard) + data["nocard"] = TRUE + data["error"] = "Please insert an intelliCard." + else + if(!AI) + data["error"] = "No AI located" + else + var/obj/item/device/aicard/cardhold = AI.loc + if(cardhold.flush) + data["error"] = "Flush in progress" + else + data["name"] = AI.name + data["restoring"] = restoring + data["health"] = (AI.health + 100) / 2 + data["isDead"] = AI.stat == DEAD + + var/list/all_laws[0] + for(var/datum/ai_law/L in AI.laws.all_laws()) + all_laws.Add(list(list( + "index" = L.index, + "text" = L.law + ))) + + data["ai_laws"] = all_laws + + return data + +/datum/computer_file/program/aidiag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ai_restorer.tmpl", "Integrity Restorer", 600, 400) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/aidiag/kill_program(forced) + restoring = FALSE + return ..(forced) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm new file mode 100644 index 00000000000..17e0a8ad2fb --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -0,0 +1,70 @@ +/datum/computer_file/program/alarm_monitor + filename = "alarmmonitor" + filedesc = "Alarm Monitoring" + ui_header = "alarm_green.gif" + program_icon_state = "alert-green" + extended_desc = "This program provides visual interface for station's alarm system." + requires_ntnet = 1 + network_destination = "alarm monitoring network" + size = 5 + var/list/datum/alarm_handler/alarm_handlers + +/datum/computer_file/program/alarm_monitor/New() + ..() + alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm) + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon) + +/datum/computer_file/program/alarm_monitor/Destroy() + ..() + alarm_handlers.unregister(src) + qdel(alarm_handlers) + alarm_handlers = null + +/datum/computer_file/program/alarm_monitor/proc/update_icon() + for(var/datum/alarm_handler/AH in alarm_handlers) + if(AH.has_major_alarms()) + program_icon_state = "alert-red" + ui_header = "alarm_red.gif" + update_computer_icon() + return 1 + program_icon_state = "alert-green" + ui_header = "alarm_green.gif" + update_computer_icon() + return 0 + +/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/alarm_monitor/ui_data(mob/user) + var/list/data = get_header_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 + + return data \ No newline at end of file diff --git a/nano/templates/ai_restorer.tmpl b/nano/templates/ai_restorer.tmpl new file mode 100644 index 00000000000..bfb23901e19 --- /dev/null +++ b/nano/templates/ai_restorer.tmpl @@ -0,0 +1,45 @@ +{{if data.restoring}} +
    Reconstruction in progress!
    +{{/if}} + +
    +
    Inserted AI:
    +
    {{:helper.link(data.name ? data.name : "-----", 'eject', {'action' : 'PRG_eject'}, data.nocard ? 'disabled' : null)}}
    +
    + +{{if data.error}} + ERROR: {{:data.error}} +{{else}} +

    System Status

    +
    +
    +
    Current AI:
    +
    {{:data.name}}
    +
    +
    +
    Status:
    +
    {{if data.isDead}}Nonfunctional{{else}}Functional{{/if}}
    +
    +
    +
    System Integrity:
    + {{:helper.displayBar(data.health, 0, 100, (data.health > 75) ? 'good' : (health > 50) ? 'average' : 'bad')}} +
    + {{:helper.smoothRound(data.health)}}% +
    +
    +
    +
    + Active Laws: +
    +
    + + {{for data.ai_laws}} +
    {{:value.index}}: {{:value.text}} + {{/for}} +
    +
    +
    +
    +

    Operations

    + {{:helper.link('Begin Reconstruction', 'plus', {'action' : 'PRG_beginReconstruction'}, data.restoring ? 'disabled' : null)}} +{{/if}} \ No newline at end of file diff --git a/nano/templates/file_manager.tmpl b/nano/templates/file_manager.tmpl index 7cf1dd8a676..1bdb0cd95c4 100644 --- a/nano/templates/file_manager.tmpl +++ b/nano/templates/file_manager.tmpl @@ -28,12 +28,12 @@ .{{:value.type}} {{:value.size}}GQ - {{:helper.link('VIEW', null, {'action' : 'PRG_openfile', 'name' : value.name})}} - {{:helper.link('DELETE', null, {'action' : 'PRG_deletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{:helper.link('RENAME', null, {'action' : 'PRG_rename', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{:helper.link('CLONE', null, {'action' : 'PRG_clone', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('VIEW', 'eye', {'action' : 'PRG_openfile', 'name' : value.name})}} + {{:helper.link('DELETE', 'trash', {'action' : 'PRG_deletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('RENAME', 'pencil', {'action' : 'PRG_rename', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('CLONE', 'files-o', {'action' : 'PRG_clone', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} {{if data.usbconnected}} - {{:helper.link('EXPORT', null, {'action' : 'PRG_copytousb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('EXPORT', 'upload', {'action' : 'PRG_copytousb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} {{/if}} @@ -56,9 +56,9 @@ .{{:value.type}} {{:value.size}}GQ - {{:helper.link('DELETE', null, {'action' : 'PRG_usbdeletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('DELETE', 'trash', {'action' : 'PRG_usbdeletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} {{if data.usbconnected}} - {{:helper.link('IMPORT', null, {'action' : 'PRG_copyfromusb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} + {{:helper.link('IMPORT', 'download', {'action' : 'PRG_copyfromusb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} {{/if}} @@ -66,6 +66,6 @@
    {{/if}} - {{:helper.link('NEW DATA FILE', null, {'action' : 'PRG_newtextfile'})}} + {{:helper.link('NEW DATA FILE', 'file-text', {'action' : 'PRG_newtextfile'})}} {{/if}} {{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl index 93faa096bfe..db92c2e84b7 100644 --- a/nano/templates/ntnet_dos.tmpl +++ b/nano/templates/ntnet_dos.tmpl @@ -14,12 +14,12 @@
    {{if data.focus}}{{:data.focus}}{{else}}None{{/if}}
    - {{:helper.link('EXECUTE', null, { 'PRG_execute' : 1 })}}
    + {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
    Detected devices on network:
    {{for data.relays}} -
    {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} +
    {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} {{/for}}
    {{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 996c56dedfe..901804277da 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1769,6 +1769,8 @@ #include "code\modules\modular_computers\file_system\data.dm" #include "code\modules\modular_computers\file_system\program.dm" #include "code\modules\modular_computers\file_system\program_events.dm" +#include "code\modules\modular_computers\file_system\programs\airestorer.dm" +#include "code\modules\modular_computers\file_system\programs\alarm.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" From 302acdc44425ce8850a6762073d7e7067c014bfb Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 19:38:23 -0300 Subject: [PATCH 08/22] Card Mod, Power Monitor and Console Presets. --- code/datums/cache/apc.dm | 12 +- .../computers/machinery/console_presets.dm | 74 ++++ .../computers/machinery/modular_console.dm | 57 +++ .../file_system/programs/airestorer.dm | 2 - .../file_system/programs/alarm.dm | 3 +- .../file_system/programs/card.dm | 410 ++++++++++++++++++ .../file_system/programs/power_monitor.dm | 47 ++ code/modules/nano/modules/power_monitor.dm | 2 +- nano/templates/card_prog.tmpl | 277 ++++++++++++ paradise.dme | 4 + 10 files changed, 878 insertions(+), 10 deletions(-) create mode 100644 code/modules/modular_computers/computers/machinery/console_presets.dm create mode 100644 code/modules/modular_computers/computers/machinery/modular_console.dm create mode 100644 code/modules/modular_computers/file_system/programs/card.dm create mode 100644 code/modules/modular_computers/file_system/programs/power_monitor.dm create mode 100644 nano/templates/card_prog.tmpl diff --git a/code/datums/cache/apc.dm b/code/datums/cache/apc.dm index 4271886ac4b..29a2574f6e0 100644 --- a/code/datums/cache/apc.dm +++ b/code/datums/cache/apc.dm @@ -1,8 +1,8 @@ var/global/datum/repository/apc/apc_repository = new() -/datum/repository/apc/proc/apc_data(var/obj/machinery/computer/monitor/powermonitor) +/datum/repository/apc/proc/apc_data(datum/powernet/powernet) var/apcData[0] - + var/datum/cache_entry/cache_entry = cache_data if(!cache_entry) cache_entry = new/datum/cache_entry @@ -10,10 +10,10 @@ var/global/datum/repository/apc/apc_repository = new() if(world.time < cache_entry.timestamp) return cache_entry.data - - if(powermonitor && !isnull(powermonitor.powernet)) + + if(powernet) var/list/L = list() - for(var/obj/machinery/power/terminal/term in powermonitor.powernet.nodes) + for(var/obj/machinery/power/terminal/term in powernet.nodes) if(istype(term.master, /obj/machinery/power/apc)) var/obj/machinery/power/apc/A = term.master L += A @@ -24,5 +24,5 @@ var/global/datum/repository/apc/apc_repository = new() apcData[++apcData.len] = list("Name" = html_encode(A.area.name), "Equipment" = Status[A.equipment+1], "Lights" = Status[A.lighting+1], "Environment" = Status[A.environ+1], "CellPct" = A.cell ? round(A.cell.percent(),1) : "M", "CellStatus" = A.cell ? chg[A.charging+1] : "M", "Load" = round(A.lastused_total,1)) cache_entry.timestamp = world.time + 5 SECONDS - cache_entry.data = apcData + cache_entry.data = apcData return apcData \ No newline at end of file diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm new file mode 100644 index 00000000000..b2f0c5fb07e --- /dev/null +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -0,0 +1,74 @@ +/obj/machinery/modular_computer/console/preset + // Can be changed to give devices specific hardware + var/_has_id_slot = 0 + var/_has_printer = 0 + var/_has_battery = 0 + var/_has_ai = 0 + +/obj/machinery/modular_computer/console/preset/New() + . = ..() + if(!cpu) + return + cpu.install_component(new /obj/item/weapon/computer_hardware/processor_unit) + + if(_has_id_slot) + cpu.install_component(new /obj/item/weapon/computer_hardware/card_slot) + if(_has_printer) + cpu.install_component(new /obj/item/weapon/computer_hardware/printer) + if(_has_battery) + cpu.install_component(new /obj/item/weapon/computer_hardware/battery(cpu, /obj/item/weapon/stock_parts/cell/computer/super)) + if(_has_ai) + cpu.install_component(new /obj/item/weapon/computer_hardware/ai_slot) + install_programs() + +// Override in child types to install preset-specific programs. +/obj/machinery/modular_computer/console/preset/proc/install_programs() + return + + + +// ===== ENGINEERING CONSOLE ===== +/obj/machinery/modular_computer/console/preset/engineering + console_department = "Engineering" + desc = "A stationary computer. This one comes preloaded with engineering programs." + +/obj/machinery/modular_computer/console/preset/engineering/install_programs() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] + hard_drive.store_file(new/datum/computer_file/program/power_monitor()) + hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) + +// ===== RESEARCH CONSOLE ===== +/obj/machinery/modular_computer/console/preset/research + console_department = "Research" + desc = "A stationary computer. This one comes preloaded with research programs." + _has_ai = 1 + +/obj/machinery/modular_computer/console/preset/research/install_programs() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] + hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor()) + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/aidiag()) + + +// ===== COMMAND CONSOLE ===== +/obj/machinery/modular_computer/console/preset/command + console_department = "Command" + desc = "A stationary computer. This one comes preloaded with command programs." + _has_id_slot = 1 + _has_printer = 1 + +/obj/machinery/modular_computer/console/preset/command/install_programs() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/card_mod()) + +// ===== CIVILIAN CONSOLE ===== +/obj/machinery/modular_computer/console/preset/civilian + console_department = "Civilian" + desc = "A stationary computer. This one comes preloaded with generic programs." + +/obj/machinery/modular_computer/console/preset/civilian/install_programs() + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) \ No newline at end of file diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm new file mode 100644 index 00000000000..60315198ca6 --- /dev/null +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -0,0 +1,57 @@ +/obj/machinery/modular_computer/console + name = "console" + desc = "A stationary computer." + + icon = 'icons/obj/modular_console.dmi' + icon_state = "console" + icon_state_powered = "console" + icon_state_unpowered = "console-off" + screen_icon_state_menu = "menu" + hardware_flag = PROGRAM_CONSOLE + var/console_department = "" // Used in New() to set network tag according to our area. + anchored = 1 + density = 1 + base_idle_power_usage = 100 + base_active_power_usage = 500 + max_hardware_size = 4 + steel_sheet_cost = 10 + light_strength = 2 + obj_integrity = 300 + max_integrity = 300 + integrity_failure = 150 + +/obj/machinery/modular_computer/console/buildable/New() + ..() + // User-built consoles start as empty frames. + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/hard_drive/network_card = cpu.all_components[MC_NET] + var/obj/item/weapon/computer_hardware/hard_drive/recharger = cpu.all_components[MC_CHARGE] + qdel(recharger) + qdel(network_card) + qdel(hard_drive) + +/obj/machinery/modular_computer/console/New() + ..() + var/obj/item/weapon/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL] + if(battery_module) + qdel(battery_module) + + var/obj/item/weapon/computer_hardware/network_card/wired/network_card = new() + + cpu.install_component(network_card) + cpu.install_component(new /obj/item/weapon/computer_hardware/recharger/APC) + cpu.install_component(new /obj/item/weapon/computer_hardware/hard_drive/super) // Consoles generally have better HDDs due to lower space limitations + + var/area/A = get_area(src) + // Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too. + if(A && console_department) + network_card.identification_string = replacetext(replacetext(replacetext("[A.name] [console_department] Console", " ", "_"), "-", ""), "__", "_") // Replace spaces with "_" + else if(A) + network_card.identification_string = replacetext(replacetext(replacetext("[A.name] Console", " ", "_"), "-", ""), "__", "_") + else if(console_department) + network_card.identification_string = replacetext(replacetext(replacetext("[console_department] Console", " ", "_"), "-", ""), "__", "_") + else + network_card.identification_string = "Unknown Console" + if(cpu) + cpu.screen_on = 1 + update_icon() \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm index 0f662b427ce..21cfdec4972 100644 --- a/code/modules/modular_computers/file_system/programs/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -1,5 +1,3 @@ - - /datum/computer_file/program/aidiag filename = "aidiag" filedesc = "AI Maintenance Utility" diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index 17e0a8ad2fb..66e3d1b120a 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -17,7 +17,8 @@ /datum/computer_file/program/alarm_monitor/Destroy() ..() - alarm_handlers.unregister(src) + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister(src) qdel(alarm_handlers) alarm_handlers = null diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm new file mode 100644 index 00000000000..9b02391eb4b --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -0,0 +1,410 @@ +/datum/computer_file/program/card_mod + filename = "cardmod" + filedesc = "ID card modification program" + program_icon_state = "id" + extended_desc = "Program for programming employee ID cards to access parts of the station." + transfer_access = access_change_ids + requires_ntnet = 0 + size = 8 + var/is_centcom = 0 + var/mode = 0 + var/printing = 0 + + //Cooldown for closing positions in seconds + //if set to -1: No cooldown... probably a bad idea + //if set to 0: Not able to close "original" positions. You can only close positions that you have opened before + var/change_position_cooldown = 60 + //Jobs you cannot open new positions for + var/list/blacklisted = list( + /datum/job/ai, + /datum/job/cyborg, + /datum/job/captain, + /datum/job/hop, + /datum/job/hos, + /datum/job/chief_engineer, + /datum/job/rd, + /datum/job/cmo, + /datum/job/judge, + /datum/job/blueshield, + /datum/job/nanotrasenrep, + /datum/job/pilot, + /datum/job/brigdoc, + /datum/job/mechanic, + /datum/job/barber, + /datum/job/chaplain, + /datum/job/ntnavyofficer, + /datum/job/ntspecops, + /datum/job/civilian + ) + + //The scaling factor of max total positions in relation to the total amount of people on board the station in % + var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players + + //This is used to keep track of opened positions for jobs to allow instant closing + //Assoc array: "JobName" = (int) + var/list/opened_positions = list() + +/datum/computer_file/program/card_mod/proc/is_authenticated(var/mob/user) + if(user.can_admin_interact()) + return 1 + if(computer) + var/obj/item/weapon/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + if(card_slot) + var/obj/item/weapon/card/id/auth_card = card_slot.stored_card2 + if(auth_card) + return check_access(auth_card) + return 0 + +/datum/computer_file/program/card_mod/proc/check_access(obj/item/I) + if(access_change_ids in I.GetAccess()) + return 1 + return 0 + +/datum/computer_file/program/card_mod/proc/get_target_rank() + if(computer) + var/obj/item/weapon/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + if(card_slot) + var/obj/item/weapon/card/id/id_card = card_slot.stored_card + if(id_card && id_card.assignment) + return id_card.assignment + return "Unassigned" + +/datum/computer_file/program/card_mod/proc/format_job_slots() + var/list/formatted = list() + for(var/datum/job/job in job_master.occupations) + if(job_blacklisted(job)) + continue + formatted.Add(list(list( + "title" = job.title, + "current_positions" = job.current_positions, + "total_positions" = job.total_positions, + "can_open" = can_open_job(job), + "can_close" = can_close_job(job)))) + + return formatted + +/datum/computer_file/program/card_mod/proc/format_card_skins(list/card_skins) + var/list/formatted = list() + for(var/skin in card_skins) + formatted.Add(list(list( + "display_name" = get_skin_desc(skin), + "skin" = skin))) + + return formatted + + +/datum/computer_file/program/card_mod/proc/job_blacklisted(datum/job/job) + return (job.type in blacklisted) + + +//Logic check for if you can open the job +/datum/computer_file/program/card_mod/proc/can_open_job(datum/job/job) + if(job) + if(!job_blacklisted(job)) + if((job.total_positions <= player_list.len * (max_relative_positions / 100))) + var/delta = (world.time / 10) - time_last_changed_position + if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) + return 1 + return -2 + return -1 + return 0 + +//Logic check for if you can close the job +/datum/computer_file/program/card_mod/proc/can_close_job(datum/job/job) + if(job) + if(!job_blacklisted(job)) + if(job.total_positions > job.current_positions) + var/delta = (world.time / 10) - time_last_changed_position + if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) + return 1 + return -2 + return -1 + return 0 + +/datum/computer_file/program/card_mod/proc/format_jobs(list/jobs) + var/obj/item/weapon/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + if(!card_slot || !card_slot.stored_card) + return null + var/obj/item/weapon/card/id/id_card = card_slot.stored_card + 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/computer_file/program/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "card_prog.tmpl", "ID card modification program", 775, 700) + ui.open() + +/datum/computer_file/program/card_mod/Topic(href, href_list) + if(..()) + return 1 + + var/obj/item/weapon/computer_hardware/card_slot/card_slot + var/obj/item/weapon/computer_hardware/printer/printer + if(computer) + card_slot = computer.all_components[MC_CARD] + printer = computer.all_components[MC_PRINT] + if(!card_slot) + return + + var/mob/user = usr + + var/obj/item/weapon/card/id/modify = card_slot.stored_card + var/obj/item/weapon/card/id/scan = card_slot.stored_card2 + + switch(href_list["action"]) + if("PRG_modify") + if(modify) + data_core.manifest_modify(modify.registered_name, modify.assignment) + modify.name = "[modify.registered_name]'s ID Card ([modify.assignment])" + card_slot.try_eject(1, user) + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + if(!usr.drop_item()) + return + I.forceMove(computer) + card_slot.stored_card = I + + if("PRG_scan") + if(scan) + card_slot.try_eject(2, user) + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + if(!usr.drop_item()) + return + I.forceMove(computer) + card_slot.stored_card2 = I + + if("PRG_access") + if(href_list["allowed"]) + if(is_authenticated(usr)) + var/access_type = text2num(href_list["access_target"]) + var/access_allowed = text2num(href_list["allowed"]) + if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses())) + modify.access -= access_type + if(!access_allowed) + modify.access += access_type + + if("PRG_skin") + var/skin = href_list["skin_target"] + if(is_authenticated(usr) && modify && ((skin in get_station_card_skins()) || ((skin in get_centcom_card_skins()) && is_centcom))) + modify.icon_state = href_list["skin_target"] + + if("PRG_assign") + if(is_authenticated(usr) && modify) + var/t1 = href_list["assign_target"] + if(t1 == "Custom") + var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN)) + //let custom jobs function as an impromptu alt title, mainly for sechuds + if(temp_t && modify) + modify.assignment = temp_t + else + var/list/access = list() + if(is_centcom) + access = get_centcom_access(t1) + else + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(ckey(J.title) == ckey(t1)) + jobdatum = J + break + if(!jobdatum) + to_chat(usr, "\red No log exists for this job: [t1]") + return + + access = jobdatum.get_access() + + modify.access = access + modify.assignment = t1 + modify.rank = t1 + + callHook("reassign_employee", list(modify)) + + if("PRG_reg") + if(is_authenticated(usr)) + var/temp_name = reject_bad_name(href_list["reg"]) + if(temp_name) + modify.registered_name = temp_name + else + computer.visible_message("[src] buzzes rudely.") + + if("PRG_account") + if(is_authenticated(usr)) + var/account_num = text2num(href_list["account"]) + modify.associated_account_number = account_num + + if("PRG_mode") + mode = text2num(href_list["mode_target"]) + + if("PRG_print") + if(!printing && computer) + printing = 1 + playsound(computer.loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + spawn(50) + printing = 0 + nanomanager.update_uis(src) + var/title + var/content + if(mode == 2) + title = "crew manifest ([worldtime2text()])" + content = "

    Crew Manifest


    [data_core ? data_core.get_manifest(0) : ""]" + else if(modify && !mode) + title = "access report" + content = {"

    Access Report

    + Prepared By: [scan && scan.registered_name ? scan.registered_name : "Unknown"]
    + For: [modify.registered_name ? modify.registered_name : "Unregistered"]
    +
    + Assignment: [modify.assignment]
    + Account Number: #[modify.associated_account_number]
    + Blood Type: [modify.blood_type]

    + Access:
    "} + + var/first = 1 + for(var/A in modify.access) + content += "[first ? "" : ", "][get_access_desc(A)]" + first = 0 + content += "
    " + + if(content) + if(!printer.print_text(content, title)) + to_chat(user, "Hardware error: Printer was unable to print the file. It may be out of paper.") + return 1 + else + computer.visible_message("\The [computer] prints out paper.") + + if("PRG_terminate") + if(is_authenticated(usr)) + modify.assignment = "Terminated" + modify.access = list() + + callHook("terminate_employee", list(modify)) + + if("PRG_make_job_available") + // MAKE ANOTHER JOB POSITION AVAILABLE FOR LATE JOINERS + if(is_authenticated(usr)) + var/edit_job_target = href_list["job"] + var/datum/job/j = job_master.GetJob(edit_job_target) + if(!j) + return 1 + if(can_open_job(j) != 1) + return 1 + if(opened_positions[edit_job_target] >= 0) + time_last_changed_position = world.time / 10 + j.total_positions++ + opened_positions[edit_job_target]++ + + if("PRG_make_job_unavailable") + // MAKE JOB POSITION UNAVAILABLE FOR LATE JOINERS + if(is_authenticated(usr)) + var/edit_job_target = href_list["job"] + var/datum/job/j = job_master.GetJob(edit_job_target) + if(!j) + return 1 + if(can_close_job(j) != 1) + return 1 + //Allow instant closing without cooldown if a position has been opened before + if(opened_positions[edit_job_target] <= 0) + time_last_changed_position = world.time / 10 + j.total_positions-- + opened_positions[edit_job_target]-- + + if(modify) + modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") + + nanomanager.update_uis(src) + return 1 + +/datum/computer_file/program/card_mod/ui_data(mob/user) + var/list/data = get_header_data() + + var/obj/item/weapon/card/id/modify = null + var/obj/item/weapon/card/id/scan = null + + var/obj/item/weapon/computer_hardware/card_slot/card_slot + var/obj/item/weapon/computer_hardware/printer/printer + if(computer) + card_slot = computer.all_components[MC_CARD] + printer = computer.all_components[MC_PRINT] + if(card_slot) + modify = card_slot.stored_card + scan = card_slot.stored_card2 + + data["src"] = UID() + data["station_name"] = station_name() + data["mode"] = mode + data["printing"] = printing + data["printer"] = printer ? TRUE : FALSE + data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["target_name"] = modify ? modify.name : "-----" + data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" + data["target_rank"] = get_target_rank() + data["scan_name"] = scan ? scan.name : "-----" + data["authenticated"] = is_authenticated(user) + data["has_modify"] = !!modify + data["account_number"] = modify ? modify.associated_account_number : null + data["centcom_access"] = is_centcom + data["all_centcom_access"] = null + data["regions"] = null + + 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["support_jobs"] = format_jobs(support_positions) + data["civilian_jobs"] = format_jobs(civilian_positions) + data["special_jobs"] = format_jobs(whitelisted_positions) + data["centcom_jobs"] = format_jobs(get_all_centcom_jobs()) + data["card_skins"] = format_card_skins(get_station_card_skins()) + + data["job_slots"] = format_job_slots() + + var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - time_last_changed_position), 1) + var/mins = round(time_to_wait / 60) + var/seconds = time_to_wait - (60*mins) + data["cooldown_mins"] = mins + data["cooldown_secs"] = (seconds < 10) ? "0[seconds]" : seconds + + if(modify) + data["current_skin"] = modify.icon_state + + if(modify && 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 modify.access) ? 1 : 0))) + + data["all_centcom_access"] = all_centcom_access + data["all_centcom_skins"] = format_card_skins(get_centcom_card_skins()) + + else if(modify) + 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 modify.access) ? 1 : 0))) + + regions.Add(list(list( + "name" = get_region_accesses_name(i), + "accesses" = accesses))) + + data["regions"] = regions + + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/power_monitor.dm b/code/modules/modular_computers/file_system/programs/power_monitor.dm new file mode 100644 index 00000000000..0b057f7aa4f --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/power_monitor.dm @@ -0,0 +1,47 @@ +/datum/computer_file/program/power_monitor + filename = "powermonitor" + filedesc = "Power Monitoring" + program_icon_state = "power_monitor" + extended_desc = "This program connects to sensors around the station to provide information about electrical systems" + ui_header = "power_norm.gif" + transfer_access = access_engine + usage_flags = PROGRAM_CONSOLE + requires_ntnet = 0 + network_destination = "power monitoring system" + size = 9 + var/obj/structure/cable/attached + +/datum/computer_file/program/power_monitor/run_program(mob/living/user) + . = ..(user) + search() + +/datum/computer_file/program/power_monitor/process_tick() + if(!attached) + search() + +/datum/computer_file/program/power_monitor/proc/search() + var/turf/T = get_turf(computer) + attached = locate() in T + +/datum/computer_file/program/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "power_monitor.tmpl", "Alarm Monitoring", 800, 700) + ui.set_auto_update(1) + ui.open() + +/datum/computer_file/program/power_monitor/ui_data() + var/list/data = get_header_data() + + data["powermonitor"] = attached ? TRUE : FALSE + + if(attached) + var/datum/powernet/powernet = attached.get_powernet() + data["poweravail"] = powernet.avail + data["powerload"] = powernet.viewload + data["powerdemand"] = powernet.load + data["apcs"] = apc_repository.apc_data(powernet) + + return data \ No newline at end of file diff --git a/code/modules/nano/modules/power_monitor.dm b/code/modules/nano/modules/power_monitor.dm index 33e08b00e83..ef052d6a5f6 100644 --- a/code/modules/nano/modules/power_monitor.dm +++ b/code/modules/nano/modules/power_monitor.dm @@ -33,7 +33,7 @@ data["poweravail"] = powermonitor.powernet.avail data["powerload"] = powermonitor.powernet.viewload data["powerdemand"] = powermonitor.powernet.load - data["apcs"] = apc_repository.apc_data(powermonitor) + data["apcs"] = apc_repository.apc_data(powermonitor.powernet) return data diff --git a/nano/templates/card_prog.tmpl b/nano/templates/card_prog.tmpl new file mode 100644 index 00000000000..897732ef1aa --- /dev/null +++ b/nano/templates/card_prog.tmpl @@ -0,0 +1,277 @@ +{{if data.printing}} +
    The computer is currently busy.
    +
    +
    Printing...
    +
    +

    + Thank you for your patience! +

    +{{else}} + {{:helper.link('Access Modification', 'home', {'action' : 'PRG_mode', 'mode_target' : 0}, !data.mode ? 'disabled' : null)}} + {{:helper.link('Job Management', 'gear', {'action' : 'PRG_mode', 'mode_target' : 1}, data.mode == 1 ? 'disabled' : null)}} + {{:helper.link('Crew Manifest', 'folder-open', {'action' : 'PRG_mode', 'mode_target' : 2}, data.mode == 2 ? 'disabled' : null)}} + {{:helper.link('Print', 'print', {'action' : 'PRG_print'}, data.printer && (data.mode == 2 || data.has_modify && !data.mode) ? null : 'disabled')}} + + {{if data.mode == 1}} +
    +

    Job Management

    +
    +
    +
    + Authorized Identity: +
    +
    + {{:helper.link(data.scan_name, 'eject', {'action' : 'PRG_scan'})}} +
    +
    + Cooldown: +
    +
    + {{if data.cooldown_mins > 0 || data.cooldown_mins == 0 && data.cooldown_secs > 0}} + Next change in: {{:data.cooldown_mins}}:{{:data.cooldown_secs}} + {{else}} + Ready + {{/if}} +
    +
    +
    +
    + {{for data.job_slots}} +
    + {{:value.title}}: {{:value.current_positions}}/{{:value.total_positions}} + {{:helper.link('-', null, {'action' : 'PRG_make_job_unavailable', 'job' : value.title}, value.can_close == 1 && data.authenticated ? null : 'disabled')}} + {{:helper.link('+', null, {'action' : 'PRG_make_job_available', 'job' : value.title}, value.can_open == 1 && data.authenticated ? null : 'disabled')}} +
    + {{/for}} +
    + {{else data.mode == 2}} +
    +

    Crew Manifest

    +
    +
    + {{:data.manifest}} +
    + {{else}} +
    +

    Access Modification

    +
    + + {{if !data.authenticated}} + Please insert the IDs into the terminal to proceed.
    + {{/if}} + +
    +
    + Target Identity: +
    +
    + {{:helper.link(data.target_name, 'eject', {'action' : 'PRG_modify'})}} +
    +
    +
    +
    + Authorized Identity: +
    +
    + {{:helper.link(data.scan_name, 'eject', {'action' : 'PRG_scan'})}} +
    +
    +
    + + {{if data.authenticated}} + + + {{if data.has_modify}} +
    +

    Details

    +
    + +
    +
    +
    + + + Registered Name: +
    +
    + + +
    +
    +
    + +
    +
    +
    + + + Account Number: +
    +
    + + +
    +
    +
    + +
    +
    + Terminations: +
    +
    + {{:helper.link('Terminate ' + data.target_owner, 'gear', {'action' : 'PRG_terminate'}, data.target_rank == "Terminated" ? 'disabled' : null, data.target_rank == "Terminated" ? 'disabled' : 'linkDanger')}} +
    +
    + +
    +

    Assignment

    +
    + + +
    + +
    + + {{if data.centcom_access}} +
    +

    Central Command

    +
    +
    + {{for data.all_centcom_access}} + {{:helper.link(value.desc, '', {'action' : 'PRG_access', 'access_target' : value.ref, 'allowed' : value.allowed}, null, value.allowed ? 'selected' : null)}} + {{/for}} +
    +
    Card Skin
    + {{for data.all_centcom_skins}} + {{:helper.link(value.display_name, '', {'action' : 'PRG_skin', 'skin_target' : value.skin}, null,(data.current_skin == value.skin) ? 'selected' : null )}} + {{/for}} +
    +
    + {{else}} +
    +

    {{:data.station_name}}

    +
    +
    + {{for data.regions}} +
    +
    {{:value.name}}
    + {{for value.accesses :accessValue:accessKey}} +
    + {{:helper.link(accessValue.desc, '', {'action' : 'PRG_access', 'access_target' : accessValue.ref, 'allowed' : accessValue.allowed}, null, accessValue.allowed ? 'selected' : null)}} +
    + {{/for}} +
    + {{/for}} +
    +
    Card Skin
    + {{for data.card_skins}} + {{:helper.link(value.display_name, '', {'action' : 'PRG_skin', 'skin_target' : value.skin}, null,(data.current_skin == value.skin) ? 'selected' : null )}} + {{/for}} +
    +
    + {{/if}} + {{/if}} + {{/if}} + {{/if}} +{{/if}} diff --git a/paradise.dme b/paradise.dme index 901804277da..c3ea1823b25 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1764,19 +1764,23 @@ #include "code\modules\modular_computers\computers\item\processor.dm" #include "code\modules\modular_computers\computers\item\tablet.dm" #include "code\modules\modular_computers\computers\item\tablet_presets.dm" +#include "code\modules\modular_computers\computers\machinery\console_presets.dm" #include "code\modules\modular_computers\computers\machinery\modular_computer.dm" +#include "code\modules\modular_computers\computers\machinery\modular_console.dm" #include "code\modules\modular_computers\file_system\computer_file.dm" #include "code\modules\modular_computers\file_system\data.dm" #include "code\modules\modular_computers\file_system\program.dm" #include "code\modules\modular_computers\file_system\program_events.dm" #include "code\modules\modular_computers\file_system\programs\airestorer.dm" #include "code\modules\modular_computers\file_system\programs\alarm.dm" +#include "code\modules\modular_computers\file_system\programs\card.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" #include "code\modules\modular_computers\file_system\programs\ntmonitor.dm" #include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" #include "code\modules\modular_computers\file_system\programs\nttransfer.dm" +#include "code\modules\modular_computers\file_system\programs\power_monitor.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm" #include "code\modules\modular_computers\hardware\_hardware.dm" From cbee4567d9a834f7544a1ef26d8704e202620e7a Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 19:44:27 -0300 Subject: [PATCH 09/22] Map edit. --- _maps/map_files/cyberiad/cyberiad.dmm | 2 +- _maps/map_files/cyberiad/z3.dmm | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 532fcd6f9bc..892a6d83427 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -3434,7 +3434,7 @@ "bob" = (/obj/machinery/computer/shuttle/labor,/turf/simulated/floor/plasteel{dir = 10; icon_state = "blue"},/area/bridge) "boc" = (/obj/machinery/computer/communications,/turf/simulated/floor/plasteel{dir = 0; icon_state = "blue"},/area/bridge) "bod" = (/obj/machinery/computer/shuttle/mining,/turf/simulated/floor/plasteel{dir = 6; icon_state = "blue"},/area/bridge) -"boe" = (/obj/machinery/computer/card,/turf/simulated/floor/plasteel{dir = 10; icon_state = "green"},/area/bridge) +"boe" = (/obj/machinery/modular_computer/console/preset/command,/turf/simulated/floor/plasteel{dir = 10; icon_state = "green"},/area/bridge) "bof" = (/obj/machinery/computer/crew,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plasteel{dir = 0; icon_state = "green"},/area/bridge) "bog" = (/obj/machinery/computer/med_data,/turf/simulated/floor/plasteel{dir = 6; icon_state = "green"},/area/bridge) "boh" = (/obj/structure/table/reinforced,/obj/item/weapon/storage/toolbox/emergency,/obj/item/weapon/wrench,/obj/item/device/assembly/timer,/obj/item/device/assembly/signaler,/obj/item/device/assembly/signaler,/turf/simulated/floor/plasteel,/area/bridge) diff --git a/_maps/map_files/cyberiad/z3.dmm b/_maps/map_files/cyberiad/z3.dmm index 0738ff80065..417a23a276c 100644 --- a/_maps/map_files/cyberiad/z3.dmm +++ b/_maps/map_files/cyberiad/z3.dmm @@ -4,6 +4,7 @@ "ad" = (/obj/docking_port/stationary{dheight = 9; dir = 2; dwidth = 5; height = 22; id = "syndicate_z3"; name = "south of telecomms"; width = 18},/turf/space,/area/space) "ae" = (/obj/machinery/atmospherics/pipe/simple/visible{dir = 4},/obj/machinery/access_button{command = "cycle_interior"; frequency = 1381; master_tag = "telecoms_airlock"; name = "interior access button"; pixel_x = -25; pixel_y = -25; req_access_txt = "61"},/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"; tag = ""},/turf/simulated/floor/plating,/area/tcommsat/powercontrol) "af" = (/obj/machinery/access_button{command = "cycle_exterior"; frequency = 1381; master_tag = "telecoms_airlock"; name = "exterior access button"; pixel_x = 25; pixel_y = 25; req_access_txt = "61"},/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"; tag = ""},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/plating/airless,/area/tcommsat/powercontrol) +"ag" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/ntnet_relay,/turf/simulated/floor/bluegrid{icon_state = "dark"; name = "Mainframe Floor"; nitrogen = 100; oxygen = 0; temperature = 80},/area/tcommsat/chamber) "aT" = (/obj/structure/lattice,/turf/space,/area/space) "ba" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s6"; icon_state = "swall_s6"; dir = 2},/area/space) "bb" = (/turf/simulated/shuttle/wall{tag = "icon-swall14"; icon_state = "swall14"; dir = 2},/area/space) @@ -504,7 +505,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaaaaaaaaaaaaaTaaaaaaaaaTaaaaaaaaaafocefqcncpcscscseffrfsdHftfufvfwevevfxevevegfzfAfBdHfCfDfEfFflfGfHcpcnfIceePaaaaaaaaaaaTaaaaaaaaaTaaaaaaaaaaaabWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaaaaaaaaaaaaaTaaaaaaaaaTaaaaaaaaaachaacrcncpctcsctcVfrdadHevfJfKeBeBfLeVfMeBeBfNfOevdHcVfdfPfQflfRfScpcncraachaaaaaaaaaaaTaaaaaaaaaTaaaaaaaaaaaabWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaadXdXdXdXdXdXdXdXdXdXdXdXdXdXdXdXchaacrcncpctcsctcVfddadHeveBeBeBeBeBeVeBeBeBeBeBevdHcVfddafQfTfUfVcpcncraachdXdXdXdXdXdXdXdXdXdXdXdXdXdXdXdXaabWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaaeDeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEcgaacrcncpctcsctcVdSdadHevfWfXeBeBeBeJeBeBeBgagbevdHcVfddafQfQfQcpcncncraTcdeOeOeOeOeOeOeOeOeOeOeOeOeOeOeOePaTbWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaaeDeEeEeEeEeEeEeEeEeEeEeEeEeEeEeEcgaacrcncpctcsctcVdSdadHevfWfXeBeBeBageBeBeBgagbevdHcVfddafQfQfQcpcncncraTcdeOeOeOeOeOeOeOeOeOeOeOeOeOeOeOePaTbWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaTdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZaaaacrcncpctcsctcVdSdadHevevevevevevgdevevevevevevdHcVfddactcsctcpcncicmaaaTdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZaabWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWcrcncpcseecseffdcNdHdHdHdHdHdHdHgfdHdHdHdHdHdHdHeffdcNcseecscpcncrbWbWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWbWbWbWbWbWbWbWbWbWbWbWcbbWbWbWbWbWbWbWcrcncpctcsctcVggghgidldlgjgkglgmgngogpgqgjdldlgrgsfddactcsctcpcncrbWbWbWbWbWbWcbbWbWbWbWbWbWbWbWbWbWcbbWbWbWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa From 67ee6fdd418f84b3a1df8384113d2333558be116 Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 21:28:14 -0300 Subject: [PATCH 10/22] Laptop vendor, access tweaks and computer recipes. --- code/game/jobs/access.dm | 8 +- code/game/jobs/job/science.dm | 6 +- .../items/stacks/sheets/sheet_types.dm | 1 + .../modular_computers/laptop_vendor.dm | 306 ++++++++++++++++++ code/modules/research/circuitprinter.dm | 1 + .../research/designs/computer_part_designs.dm | 251 ++++++++++++++ code/modules/research/protolathe.dm | 1 + nano/templates/card_prog.tmpl | 2 +- nano/templates/computer_fabricator.tmpl | 85 +++++ paradise.dme | 3 +- 10 files changed, 656 insertions(+), 8 deletions(-) create mode 100644 code/modules/modular_computers/laptop_vendor.dm create mode 100644 code/modules/research/designs/computer_part_designs.dm create mode 100644 nano/templates/computer_fabricator.tmpl diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index e54a4ca7bf2..0fa20a621fd 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -245,7 +245,7 @@ var/const/access_trade_sol = 160 access_pilot, access_ntrep, access_magistrate, access_mineral_storeroom, access_minisat, access_network) /proc/get_all_centcom_access() - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_shuttles, access_cent_telecomms, access_cent_teleporter, access_cent_specops, access_cent_specops_commander, access_cent_blackops, access_cent_thunder, access_cent_bridge, access_cent_commander, access_network) + return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_shuttles, access_cent_telecomms, access_cent_teleporter, access_cent_specops, access_cent_specops_commander, access_cent_blackops, access_cent_thunder, access_cent_bridge, access_cent_commander) /proc/get_all_syndicate_access() return list(access_syndicate, access_syndicate_leader, access_vox) @@ -267,13 +267,13 @@ var/const/access_trade_sol = 160 if(REGION_MEDBAY) //medbay return list(access_medical, access_genetics, access_morgue, access_chemistry, access_psychiatrist, access_virology, access_surgery, access_cmo, access_paramedic) if(REGION_RESEARCH) //research - return list(access_research, access_tox, access_tox_storage, access_genetics, access_robotics, access_xenobiology, access_xenoarch, access_minisat, access_rd) + return list(access_research, access_tox, access_tox_storage, access_genetics, access_robotics, access_xenobiology, access_xenoarch, access_minisat, access_rd, access_network) if(REGION_ENGINEERING) //engineering and maintenance return list(access_construction, access_maint_tunnels, access_engine, access_engine_equip, access_external_airlocks, access_tech_storage, access_atmospherics, access_minisat, access_ce, access_mechanic) if(REGION_SUPPLY) //supply return list(access_mailsorting, access_mining, access_mining_station, access_mineral_storeroom, access_cargo, access_qm) if(REGION_COMMAND) //command - return list(access_heads, access_RC_announce, access_keycard_auth, access_change_ids, access_ai_upload, access_teleporter, access_eva, access_tcomsat, access_network, access_gateway, access_all_personal_lockers, access_heads_vault, access_blueshield, access_ntrep, access_hop, access_captain) + return list(access_heads, access_RC_announce, access_keycard_auth, access_change_ids, access_ai_upload, access_teleporter, access_eva, access_tcomsat, access_gateway, access_all_personal_lockers, access_heads_vault, access_blueshield, access_ntrep, access_hop, access_captain) if(REGION_CENTCOMM) //because why the heck not return get_all_centcom_access() + get_all_accesses() @@ -428,7 +428,7 @@ var/const/access_trade_sol = 160 if(access_tcomsat) return "Telecommunications" if(access_network) - return "Network" + return "Network Access" if(access_gateway) return "Gateway" if(access_sec_doors) diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 2e07a08b2bc..c09bf8b2e96 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -12,11 +12,13 @@ access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_tech_storage, access_teleporter, access_sec_doors, access_research, access_robotics, access_xenobiology, access_ai_upload, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_mineral_storeroom) + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, + access_minisat, access_mineral_storeroom, access_network) minimal_access = list(access_eva, access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_tech_storage, access_teleporter, access_sec_doors, access_research, access_robotics, access_xenobiology, access_ai_upload, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_maint_tunnels, access_mineral_storeroom) + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, + access_minisat, access_maint_tunnels, access_mineral_storeroom, access_network) minimal_player_age = 21 exp_requirements = 600 exp_type = EXP_TYPE_CREW diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 4e822d3ce12..04e42dfa15b 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -50,6 +50,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list( new /datum/stack_recipe/rods("metal rod", /obj/item/stack/rods, 1, 2, 60), null, new /datum/stack_recipe("computer frame", /obj/structure/computerframe, 5, time = 25, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("modular console", /obj/machinery/modular_computer/console/buildable/, 10, time = 25, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("wall girders", /obj/structure/girder, 2, time = 50, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("machine frame", /obj/machinery/constructable_frame/machine_frame, 5, time = 25, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("turret frame", /obj/machinery/porta_turret_construct, 5, time = 25, one_per_turf = 1, on_floor = 1), diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm new file mode 100644 index 00000000000..71324e005ef --- /dev/null +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -0,0 +1,306 @@ +// A vendor machine for modular computer portable devices - Laptops and Tablets + +/obj/machinery/lapvend + name = "computer vendor" + desc = "A vending machine with a built-in microfabricator, capable of dispensing various NT-branded computers." + icon = 'icons/obj/vending.dmi' + icon_state = "robotics" + layer = BELOW_OBJ_LAYER + anchored = 1 + density = 1 + + // The actual laptop/tablet + var/obj/item/device/modular_computer/laptop/fabricated_laptop = null + var/obj/item/device/modular_computer/tablet/fabricated_tablet = null + + // Utility vars + var/state = 0 // 0: Select device type, 1: Select loadout, 2: Payment, 3: Thankyou screen + var/devtype = 0 // 0: None(unselected), 1: Laptop, 2: Tablet + var/total_price = 0 // Price of currently vended device. + + // Device loadout + var/dev_cpu = 1 // 1: Default, 2: Upgraded + var/dev_battery = 1 // 1: Default, 2: Upgraded, 3: Advanced + var/dev_disk = 1 // 1: Default, 2: Upgraded, 3: Advanced + var/dev_netcard = 0 // 0: None, 1: Basic, 2: Long-Range + var/dev_apc_recharger = 0 // 0: None, 1: Standard (LAPTOP ONLY) + var/dev_printer = 0 // 0: None, 1: Standard + var/dev_card = 0 // 0: None, 1: Standard + +// Removes all traces of old order and allows you to begin configuration from scratch. +/obj/machinery/lapvend/proc/reset_order() + state = 0 + devtype = 0 + if(fabricated_laptop) + qdel(fabricated_laptop) + fabricated_laptop = null + if(fabricated_tablet) + qdel(fabricated_tablet) + fabricated_tablet = null + dev_cpu = 1 + dev_battery = 1 + dev_disk = 1 + dev_netcard = 0 + dev_apc_recharger = 0 + dev_printer = 0 + dev_card = 0 + +// Recalculates the price and optionally even fabricates the device. +/obj/machinery/lapvend/proc/fabricate_and_recalc_price(fabricate = 0) + total_price = 0 + if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles + var/obj/item/weapon/computer_hardware/battery/battery_module = null + if(fabricate) + fabricated_laptop = new /obj/item/device/modular_computer/laptop/buildable(src) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/battery) + battery_module = fabricated_laptop.all_components[MC_CELL] + total_price = 99 + switch(dev_cpu) + if(1) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + if(2) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/processor_unit) + total_price += 299 + switch(dev_battery) + if(1) // Basic(750C) + if(fabricate) + battery_module.try_insert(new /obj/item/weapon/stock_parts/cell/computer) + if(2) // Upgraded(1100C) + if(fabricate) + battery_module.try_insert(new /obj/item/weapon/stock_parts/cell/computer/advanced) + total_price += 199 + if(3) // Advanced(1500C) + if(fabricate) + battery_module.try_insert(new /obj/item/weapon/stock_parts/cell/computer/super) + total_price += 499 + switch(dev_disk) + if(1) // Basic(128GQ) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/hard_drive) + if(2) // Upgraded(256GQ) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/hard_drive/advanced) + total_price += 99 + if(3) // Advanced(512GQ) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/hard_drive/super) + total_price += 299 + switch(dev_netcard) + if(1) // Basic(Short-Range) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/network_card) + total_price += 99 + if(2) // Advanced (Long Range) + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/network_card/advanced) + total_price += 299 + if(dev_apc_recharger) + total_price += 399 + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/recharger/APC) + if(dev_printer) + total_price += 99 + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/printer/mini) + if(dev_card) + total_price += 199 + if(fabricate) + fabricated_laptop.install_component(new /obj/item/weapon/computer_hardware/card_slot) + + return total_price + else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this. + var/obj/item/weapon/computer_hardware/battery/battery_module = null + if(fabricate) + fabricated_tablet = new(src) + fabricated_tablet.install_component(new /obj/item/weapon/computer_hardware/battery) + fabricated_tablet.install_component(new /obj/item/weapon/computer_hardware/processor_unit/small) + battery_module = fabricated_tablet.all_components[MC_CELL] + total_price = 199 + switch(dev_battery) + if(1) // Basic(300C) + if(fabricate) + battery_module.try_insert(new /obj/item/weapon/stock_parts/cell/computer/nano) + if(2) // Upgraded(500C) + if(fabricate) + battery_module.try_insert(new /obj/item/weapon/stock_parts/cell/computer/micro) + total_price += 199 + if(3) // Advanced(750C) + if(fabricate) + battery_module.try_insert(new /obj/item/weapon/stock_parts/cell/computer) + total_price += 499 + switch(dev_disk) + if(1) // Basic(32GQ) + if(fabricate) + fabricated_tablet.install_component(new /obj/item/weapon/computer_hardware/hard_drive/micro) + if(2) // Upgraded(64GQ) + if(fabricate) + fabricated_tablet.install_component(new /obj/item/weapon/computer_hardware/hard_drive/small) + total_price += 99 + if(3) // Advanced(128GQ) + if(fabricate) + fabricated_tablet.install_component(new /obj/item/weapon/computer_hardware/hard_drive) + total_price += 299 + switch(dev_netcard) + if(1) // Basic(Short-Range) + if(fabricate) + fabricated_tablet.install_component(new/obj/item/weapon/computer_hardware/network_card) + total_price += 99 + if(2) // Advanced (Long Range) + if(fabricate) + fabricated_tablet.install_component(new/obj/item/weapon/computer_hardware/network_card/advanced) + total_price += 299 + if(dev_printer) + total_price += 99 + if(fabricate) + fabricated_tablet.install_component(new/obj/item/weapon/computer_hardware/printer) + if(dev_card) + total_price += 199 + if(fabricate) + fabricated_tablet.install_component(new/obj/item/weapon/computer_hardware/card_slot) + return total_price + return 0 + + +/obj/machinery/lapvend/Topic(href, href_list) + if(..()) + return 1 + + switch(href_list["action"]) + if("pick_device") + if(state) // We've already picked a device type + return 0 + devtype = text2num(href_list["pick"]) + state = 1 + fabricate_and_recalc_price(0) + return 1 + if("clean_order") + reset_order() + return 1 + if((state != 1) && devtype) // Following IFs should only be usable when in the Select Loadout mode + return 0 + switch(href_list["action"]) + if("confirm_order") + state = 2 // Wait for ID swipe for payment processing + fabricate_and_recalc_price(0) + return 1 + if("hw_cpu") + dev_cpu = text2num(href_list["cpu"]) + fabricate_and_recalc_price(0) + return 1 + if("hw_battery") + dev_battery = text2num(href_list["battery"]) + fabricate_and_recalc_price(0) + return 1 + if("hw_disk") + dev_disk = text2num(href_list["disk"]) + fabricate_and_recalc_price(0) + return 1 + if("hw_netcard") + dev_netcard = text2num(href_list["netcard"]) + fabricate_and_recalc_price(0) + return 1 + if("hw_tesla") + dev_apc_recharger = text2num(href_list["tesla"]) + fabricate_and_recalc_price(0) + return 1 + if("hw_nanoprint") + dev_printer = text2num(href_list["print"]) + fabricate_and_recalc_price(0) + return 1 + if("hw_card") + dev_card = text2num(href_list["card"]) + fabricate_and_recalc_price(0) + return 1 + return 0 + +/obj/machinery/lapvend/attack_hand(mob/user) + ui_interact(user) + +/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 400) + ui.set_auto_update(1) + ui.open() + +/obj/machinery/lapvend/ui_data(mob/user) + var/list/data[0] + data["state"] = state + if(state == 1) + data["devtype"] = devtype + data["hw_battery"] = dev_battery + data["hw_disk"] = dev_disk + data["hw_netcard"] = dev_netcard + data["hw_tesla"] = dev_apc_recharger + data["hw_nanoprint"] = dev_printer + data["hw_card"] = dev_card + data["hw_cpu"] = dev_cpu + if(state == 1 || state == 2) + data["totalprice"] = total_price + return data + +obj/machinery/lapvend/attackby(obj/item/I, mob/user) + var/obj/item/weapon/card/id/C + if(istype(I, /obj/item/weapon/card/id)) + C = I + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/PDA = I + if(PDA.id) + C = PDA.id + + if(C && istype(C) && state == 2) + if(process_payment(C, I)) + fabricate_and_recalc_price(1) + if((devtype == 1) && fabricated_laptop) + fabricated_laptop.forceMove(loc) + fabricated_laptop = null + else if((devtype == 2) && fabricated_tablet) + fabricated_tablet.forceMove(loc) + fabricated_tablet = null + atom_say("Enjoy your new product!") + state = 3 + return 1 + return 0 + return ..() + + +// Simplified payment processing, returns 1 on success. +/obj/machinery/lapvend/proc/process_payment(obj/item/weapon/card/id/I, obj/item/ID_container) + visible_message("\The [usr] swipes \the [ID_container] through \the [src].") + var/datum/money_account/customer_account = get_card_account(I) + if(!customer_account || customer_account.suspended) + atom_say("Connection error. Unable to connect to account.") + return 0 + + if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + var/attempt_pin = input("Enter pin code", "Vendor transaction") as num + customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) + + if(!customer_account) + atom_say("Unable to access account: incorrect credentials.") + return 0 + + if(total_price > customer_account.money) + atom_say("Insufficient funds in account.") + return 0 + else + var/paid = customer_account.charge(total_price, + transaction_purpose = "Purchase of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"].", + terminal_name = name, + terminal_id = name, + dest_name = vendor_account.owner_name) + + if(paid) + vendor_account.money += total_price + var/datum/transaction/T = new() + T.target_name = customer_account.owner_name + T.purpose = "Purchase of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"]" + T.amount = "[total_price]" + T.source_terminal = name + T.date = current_date_string + T.time = worldtime2text() + vendor_account.transaction_log.Add(T) + return 1 + return 0 \ No newline at end of file diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 82bf6b43989..9e411abaee3 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -19,6 +19,7 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis). var/list/categories = list( "AI Modules", "Computer Boards", + "Computer Parts", "Engineering Machinery", "Exosuit Modules", "Hydroponics Machinery", diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm new file mode 100644 index 00000000000..5108cdfeaee --- /dev/null +++ b/code/modules/research/designs/computer_part_designs.dm @@ -0,0 +1,251 @@ +//////////////////////////////////////// +///////////Computer Parts/////////////// +//////////////////////////////////////// + +/datum/design/disk/normal + name = "hard disk drive" + id = "hdd_basic" + req_tech = list("programming" = 1, "engineering" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL = 400, MAT_GLASS = 100) + build_path = /obj/item/weapon/computer_hardware/hard_drive + category = list("Computer Parts") + +/datum/design/disk/advanced + name = "advanced hard disk drive" + id = "hdd_advanced" + req_tech = list("programming" = 2, "engineering" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 800, MAT_GLASS = 200) + build_path = /obj/item/weapon/computer_hardware/hard_drive/advanced + category = list("Computer Parts") + +/datum/design/disk/super + name = "super hard disk drive" + id = "hdd_super" + req_tech = list("programming" = 3, "engineering" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 1600, MAT_GLASS = 400) + build_path = /obj/item/weapon/computer_hardware/hard_drive/super + category = list("Computer Parts") + +/datum/design/disk/cluster + name = "cluster hard disk drive" + id = "hdd_cluster" + req_tech = list("programming" = 4, "engineering" = 4) + build_type = PROTOLATHE + materials = list(MAT_METAL = 3200, MAT_GLASS = 800) + build_path = /obj/item/weapon/computer_hardware/hard_drive/cluster + category = list("Computer Parts") + +/datum/design/disk/small + name = "solid state drive" + id = "ssd_small" + req_tech = list("programming" = 2, "engineering" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 800, MAT_GLASS = 200) + build_path = /obj/item/weapon/computer_hardware/hard_drive/small + category = list("Computer Parts") + +/datum/design/disk/micro + name = "micro solid state drive" + id = "ssd_micro" + req_tech = list("programming" = 1, "engineering" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL = 400, MAT_GLASS = 100) + build_path = /obj/item/weapon/computer_hardware/hard_drive/micro + category = list("Computer Parts") + + +// Network cards +/datum/design/netcard/basic + name = "network card" + id = "netcard_basic" + req_tech = list("programming" = 2, "engineering" = 1) + build_type = IMPRINTER + materials = list(MAT_METAL = 250, MAT_GLASS = 100, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/network_card + category = list("Computer Parts") + +/datum/design/netcard/advanced + name = "advanced network card" + id = "netcard_advanced" + req_tech = list("programming" = 4, "engineering" = 2) + build_type = IMPRINTER + materials = list(MAT_METAL = 500, MAT_GLASS = 200, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/network_card/advanced + category = list("Computer Parts") + +/datum/design/netcard/wired + name = "wired network card" + id = "netcard_wired" + req_tech = list("programming" = 5, "engineering" = 3) + build_type = IMPRINTER + materials = list(MAT_METAL = 2500, MAT_GLASS = 400, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/network_card/wired + category = list("Computer Parts") + + +// Data disks +/datum/design/portabledrive/basic + name = "data disk" + id = "portadrive_basic" + req_tech = list("programming" = 1) + build_type = IMPRINTER + materials = list(MAT_GLASS = 800, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/hard_drive/portable + category = list("Computer Parts") + +/datum/design/portabledrive/advanced + name = "advanced data disk" + id = "portadrive_advanced" + req_tech = list("programming" = 2) + build_type = IMPRINTER + materials = list(MAT_GLASS = 1600, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/hard_drive/portable/advanced + category = list("Computer Parts") + +/datum/design/portabledrive/super + name = "super data disk" + id = "portadrive_super" + req_tech = list("programming" = 4) + build_type = IMPRINTER + materials = list(MAT_GLASS = 3200, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/hard_drive/portable/super + category = list("Computer Parts") + + +// Card slot +/datum/design/cardslot + name = "ID card slot" + id = "cardslot" + req_tech = list("programming" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 600) + build_path = /obj/item/weapon/computer_hardware/card_slot + category = list("Computer Parts") + +// Intellicard slot +/datum/design/aislot + name = "Intellicard slot" + id = "aislot" + req_tech = list("programming" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 600) + build_path = /obj/item/weapon/computer_hardware/ai_slot + category = list("Computer Parts") + +// Mini printer +/datum/design/miniprinter + name = "miniprinter" + id = "miniprinter" + req_tech = list("programming" = 2, "engineering" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 600) + build_path = /obj/item/weapon/computer_hardware/printer/mini + category = list("Computer Parts") + + +// APC Link +/datum/design/APClink + name = "area power connector" + id = "APClink" + req_tech = list("programming" = 2, "powerstorage" = 3, "engineering" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000) + build_path = /obj/item/weapon/computer_hardware/recharger/APC + category = list("Computer Parts") + + +// Batteries +/datum/design/battery/controller + name = "power cell controller" + id = "bat_control" + req_tech = list("powerstorage" = 1, "engineering" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL = 400) + build_path = /obj/item/weapon/computer_hardware/battery + category = list("Computer Parts") + +/datum/design/battery/normal + name = "battery module" + id = "bat_normal" + req_tech = list("powerstorage" = 1, "engineering" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL = 400) + build_path = /obj/item/weapon/stock_parts/cell/computer + category = list("Computer Parts") + +/datum/design/battery/advanced + name = "advanced battery module" + id = "bat_advanced" + req_tech = list("powerstorage" = 2, "engineering" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 800) + build_path = /obj/item/weapon/stock_parts/cell/computer/advanced + category = list("Computer Parts") + +/datum/design/battery/super + name = "super battery module" + id = "bat_super" + req_tech = list("powerstorage" = 3, "engineering" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 1600) + build_path = /obj/item/weapon/stock_parts/cell/computer/super + category = list("Computer Parts") + +/datum/design/battery/nano + name = "nano battery module" + id = "bat_nano" + req_tech = list("powerstorage" = 1, "engineering" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL = 200) + build_path = /obj/item/weapon/stock_parts/cell/computer/nano + category = list("Computer Parts") + +/datum/design/battery/micro + name = "micro battery module" + id = "bat_micro" + req_tech = list("powerstorage" = 2, "engineering" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 400) + build_path = /obj/item/weapon/stock_parts/cell/computer/micro + category = list("Computer Parts") + + +// Processor unit +/datum/design/cpu + name = "processor board" + id = "cpu_normal" + req_tech = list("programming" = 3, "engineering" = 2) + build_type = IMPRINTER + materials = list(MAT_GLASS = 1600, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/processor_unit + category = list("Computer Parts") + +/datum/design/cpu/small + name = "microprocessor" + id = "cpu_small" + req_tech = list("programming" = 2, "engineering" = 2) + build_type = IMPRINTER + materials = list(MAT_GLASS = 800, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/processor_unit/small + category = list("Computer Parts") + +/datum/design/cpu/photonic + name = "photonic processor board" + id = "pcpu_normal" + req_tech = list("programming" = 5, "engineering" = 4) + build_type = IMPRINTER + materials = list(MAT_GLASS= 6400, MAT_GOLD = 2000, "sacid" = 40) + build_path = /obj/item/weapon/computer_hardware/processor_unit/photonic + category = list("Computer Parts") + +/datum/design/cpu/photonic/small + name = "photonic microprocessor" + id = "pcpu_small" + req_tech = list("programming" = 4, "engineering" = 3) + build_type = IMPRINTER + materials = list(MAT_GLASS = 3200, MAT_GOLD = 1000, "sacid" = 20) + build_path = /obj/item/weapon/computer_hardware/processor_unit/photonic/small + category = list("Computer Parts") \ No newline at end of file diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index 81392aa99e5..2084e2a93c4 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -18,6 +18,7 @@ Note: Must be placed west/left of and R&D console to function. var/list/categories = list( "Bluespace", + "Computer Parts", "Equipment", "Janitorial", "Medical", diff --git a/nano/templates/card_prog.tmpl b/nano/templates/card_prog.tmpl index 897732ef1aa..56c913a9de9 100644 --- a/nano/templates/card_prog.tmpl +++ b/nano/templates/card_prog.tmpl @@ -1,7 +1,7 @@ {{if data.printing}}
    The computer is currently busy.
    -
    Printing...
    +
    Printing...

    Thank you for your patience! diff --git a/nano/templates/computer_fabricator.tmpl b/nano/templates/computer_fabricator.tmpl new file mode 100644 index 00000000000..d262beb4f6f --- /dev/null +++ b/nano/templates/computer_fabricator.tmpl @@ -0,0 +1,85 @@ +{{:helper.link('Clear Order', 'circle', {'action' : 'clean_order'})}}

    +Your new computer device you always dreamed of is just four steps away...


    +{{if data.state == 0}} +
    +

    Step 1: Select your device type

    + {{:helper.link('Laptop', 'laptop', {'action' : 'pick_device', 'pick' : 1})}} + {{:helper.link('LTablet', 'tablet', {'action' : 'pick_device', 'pick' : 2})}} +
    +{{else data.state == 1}} +
    +

    Step 2: Personalise your device

    + + + + + + + + + + + + + +
    Current Price: + {{:data.totalprice}}C +
    Battery: + {{:helper.link('Standard', 'bolt', {'action' : 'hw_battery', 'battery' : 1}, data.hw_battery == 1 ? "selected" : null)}} + {{:helper.link('Upgraded', 'bolt', {'action' : 'hw_battery', 'battery' : 2}, data.hw_battery == 2 ? "selected" : null)}} + {{:helper.link('Advanced', 'bolt', {'action' : 'hw_battery', 'battery' : 3}, data.hw_battery == 3 ? "selected" : null)}} +
    Hard Drive: + {{:helper.link('Standard', 'hdd-o', {'action' : 'hw_disk', 'disk' : 1}, data.hw_disk == 1 ? "selected" : null)}} + {{:helper.link('Upgraded', 'hdd-o', {'action' : 'hw_disk', 'disk' : 2}, data.hw_disk == 2 ? "selected" : null)}} + {{:helper.link('Advanced', 'hdd-o', {'action' : 'hw_disk', 'disk' : 3}, data.hw_disk == 3 ? "selected" : null)}} +
    Network Card: + {{:helper.link('None', 'times', {'action' : 'hw_netcard', 'netcard' : 0}, data.hw_netcard == 0 ? "selected" : null)}} + {{:helper.link('Standard', 'signal', {'action' : 'hw_netcard', 'netcard' : 1}, data.hw_netcard == 1 ? "selected" : null)}} + {{:helper.link('Advanced', 'signal', {'action' : 'hw_netcard', 'netcard' : 2}, data.hw_netcard == 2 ? "selected" : null)}} +
    Nano Printer: + {{:helper.link('None', 'times', {'action' : 'hw_nanoprint', 'print' : 0}, data.hw_nanoprint == 0 ? "selected" : null)}} + {{:helper.link('Standard', 'print', {'action' : 'hw_nanoprint', 'print' : 1}, data.hw_nanoprint == 1 ? "selected" : null)}} +
    Card Reader: + {{:helper.link('None', 'times', {'action' : 'hw_card', 'card' : 0}, data.hw_card == 0 ? "selected" : null)}} + {{:helper.link('Standard', 'credit-card', {'action' : 'hw_card', 'card' : 1}, data.hw_card == 1 ? "selected" : null)}} +
    + + {{if data.devtype != 2}} + + + + + +
    Processor Unit: + {{:helper.link('Standard', 'tasks', {'action' : 'hw_cpu', 'cpu' : 1}, data.hw_cpu == 1 ? "selected" : null)}} + {{:helper.link('Advanced', 'tasks', {'action' : 'hw_cpu', 'cpu' : 2}, data.hw_cpu == 2 ? "selected" : null)}} +
    Tesla Relay: + {{:helper.link('None', 'times', {'action' : 'hw_tesla', 'tesla' : 0}, data.hw_tesla == 0 ? "selected" : null)}} + {{:helper.link('Standard', 'plug', {'action' : 'hw_tesla', 'tesla' : 1}, data.hw_tesla == 1 ? "selected" : null)}} +
    + {{/if}} + + + +
    Confirm Order: + {{:helper.link('CONFIRM', 'check', {'action' : 'confirm_order'})}} +
    + +
    + Battery allows your device to operate without external utility power source. Advanced batteries increase battery life.
    + Hard Drive stores file on your device. Advanced drives can store more files, but use more power, shortening battery life.
    + Network Card allows your device to wirelessly connect to stationwide NTNet network. Basic cards are limited to on-station use, while advanced cards can operate anywhere near the station, which includes the asteroid outposts.
    + Processor Unit is critical for your device's functionality. It allows you to run programs from your hard drive. Advanced CPUs use more power, but allow you to run more programs on background at once.
    + Tesla Relay is an advanced wireless power relay that allows your device to connect to nearby area power controller to provide alternative power source. This component is currently unavailable on tablet computers due to size restrictions.
    + Nano Printer is device that allows for various paperwork manipulations, such as, scanning of documents or printing new ones. This device was certified EcoFriendlyPlus and is capable of recycling existing paper for printing purposes.
    + Card Reader adds a slot that allows you to manipulate RFID cards. Please note that this is not necessary to allow the device to read your identification, it is just necessary to manipulate other cards. +
    +{{else data.state == 2}} +

    Step 3: Payment

    + Your device is now ready for fabrication..
    + Please swipe your identification card to finish purchase.
    + Total price: {{:data.totalprice}}T +{{else data.state == 3}} +

    Step 4: Thank you for your purchase

    + Should you experience any issues with your new device, contact technical support at support@computerservice.nt +{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index c3ea1823b25..5c468a2dba6 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1218,7 +1218,6 @@ #include "code\modules\computer3\computer3_notes.dm" #include "code\modules\computer3\file.dm" #include "code\modules\computer3\laptop.dm" -#include "code\modules\computer3\lapvend.dm" #include "code\modules\computer3\networking.dm" #include "code\modules\computer3\NTOS.dm" #include "code\modules\computer3\program.dm" @@ -1754,6 +1753,7 @@ #include "code\modules\mob\new_player\poll.dm" #include "code\modules\mob\new_player\preferences_setup.dm" #include "code\modules\mob\new_player\sprite_accessories.dm" +#include "code\modules\modular_computers\laptop_vendor.dm" #include "code\modules\modular_computers\computers\item\computer.dm" #include "code\modules\modular_computers\computers\item\computer_components.dm" #include "code\modules\modular_computers\computers\item\computer_damage.dm" @@ -2012,6 +2012,7 @@ #include "code\modules\research\designs\biogenerator_designs.dm" #include "code\modules\research\designs\bluespace_designs.dm" #include "code\modules\research\designs\comp_board_designs.dm" +#include "code\modules\research\designs\computer_part_designs.dm" #include "code\modules\research\designs\equipment_designs.dm" #include "code\modules\research\designs\janitorial_designs.dm" #include "code\modules\research\designs\machine_designs.dm" From 84f71a7d0ff4cc4078570df4cd7bfeee3cb6ec5e Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 21:44:06 -0300 Subject: [PATCH 11/22] Runtime fixes. --- .../computers/item/computer.dm | 2 +- .../computers/item/computer_components.dm | 12 ++++++++---- .../modular_computers/hardware/ai_slot.dm | 19 ++++++++++++------- .../hardware/battery_module.dm | 15 ++++++++++----- .../modular_computers/hardware/card_slot.dm | 15 ++++++++++----- .../modular_computers/hardware/printer.dm | 6 ++++-- .../modular_computers/hardware/recharger.dm | 3 ++- 7 files changed, 47 insertions(+), 25 deletions(-) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 2505024ff4a..60af73bdb98 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -307,7 +307,7 @@ update_icon() -/obj/item/device/modular_computer/attackby(obj/item/weapon/W as obj, mob/user as mob) +/obj/item/device/modular_computer/attackby(obj/item/weapon/W, mob/user) // Insert items into the components for(var/h in all_components) var/obj/item/weapon/computer_hardware/H = all_components[h] diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm index ad1f928d145..09f027f2301 100644 --- a/code/modules/modular_computers/computers/item/computer_components.dm +++ b/code/modules/modular_computers/computers/item/computer_components.dm @@ -3,11 +3,13 @@ return FALSE if(H.w_class > max_hardware_size) - to_chat(user, "This component is too large for \the [src]!") + if(user) + to_chat(user, "This component is too large for \the [src]!") return FALSE if(all_components[H.device_type]) - to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].") + if(user) + to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].") return FALSE return TRUE @@ -23,7 +25,8 @@ all_components[H.device_type] = H - to_chat(user, "You install \the [H] into \the [src].") + if(user) + to_chat(user, "You install \the [H] into \the [src].") H.holder = src H.on_install(src, user) @@ -35,7 +38,8 @@ all_components.Remove(H.device_type) - to_chat(user, "You remove \the [H] from \the [src].") + if(user) + to_chat(user, "You remove \the [H] from \the [src].") H.forceMove(get_turf(src)) H.holder = null diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index fd2d06bef27..a11100534e1 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -30,25 +30,29 @@ return FALSE if(stored_card) - to_chat(user, "You try to insert \the [I] into \the [src], but the slot is occupied.") + if(user) + to_chat(user, "You try to insert \the [I] into \the [src], but the slot is occupied.") return FALSE if(user && !user.unEquip(I)) return FALSE I.forceMove(src) stored_card = I - to_chat(user, "You insert \the [I] into \the [src].") + if(user) + to_chat(user, "You insert \the [I] into \the [src].") return TRUE -/obj/item/weapon/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0) +/obj/item/weapon/computer_hardware/ai_slot/try_eject(slot=0, mob/living/user = null, forced = 0) if(!stored_card) - to_chat(user, "There is no card in \the [src].") + if(user) + to_chat(user, "There is no card in \the [src].") return FALSE if(locked && !forced) - to_chat(user, "Safeties prevent you from removing the card until reconstruction is complete...") + if(user) + to_chat(user, "Safeties prevent you from removing the card until reconstruction is complete...") return FALSE if(stored_card) @@ -57,7 +61,8 @@ stored_card.verb_pickup() stored_card = null - to_chat(user, "You remove the card from \the [src].") + if(user) + to_chat(user, "You remove the card from \the [src].") return TRUE return FALSE @@ -66,5 +71,5 @@ return if(istype(I, /obj/item/weapon/screwdriver)) to_chat(user, "You press down on the manual eject button with \the [I].") - try_eject(,user,1) + try_eject(0, user, 1) return \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 64558e5f46a..16b1552f11e 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -21,11 +21,13 @@ return FALSE if(battery) - to_chat(user, "You try to connect \the [I] to \the [src], but its connectors are occupied.") + if(user) + to_chat(user, "You try to connect \the [I] to \the [src], but its connectors are occupied.") return FALSE if(I.w_class > holder.max_hardware_size) - to_chat(user, "This power cell is too large for \the [holder]!") + if(user) + to_chat(user, "This power cell is too large for \the [holder]!") return FALSE if(user && !user.unEquip(I)) @@ -33,18 +35,21 @@ I.forceMove(src) battery = I - to_chat(user, "You connect \the [I] to \the [src].") + if(user) + to_chat(user, "You connect \the [I] to \the [src].") return TRUE /obj/item/weapon/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0) if(!battery) - to_chat(user, "There is no power cell connected to \the [src].") + if(user) + to_chat(user, "There is no power cell connected to \the [src].") return FALSE else battery.forceMove(get_turf(src)) - to_chat(user, "You detach \the [battery] from \the [src].") + if(user) + to_chat(user, "You detach \the [battery] from \the [src].") battery = null if(holder) diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index 76781828650..b310fa5be7d 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -44,7 +44,8 @@ return FALSE if(stored_card && stored_card2) - to_chat(user, "You try to insert \the [I] into \the [src], but its slots are occupied.") + if(user) + to_chat(user, "You try to insert \the [I] into \the [src], but its slots are occupied.") return FALSE if(user && !user.unEquip(I)) return FALSE @@ -55,14 +56,17 @@ stored_card = I else stored_card2 = I - to_chat(user, "You insert \the [I] into \the [src].") + + if(user) + to_chat(user, "You insert \the [I] into \the [src].") return TRUE -/obj/item/weapon/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0) +/obj/item/weapon/computer_hardware/card_slot/try_eject(slot = 0, mob/living/user = null, forced = 0) if(!stored_card && !stored_card2) - to_chat(user, "There are no cards in \the [src].") + if(user) + to_chat(user, "There are no cards in \the [src].") return FALSE var/ejected = 0 @@ -87,7 +91,8 @@ var/datum/computer_file/program/P = I P.event_idremoved(1, slot) - to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].") + if(user) + to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].") return TRUE return FALSE diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm index 611e2a8765e..c5d8ba0cc77 100644 --- a/code/modules/modular_computers/hardware/printer.dm +++ b/code/modules/modular_computers/hardware/printer.dm @@ -41,13 +41,15 @@ /obj/item/weapon/computer_hardware/printer/try_insert(obj/item/I, mob/living/user = null) if(istype(I, /obj/item/weapon/paper)) if(stored_paper >= max_paper) - to_chat(user, "You try to add \the [I] into [src], but its paper bin is full!") + if(user) + to_chat(user, "You try to add \the [I] into [src], but its paper bin is full!") return FALSE if(user && !user.unEquip(I)) return FALSE - to_chat(user, "You insert \the [I] into [src]'s paper recycler.") + if(user) + to_chat(user, "You insert \the [I] into [src]'s paper recycler.") qdel(I) stored_paper++ return TRUE diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm index d1cd1b52948..c7ab6910780 100644 --- a/code/modules/modular_computers/hardware/recharger.dm +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -57,7 +57,8 @@ /obj/item/weapon/computer_hardware/recharger/wired/can_install(obj/item/device/modular_computer/M, mob/living/user = null) if(istype(M.physical, /obj/machinery) && M.physical.anchored) return ..() - to_chat(user, "\The [src] is incompatible with portable computers!") + if(user) + to_chat(user, "\The [src] is incompatible with portable computers!") return 0 /obj/item/weapon/computer_hardware/recharger/wired/use_power(amount, charging=0) From bd27071a2b5e042ca7136270ff9df04fc9726296 Mon Sep 17 00:00:00 2001 From: davipatury Date: Mon, 20 Feb 2017 22:26:41 -0300 Subject: [PATCH 12/22] Layout and template tweaks. --- nano/layouts/layout_default.tmpl | 16 ++++++------ nano/templates/ai_restorer.tmpl | 2 +- nano/templates/ntnet_dos.tmpl | 4 +-- nano/templates/ntnet_monitor.tmpl | 42 +++++++++++++++++-------------- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/nano/layouts/layout_default.tmpl b/nano/layouts/layout_default.tmpl index c0832fb6803..812b974a1ab 100644 --- a/nano/layouts/layout_default.tmpl +++ b/nano/layouts/layout_default.tmpl @@ -57,13 +57,15 @@
    - -
    {{:helper.link('Shutdown', null, {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', null, {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', null, {'action' : 'PC_minimize'})}} - {{/if}} -
    +
    + +
    {{:helper.link('Shutdown', 'power-off', {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', 'times', {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', 'minus', {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    {{/if}} diff --git a/nano/templates/ai_restorer.tmpl b/nano/templates/ai_restorer.tmpl index bfb23901e19..eb4c20a74f8 100644 --- a/nano/templates/ai_restorer.tmpl +++ b/nano/templates/ai_restorer.tmpl @@ -22,7 +22,7 @@
    System Integrity:
    - {{:helper.displayBar(data.health, 0, 100, (data.health > 75) ? 'good' : (health > 50) ? 'average' : 'bad')}} + {{:helper.displayBar(data.health, 0, 100, (data.health > 75) ? 'good' : (data.health > 50) ? 'average' : 'bad')}}
    {{:helper.smoothRound(data.health)}}%
    diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl index db92c2e84b7..bff7bb3b9f0 100644 --- a/nano/templates/ntnet_dos.tmpl +++ b/nano/templates/ntnet_dos.tmpl @@ -6,7 +6,7 @@ {{for data.dos_strings}} {{:value.nums}}
    {{/for}} - {{:helper.link('ABORT', null, {'action' : 'PRG_reset'})}} + {{:helper.link('ABORT', 'ban', {'action' : 'PRG_reset'})}} {{else}} ##DoS traffic generator ready. Select target device.
    @@ -14,7 +14,7 @@
    {{if data.focus}}{{:data.focus}}{{else}}None{{/if}}
    - {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
    + {{:helper.link('EXECUTE', 'play', {'action' : 'PRG_execute'})}}
    Detected devices on network:
    diff --git a/nano/templates/ntnet_monitor.tmpl b/nano/templates/ntnet_monitor.tmpl index c5b66e1102e..319baa872f0 100644 --- a/nano/templates/ntnet_monitor.tmpl +++ b/nano/templates/ntnet_monitor.tmpl @@ -7,18 +7,22 @@ {{:data.ntnetrelays}} {{if data.ntnetrelays}} -
    - System status: +
    +
    + System status: +
    +
    + {{:data.ntnetstatus ? "ENABLED" : "DISABLED"}} +
    -
    - {{:data.ntnetstatus ? "ENABLED" : "DISABLED"}} -
    -
    - Control: +
    +
    + Control: +
    +
    + {{:helper.link('TOGGLE', null, {'action' : 'toggleWireless'})}} +
    -
    - {{:helper.link('TOGGLE', null, {'action' : 'toggleWireless'})}} -


    Caution - Disabling wireless transmitters when using wireless device may prevent you from re-enabling them again! {{else}}

    Wireless coverage unavailable, no relays are connected.

    @@ -27,7 +31,7 @@

    FIREWALL CONFIGURATION

    -
    +
    @@ -35,23 +39,23 @@ - - + + - - + + - - + + - - + +
    PROTOCOL STATUS
    Software Downloads{{:data.config_softwaredownload ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 1})}}
    {{:data.config_softwaredownload ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 1})}}
    Peer to Peer Traffic{{:data.config_peertopeer ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 2})}}
    {{:data.config_peertopeer ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 2})}}
    Communication Systems{{:data.config_communication ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 3})}}
    {{:data.config_communication ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 3})}}
    Remote System Control{{:data.config_systemcontrol ? 'ENABLED' : 'DISABLED'}}{{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 4})}}
    {{:data.config_systemcontrol ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 4})}}
    From 9eadc35e354be60ce8da7685955d6fd1b3d01630 Mon Sep 17 00:00:00 2001 From: davipatury Date: Tue, 21 Feb 2017 12:37:28 -0300 Subject: [PATCH 13/22] Comms program. --- code/_globalvars/lists/objects.dm | 2 +- code/controllers/Processes/shuttles.dm | 2 + .../gamemodes/malfunction/Malf_Modules.dm | 4 +- .../game/machinery/computer/communications.dm | 61 ++- .../computers/machinery/console_presets.dm | 1 + .../file_system/programs/comms.dm | 401 ++++++++++++++++++ .../modular_computers/hardware/hard_drive.dm | 2 + .../security_levels/security levels.dm | 24 +- nano/templates/comm_program.tmpl | 135 ++++++ paradise.dme | 1 + 10 files changed, 578 insertions(+), 55 deletions(-) create mode 100644 code/modules/modular_computers/file_system/programs/comms.dm create mode 100644 nano/templates/comm_program.tmpl diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 41588a7b07a..f4fad058068 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -30,7 +30,7 @@ var/global/list/deliverybeacons = list() //list of all MULEbot delivery beacon var/global/list/deliverybeacontags = list() //list of all tags associated with delivery beacons. var/global/list/beacons = list() -var/global/list/shuttle_caller_list = list() //list of all communication consoles and AIs, for automatic shuttle calls when there are none. +var/global/list/shuttle_caller_list = list() //list of all communication consoles, comms consoles circuit and AIs, for automatic shuttle calls when there are none. var/global/list/tracked_implants = list() //list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented... var/global/list/pinpointer_list = list() //list of all pinpointers. Used to change stuff they are pointing to all at once. var/global/list/abductor_equipment = list() //list of all abductor equipment diff --git a/code/controllers/Processes/shuttles.dm b/code/controllers/Processes/shuttles.dm index 9c01974a1a9..b6b078323e9 100644 --- a/code/controllers/Processes/shuttles.dm +++ b/code/controllers/Processes/shuttles.dm @@ -182,6 +182,8 @@ var/const/CALL_SHUTTLE_REASON_LENGTH = 12 var/obj/machinery/computer/communications/C = thing if(C.stat & BROKEN) continue + else if(istype(thing, /datum/computer_file/program/comm) || istype(thing, /obj/item/weapon/circuitboard/communications)) + continue var/turf/T = get_turf(thing) if(T && is_station_level(T.z)) diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index a41f86020d5..e644af03f3c 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -163,9 +163,7 @@ D.hostile_lockdown(src) addtimer(D, "disable_lockdown", 900) - var/obj/machinery/computer/communications/C = locate() in machines - if(C) - C.post_status("alert", "lockdown") + post_status("alert", "lockdown") verbs -= /mob/living/silicon/ai/proc/lockdown minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 6087bc68d08..2b3d461da4c 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -69,8 +69,8 @@ feedback_inc("alert_comms_green",1) if(SEC_LEVEL_BLUE) feedback_inc("alert_comms_blue",1) - tmp_alertlevel = 0 - + tmp_alertlevel = 0 + /obj/machinery/computer/communications/Topic(href, href_list) if(..(href, href_list)) return 1 @@ -196,9 +196,9 @@ var/text = messagetext[id] messagetitle.Remove(title) messagetext.Remove(text) - if(currmsg == id) + if(currmsg == id) currmsg = 0 - if(aicurrmsg == id) + if(aicurrmsg == id) aicurrmsg = 0 setMenuState(usr,COMM_SCREEN_MESSAGES) @@ -210,11 +210,11 @@ display_type=href_list["statdisp"] switch(display_type) if("message") - post_status("message", stat_msg1, stat_msg2) + post_status("message", stat_msg1, stat_msg2, usr) if("alert") - post_status("alert", href_list["alert"]) + post_status("alert", href_list["alert"], user = usr) else - post_status(href_list["statdisp"]) + post_status(href_list["statdisp"], user = usr) setMenuState(usr,COMM_SCREEN_STAT) if("setmsg1") @@ -378,7 +378,7 @@ list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), //SEC_LEVEL_RED = list("name"="Red"), ) - + var/list/msg_data = list() for(var/i = 1; i <= messagetext.len; i++) msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) @@ -499,7 +499,7 @@ message_admins("[key_name_admin(user)] has recalled the shuttle - [formatJumpTo(user)].", 1) return -/obj/machinery/computer/communications/proc/post_status(var/command, var/data1, var/data2) +/proc/post_status(command, data1, data2, mob/user = null) var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) @@ -514,7 +514,7 @@ if("message") status_signal.data["msg1"] = data1 status_signal.data["msg2"] = data2 - log_admin("STATUS: [src.fingerprintslast] set status screen message with [src]: [data1] [data2]") + log_admin("STATUS: [user] set status screen message with [src]: [data1] [data2]") //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") if("alert") status_signal.data["picture_state"] = data1 @@ -527,35 +527,30 @@ shuttle_master.autoEvac() return ..() +/obj/item/weapon/circuitboard/communications/New() + shuttle_caller_list += src + ..() + /obj/item/weapon/circuitboard/communications/Destroy() - - for(var/obj/machinery/computer/communications/commconsole in world) - if(istype(commconsole.loc,/turf)) - return ..() - - for(var/obj/item/weapon/circuitboard/communications/commboard in world) - if((istype(commboard.loc,/turf) || istype(commboard.loc,/obj/item/weapon/storage)) && commboard != src) - return ..() - - for(var/mob/living/silicon/ai/shuttlecaller in player_list) - if(!shuttlecaller.stat && shuttlecaller.client && istype(shuttlecaller.loc,/turf)) - return ..() - - if(GAMEMODE_IS_REVOLUTION || sent_strike_team) - return ..() - - shuttle_master.emergency.request(null, 0.3, null, "All communication consoles, boards, and AI's have been destroyed.") - log_game("All the AI's, communication consoles and boards are destroyed. Shuttle called.") - message_admins("All the AI's, communication consoles and boards are destroyed. Shuttle called.", 1) - + shuttle_caller_list -= src + shuttle_master.autoEvac() return ..() - + /proc/print_command_report(text = "", title = "Central Command Update") - for(var/obj/machinery/computer/communications/C in machines) + for(var/obj/machinery/computer/communications/C in shuttle_caller_list) if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(C.loc) P.name = "paper- '[title]'" P.info = text + P.update_icon() C.messagetitle.Add("[title]") C.messagetext.Add(text) - P.update_icon() \ No newline at end of file + for(var/datum/computer_file/program/comm/P in shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) + if(P.computer) + var/obj/item/weapon/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) \ No newline at end of file diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index b2f0c5fb07e..3f9d3de11ca 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -62,6 +62,7 @@ var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) hard_drive.store_file(new/datum/computer_file/program/card_mod()) + hard_drive.store_file(new/datum/computer_file/program/comm()) // ===== CIVILIAN CONSOLE ===== /obj/machinery/modular_computer/console/preset/civilian diff --git a/code/modules/modular_computers/file_system/programs/comms.dm b/code/modules/modular_computers/file_system/programs/comms.dm new file mode 100644 index 00000000000..52b8a154570 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/comms.dm @@ -0,0 +1,401 @@ +/datum/computer_file/program/comm + filename = "comm" + filedesc = "Command and communications program." + program_icon_state = "comm" + extended_desc = "Used to command and control the station. 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 = "station long-range communication array" + + var/authenticated = COMM_AUTHENTICATION_NONE + var/list/messagetitle = list() + var/list/messagetext = list() + var/currmsg = 0 + var/aicurrmsg = 0 + var/menu_state = COMM_SCREEN_MAIN + var/ai_menu_state = COMM_SCREEN_MAIN + var/message_cooldown = 0 + var/centcomm_message_cooldown = 0 + var/tmp_alertlevel = 0 + + var/status_display_freq = "1435" + var/stat_msg1 + var/stat_msg2 + var/display_type="blank" + + var/datum/announcement/priority/crew_announcement = new + +/datum/computer_file/program/comm/New() + shuttle_caller_list += src + ..() + crew_announcement.newscast = 0 + +/datum/computer_file/program/comm/Destroy() + shuttle_caller_list -= src + shuttle_master.autoEvac() + return ..() + +/datum/computer_file/program/comm/proc/is_authenticated(mob/user, loud = 1) + if(authenticated == COMM_AUTHENTICATION_MAX) + return COMM_AUTHENTICATION_MAX + else if(user.can_admin_interact()) + return COMM_AUTHENTICATION_MAX + else if(authenticated) + return COMM_AUTHENTICATION_MIN + else + if(loud) + to_chat(user, "Access denied.") + return COMM_AUTHENTICATION_NONE + +/datum/computer_file/program/comm/proc/change_security_level(mob/user, new_level) + tmp_alertlevel = new_level + var/old_level = security_level + if(!tmp_alertlevel) + tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel < SEC_LEVEL_GREEN) + tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel > SEC_LEVEL_BLUE) + tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this + set_security_level(tmp_alertlevel) + if(security_level != old_level) + log_game("[key_name(user)] has changed the security level to [get_security_level()].") + message_admins("[key_name_admin(user)] 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_BLUE) + feedback_inc("alert_comms_blue", 1) + tmp_alertlevel = 0 + +/datum/computer_file/program/comm/proc/setCurrentMessage(mob/user, value) + if(isAI(user) || isrobot(user)) + aicurrmsg = value + else + currmsg = value + +/datum/computer_file/program/comm/proc/getCurrentMessage(mob/user) + if(isAI(user) || isrobot(user)) + return aicurrmsg + else + return currmsg + +/datum/computer_file/program/comm/proc/setMenuState(mob/user, value) + if(isAI(user) || isrobot(user)) + ai_menu_state=value + else + menu_state=value + +/datum/computer_file/program/comm/proc/getMenuState(mob/user) + if(isAI(user) || isrobot(user)) + return ai_menu_state + else + return menu_state + +/datum/computer_file/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) + ui = nanomanager.try_update_ui(user, src, ui_key, ui) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "comm_program.tmpl", "Command and communications program", 575, 500) + ui.open() + +/datum/computer_file/program/comm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/list/data = get_header_data() + data["is_ai"] = isAI(user) || isrobot(user) + data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state + data["emagged"] = computer ? computer.emagged : null + data["authenticated"] = is_authenticated(user, 0) + data["screen"] = getMenuState(usr) + + data["stat_display"] = list( + "type" = display_type, + "line_1" = (stat_msg1 ? stat_msg1 : "-----"), + "line_2" = (stat_msg2 ? stat_msg2 : "-----"), + + "presets" = list( + list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), + list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), + list("name" = "message", "label" = "Message", "desc" = "A custom message.") + ), + + "alerts"=list( + list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), + list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), + list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), + list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), + ) + ) + + data["security_level"] = security_level + data["str_security_level"] = capitalize(get_security_level()) + data["levels"] = list( + list("id" = SEC_LEVEL_GREEN, "name" = "Green"), + list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), + ) + + var/list/msg_data = list() + for(var/i = 1; i <= messagetext.len; i++) + msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) + + data["messages"] = msg_data + if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) + data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] + data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] + + data["lastCallLoc"] = shuttle_master.emergencyLastCallLoc ? format_text(shuttle_master.emergencyLastCallLoc.name) : null + + var/shuttle[0] + switch(shuttle_master.emergency.mode) + if(SHUTTLE_IDLE, SHUTTLE_RECALL) + shuttle["callStatus"] = 2 //#define + else + shuttle["callStatus"] = 1 + if(shuttle_master.emergency.mode == SHUTTLE_CALL) + var/timeleft = shuttle_master.emergency.timeLeft() + shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + + data["shuttle"] = shuttle + + if(trade_dockrequest_timelimit > world.time) + data["dock_request"] = 1 + else + data["dock_request"] = 0 + + return data + +/datum/computer_file/program/comm/Topic(href, href_list) + if(..()) + return 1 + + var/turf/T = get_turf(computer) + if(!is_secure_level(T.z)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return 1 + + if(href_list["PRG_login"]) + if(!ishuman(usr)) + to_chat(usr, "Access denied.") + return + + var/list/access = usr.get_access() + if(access_heads in access) + authenticated = COMM_AUTHENTICATION_MIN + + if(access_captain in access) + authenticated = COMM_AUTHENTICATION_MAX + var/mob/living/carbon/human/H = usr + var/obj/item/weapon/card/id = H.get_idcard(TRUE) + if(istype(id)) + crew_announcement.announcer = GetNameAndAssignmentFromId(id) + + nanomanager.update_uis(src) + return 1 + + if(href_list["PRG_logout"]) + authenticated = COMM_AUTHENTICATION_NONE + crew_announcement.announcer = "" + setMenuState(usr, COMM_SCREEN_MAIN) + nanomanager.update_uis(src) + return 1 + + if(is_authenticated(usr)) + switch(href_list["PRG_operation"]) + if("main") + setMenuState(usr, COMM_SCREEN_MAIN) + + if("changeseclevel") + setMenuState(usr,COMM_SCREEN_SECLEVEL) + + if("newalertlevel") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from changing the alert level.") + return 1 + else if(usr.can_admin_interact()) + change_security_level(usr, text2num(href_list["level"])) + return 1 + else if(!ishuman(usr)) + to_chat(usr, "Security measures prevent you from changing the alert level.") + return 1 + + var/mob/living/carbon/human/L = usr + var/obj/item/card = L.get_active_hand() + var/obj/item/weapon/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/pda = I + I = pda.id + if(I && istype(I)) + if(access_captain in I.access) + change_security_level(usr, text2num(href_list["level"])) + else + to_chat(usr, "You are not authorized to do this.") + setMenuState(usr, COMM_SCREEN_MAIN) + else + to_chat(usr, "You need to swipe your ID.") + + if("announce") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(message_cooldown) + to_chat(usr, "Please allow at least one minute to pass between announcements.") + nanomanager.update_uis(src) + return 1 + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") + if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + nanomanager.update_uis(src) + return 1 + crew_announcement.Announce(input) + message_cooldown = 1 + spawn(600)//One minute cooldown + message_cooldown = 0 + + if("callshuttle") + var/input = input(usr, "Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") as text|null + if(!input || ..() || !is_authenticated(usr)) + nanomanager.update_uis(src) + return 1 + + call_shuttle_proc(usr, input) + if(shuttle_master.emergency.timer) + post_status("shuttle") + setMenuState(usr, COMM_SCREEN_MAIN) + + if("cancelshuttle") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from recalling the shuttle.") + nanomanager.update_uis(src) + return 1 + var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") + if(response == "Yes") + cancel_call_proc(usr) + if(shuttle_master.emergency.timer) + post_status("shuttle") + setMenuState(usr, COMM_SCREEN_MAIN) + + if("messagelist") + currmsg = 0 + if(href_list["msgid"]) + setCurrentMessage(usr, text2num(href_list["msgid"])) + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("delmessage") + if(href_list["msgid"]) + currmsg = text2num(href_list["msgid"]) + var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") + if(response == "Yes") + if(currmsg) + var/id = getCurrentMessage() + var/title = messagetitle[id] + var/text = messagetext[id] + messagetitle.Remove(title) + messagetext.Remove(text) + if(currmsg == id) + currmsg = 0 + if(aicurrmsg == id) + aicurrmsg = 0 + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("status") + setMenuState(usr,COMM_SCREEN_STAT) + + // Status display stuff + if("setstat") + display_type=href_list["statdisp"] + switch(display_type) + if("message") + post_status("message", stat_msg1, stat_msg2, usr) + if("alert") + post_status("alert", href_list["alert"], user = usr) + else + post_status(href_list["statdisp"], user = usr) + setMenuState(usr, COMM_SCREEN_STAT) + + if("setmsg1") + stat_msg1 = input("Line 1", "Enter Message Text", stat_msg1) as text|null + setMenuState(usr, COMM_SCREEN_STAT) + + if("setmsg2") + stat_msg2 = input("Line 2", "Enter Message Text", stat_msg2) as text|null + setMenuState(usr, COMM_SCREEN_STAT) + + if("nukerequest") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + nanomanager.update_uis(src) + return 1 + var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") as text|null + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + nanomanager.update_uis(src) + return 1 + Nuke_request(input, usr) + to_chat(usr, "Request sent.") + log_say("[key_name(usr)] has requested the nuclear codes from Centcomm") + priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("MessageCentcomm") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + nanomanager.update_uis(src) + return 1 + var/input = stripped_input(usr, "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.", "To abort, send an empty message.", "") as text|null + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + nanomanager.update_uis(src) + return 1 + Centcomm_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_say("[key_name(usr)] has made a Centcomm announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + // OMG SYNDICATE ...LETTERHEAD + if("MessageSyndicate") + if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (computer && computer.emagged)) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + nanomanager.update_uis(src) + return 1 + var/input = stripped_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.", "To abort, send an empty message.", "") as text|null + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + nanomanager.update_uis(src) + return 1 + Syndicate_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_say("[key_name(usr)] has made a Syndicate announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestartNanoMob") + if(mob_hunt_server) + if(mob_hunt_server.manual_reboot()) + var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", + "Capturing all of them", "Fixing minor text issues", "Being the very best", + "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") + to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") + else + to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") + else + to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") + + if("AcceptDocking") + to_chat(usr, "Docking request accepted!") + trade_dock_timelimit = world.time + 1200 + trade_dockrequest_timelimit = 0 + event_announcement.Announce("Docking request for trading ship approved, please dock at port bay 4.", "Docking Request") + if("DenyDocking") + to_chat(usr, "Docking requeset denied!") + trade_dock_timelimit = 0 + trade_dockrequest_timelimit = 0 + event_announcement.Announce("Docking request for trading ship denied.", "Docking request") + + nanomanager.update_uis(src) + return 1 \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 9339f3d3c5d..a92d2e4802d 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -118,6 +118,8 @@ return null /obj/item/weapon/computer_hardware/hard_drive/Destroy() + for(var/file in stored_files) + qdel(file) stored_files = null return ..() diff --git a/code/modules/security_levels/security levels.dm b/code/modules/security_levels/security levels.dm index bfa4f7449b6..0d25330177c 100644 --- a/code/modules/security_levels/security levels.dm +++ b/code/modules/security_levels/security levels.dm @@ -32,9 +32,7 @@ security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.") security_level = SEC_LEVEL_GREEN - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "outline") + post_status("alert", "outline") for(var/obj/machinery/firealarm/FA in world) if(is_station_contact(FA.z)) @@ -48,9 +46,7 @@ security_announcement_down.Announce("The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed.","Attention! Security level lowered to blue.") security_level = SEC_LEVEL_BLUE - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "outline") + post_status("alert", "outline") for(var/obj/machinery/firealarm/FA in world) if(is_station_contact(FA.z)) @@ -69,9 +65,7 @@ R.locked = 0 R.update_icon() - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "redalert") + post_status("alert", "redalert") for(var/obj/machinery/firealarm/FA in world) if(is_station_contact(FA.z)) @@ -95,9 +89,7 @@ H.locked = 0 H.update_icon() - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "gammaalert") + post_status("alert", "gammaalert") for(var/obj/machinery/firealarm/FA in world) if(is_station_contact(FA.z)) @@ -109,9 +101,7 @@ security_announcement_up.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.","Attention! Epsilon security level activated!") security_level = SEC_LEVEL_EPSILON - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "epsilonalert") + post_status("alert", "epsilonalert") for(var/obj/machinery/firealarm/FA in world) if(is_station_contact(FA.z)) @@ -122,9 +112,7 @@ security_announcement_up.Announce("The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.","Attention! Delta security level reached!") security_level = SEC_LEVEL_DELTA - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "deltaalert") + post_status("alert", "deltaalert") for(var/obj/machinery/firealarm/FA in world) if(is_station_contact(FA.z)) diff --git a/nano/templates/comm_program.tmpl b/nano/templates/comm_program.tmpl new file mode 100644 index 00000000000..6925b28b914 --- /dev/null +++ b/nano/templates/comm_program.tmpl @@ -0,0 +1,135 @@ +{{if !data.authenticated}} +
    Please swipe your ID card. {{:helper.link('Log In', 'unlock', {'PRG_login' : 1}, null, 'fixed')}}
    +{{else}} + {{if !data.is_ai}} +
    Please remember to {{:helper.link('Log Out', 'lock', {'PRG_logout' : 1}, null, 'fixed')}}
    + {{/if}} + {{if data.lastCallLoc}} +
    Most recent shuttle call/recall traced to: {{:data.lastCallLoc}}
    + {{else}} +
    Unable to trace most recent shuttle call/recall signal.
    + {{/if}} +
    +

    Emergency Shuttle:

    + {{if data.shuttle.eta}} +
    ETA:
    +
    + {{>data.shuttle.eta}} +
    + {{/if}} +
    Options:
    +
    + {{if data.shuttle.callStatus == 1 && !data.is_ai}} + {{:helper.link('Cancel Shuttle', 'arrow-left', {'PRG_operation' : 'cancelshuttle'})}} + {{else data.shuttle.callStatus == 2}} + {{:helper.link('Call Shuttle', 'arrow-circle-down', {'PRG_operation' : 'callshuttle'})}} + {{/if}} +
    +
    + + {{if data.screen==1}} + +

    Menu

    +
    + {{if data.authenticated==2}} +
    + {{:helper.link('Make an Announcement', 'info', {'PRG_operation' : 'announce'})}} +
    +
    + {{if data.emagged}} + {{:helper.link('Message [UNKNOWN]', 'envelope', {'PRG_operation' : 'MessageSyndicate'})}} + {{else}} + {{:helper.link('Message CentComm', 'envelope', {'PRG_operation' : 'MessageCentcomm'})}} + {{/if}} +
    +
    + {{:helper.link('Request Nuclear Authentication Codes', 'exclamation-triangle', {'PRG_operation' : 'nukerequest'})}} +
    + {{/if}} +
    + {{:helper.link('Change Alert Level', 'signal', {'PRG_operation' : 'changeseclevel'})}} +
    +
    + {{:helper.link('Change Status Displays', 'info', {'PRG_operation' : 'status'})}} +
    +
    + {{:helper.link('Message List', 'comment', {'PRG_operation' : 'messagelist'})}} +
    +
    + {{:helper.link('Restart Nano-Mob Hunter GO! Server', 'power-off', {'PRG_operation' : 'RestartNanoMob'})}} +
    + {{if data.dock_request}} +
    + {{:helper.link('Accept Docking Request', 'signal', {'PRG_operation' : 'AcceptDocking'})}} + {{:helper.link('Deny Docking Request', 'signal', {'PRG_operation' : 'DenyDocking'})}} +
    + {{/if}} +
    + {{else data.screen==2}} + +

    Status Displays

    + {{:helper.link('Back', 'home', {'PRG_operation' : 'main'})}} +

    Presets

    + {{for data.stat_display.presets}} +
    +
    {{:helper.link(value.label, 'info', {'PRG_operation' : 'setstat', 'statdisp' : value.name}, name == data.stat_display.type ? 'selected' : null)}}
    +
    + {{/for}} +

    Alerts

    + {{for data.stat_display.alerts}} +
    +
    {{:helper.link(value.label, 'exclamation-triangle', {'PRG_operation' : 'setstat', 'statdisp' : 'alert', 'alert' : value.alert}, value.alert == data.stat_display.type ? 'selected' : null)}}
    +
    + {{/for}} +

    Messages

    +
    + {{if data.stat_display.type}} +
    +
    {{:helper.link('Line 1:', 'gear', {'PRG_operation':'setmsg1'})}}
    +
    {{>data.stat_display.line_1}}
    +
    +
    +
    {{:helper.link('Line 2:', 'gear', {'PRG_operation':'setmsg2'})}}
    +
    {{>data.stat_display.line_2}}
    +
    + {{/if}} +
    + {{else data.screen==3}} + +

    Messages

    + {{if data.current_message}} + {{:helper.link('Messages', 'home', {'PRG_operation' : 'messagelist'})}} +

    {{:data.current_message_title}}

    +
    + {{:data.current_message}} +
    + {{else}} + {{:helper.link('Back', 'home', {'PRG_operation' : 'main'})}} + {{for data.messages}} +
    + {{:value.title}}
    + {{:helper.link('Open', 'envelope-o', {'PRG_operation' : 'messagelist', 'msgid' : value.id})}} + {{:helper.link('Delete', 'close', {'PRG_operation' : 'delmessage', 'msgid' : value.id})}} +
    + {{/for}} + {{/if}} + {{else data.screen==4}} + +

    Security Level

    + {{:helper.link('Back', 'home', {'PRG_operation' : 'main'})}} +
    +
    +
    Security Level:
    +
    {{>data.str_security_level}}
    +
    +
    +
    Presets:
    +
    + {{for data.levels}} + {{:helper.link(value.name, 'comment', {'PRG_operation' : 'newalertlevel', 'level' : value.id}, value.id == data.security_level ? 'selected' : null)}} + {{/for}} +
    +
    +
    + {{/if}} +{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 5c468a2dba6..a2902da76db 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1774,6 +1774,7 @@ #include "code\modules\modular_computers\file_system\programs\airestorer.dm" #include "code\modules\modular_computers\file_system\programs\alarm.dm" #include "code\modules\modular_computers\file_system\programs\card.dm" +#include "code\modules\modular_computers\file_system\programs\comms.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" From 785f5b6f69bce7844ff3f63a0ca89df4df6da727 Mon Sep 17 00:00:00 2001 From: davipatury Date: Tue, 21 Feb 2017 18:40:58 -0300 Subject: [PATCH 14/22] GC --- code/modules/modular_computers/hardware/ai_slot.dm | 7 +++++++ .../modules/modular_computers/hardware/battery_module.dm | 9 +++++++++ code/modules/modular_computers/hardware/card_slot.dm | 8 +++++++- code/modules/modular_computers/hardware/hard_drive.dm | 1 + code/modules/modular_computers/laptop_vendor.dm | 2 +- 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index a11100534e1..5fd3ca1fc6b 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -10,6 +10,12 @@ var/obj/item/device/aicard/stored_card = null var/locked = FALSE +/obj/item/weapon/computer_hardware/ai_slot/Destroy() + if(stored_card) + qdel(stored_card) + stored_card = null + return ..() + /obj/item/weapon/computer_hardware/ai_slot/examine(mob/user) ..() @@ -21,6 +27,7 @@ /obj/item/weapon/computer_hardware/ai_slot/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) M.remove_verb(device_type) + try_eject(0, forced = 1) /obj/item/weapon/computer_hardware/ai_slot/try_insert(obj/item/I, mob/living/user = null) if(!holder) diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 16b1552f11e..562fb8c6cad 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -13,6 +13,15 @@ battery = new battery_type(src) ..() +/obj/item/weapon/computer_hardware/battery/Destroy() + if(battery) + qdel(battery) + battery = null + return ..() + +/obj/item/weapon/computer_hardware/battery/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) + try_eject(0, forced = 1) + /obj/item/weapon/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null) if(!holder) return FALSE diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index b310fa5be7d..6d323bb7fca 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -11,7 +11,12 @@ var/obj/item/weapon/card/id/stored_card2 = null /obj/item/weapon/computer_hardware/card_slot/Destroy() - try_eject() + if(stored_card) + qdel(stored_card) + stored_card = null + if(stored_card2) + qdel(stored_card2) + stored_card2 = null return ..() /obj/item/weapon/computer_hardware/card_slot/GetAccess() @@ -35,6 +40,7 @@ /obj/item/weapon/computer_hardware/card_slot/on_remove(obj/item/device/modular_computer/M, mob/living/user = null) M.remove_verb(device_type) + try_eject(0, forced = 1) /obj/item/weapon/computer_hardware/card_slot/try_insert(obj/item/I, mob/living/user = null) if(!holder) diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index a92d2e4802d..707e16e22ac 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -65,6 +65,7 @@ if(F in stored_files) stored_files -= F + qdel(F) recalculate_size() return 1 else diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index 71324e005ef..2a6cb6f18a6 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -221,7 +221,7 @@ /obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 400) + ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 700) ui.set_auto_update(1) ui.open() From 849c6dc2d1836a51d4582ae6430c9827e768e864 Mon Sep 17 00:00:00 2001 From: davipatury Date: Tue, 21 Feb 2017 18:53:32 -0300 Subject: [PATCH 15/22] Organizes programs folder. --- .../programs/{ => command}/card.dm | 0 .../programs/{ => command}/comms.dm | 0 .../programs/{ => engineering}/alarm.dm | 0 .../{ => engineering}/power_monitor.dm | 0 .../programs/{ => generic}/configurator.dm | 0 .../programs/{ => generic}/file_browser.dm | 0 .../programs/{ => generic}/ntdownloader.dm | 0 .../programs/{ => generic}/ntnrc_client.dm | 0 .../programs/{ => generic}/nttransfer.dm | 0 .../programs/{ => research}/airestorer.dm | 0 .../programs/{ => research}/ntmonitor.dm | 0 paradise.dme | 22 +++++++++---------- 12 files changed, 11 insertions(+), 11 deletions(-) rename code/modules/modular_computers/file_system/programs/{ => command}/card.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => command}/comms.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => engineering}/alarm.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => engineering}/power_monitor.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => generic}/configurator.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => generic}/file_browser.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => generic}/ntdownloader.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => generic}/ntnrc_client.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => generic}/nttransfer.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => research}/airestorer.dm (100%) rename code/modules/modular_computers/file_system/programs/{ => research}/ntmonitor.dm (100%) diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/card.dm rename to code/modules/modular_computers/file_system/programs/command/card.dm diff --git a/code/modules/modular_computers/file_system/programs/comms.dm b/code/modules/modular_computers/file_system/programs/command/comms.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/comms.dm rename to code/modules/modular_computers/file_system/programs/command/comms.dm diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/alarm.dm rename to code/modules/modular_computers/file_system/programs/engineering/alarm.dm diff --git a/code/modules/modular_computers/file_system/programs/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/power_monitor.dm rename to code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/configurator.dm rename to code/modules/modular_computers/file_system/programs/generic/configurator.dm diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/file_browser.dm rename to code/modules/modular_computers/file_system/programs/generic/file_browser.dm diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/ntdownloader.dm rename to code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/ntnrc_client.dm rename to code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm diff --git a/code/modules/modular_computers/file_system/programs/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/nttransfer.dm rename to code/modules/modular_computers/file_system/programs/generic/nttransfer.dm diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/research/airestorer.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/airestorer.dm rename to code/modules/modular_computers/file_system/programs/research/airestorer.dm diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm similarity index 100% rename from code/modules/modular_computers/file_system/programs/ntmonitor.dm rename to code/modules/modular_computers/file_system/programs/research/ntmonitor.dm diff --git a/paradise.dme b/paradise.dme index a2902da76db..aa04874168c 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1771,19 +1771,19 @@ #include "code\modules\modular_computers\file_system\data.dm" #include "code\modules\modular_computers\file_system\program.dm" #include "code\modules\modular_computers\file_system\program_events.dm" -#include "code\modules\modular_computers\file_system\programs\airestorer.dm" -#include "code\modules\modular_computers\file_system\programs\alarm.dm" -#include "code\modules\modular_computers\file_system\programs\card.dm" -#include "code\modules\modular_computers\file_system\programs\comms.dm" -#include "code\modules\modular_computers\file_system\programs\configurator.dm" -#include "code\modules\modular_computers\file_system\programs\file_browser.dm" -#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" -#include "code\modules\modular_computers\file_system\programs\ntmonitor.dm" -#include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" -#include "code\modules\modular_computers\file_system\programs\nttransfer.dm" -#include "code\modules\modular_computers\file_system\programs\power_monitor.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm" +#include "code\modules\modular_computers\file_system\programs\command\card.dm" +#include "code\modules\modular_computers\file_system\programs\command\comms.dm" +#include "code\modules\modular_computers\file_system\programs\engineering\alarm.dm" +#include "code\modules\modular_computers\file_system\programs\engineering\power_monitor.dm" +#include "code\modules\modular_computers\file_system\programs\generic\configurator.dm" +#include "code\modules\modular_computers\file_system\programs\generic\file_browser.dm" +#include "code\modules\modular_computers\file_system\programs\generic\ntdownloader.dm" +#include "code\modules\modular_computers\file_system\programs\generic\ntnrc_client.dm" +#include "code\modules\modular_computers\file_system\programs\generic\nttransfer.dm" +#include "code\modules\modular_computers\file_system\programs\research\airestorer.dm" +#include "code\modules\modular_computers\file_system\programs\research\ntmonitor.dm" #include "code\modules\modular_computers\hardware\_hardware.dm" #include "code\modules\modular_computers\hardware\ai_slot.dm" #include "code\modules\modular_computers\hardware\battery_module.dm" From 31f2ca0f63131b5017614d8e6ddf586a356b3070 Mon Sep 17 00:00:00 2001 From: davipatury Date: Thu, 23 Feb 2017 12:18:17 -0300 Subject: [PATCH 16/22] Autolathe designs. --- .../research/designs/autolathe_designs.dm | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index 450b0b8982a..9025f4e38a0 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -130,7 +130,7 @@ materials = list(MAT_METAL = 50, MAT_GLASS = 50) build_path = /obj/item/weapon/airlock_electronics category = list("initial", "Electronics") - + /datum/design/firelock_board name = "Firelock Electronics" id = "firelock_board" @@ -170,7 +170,7 @@ materials = list(MAT_METAL = 50, MAT_GLASS = 50) build_path = /obj/item/weapon/firealarm_electronics category = list("initial", "Electronics") - + /datum/design/earmuffs name = "Earmuffs" id = "earmuffs" @@ -744,4 +744,20 @@ build_type = AUTOLATHE materials = list(MAT_METAL = 450, MAT_GLASS = 190) build_path = /obj/item/conveyor_switch_construct - category = list("initial", "Construction") \ No newline at end of file + category = list("initial", "Construction") + +/datum/design/laptop + name = "Laptop Frame" + id = "laptop" + build_type = AUTOLATHE + materials = list(MAT_METAL = 10000, MAT_GLASS = 1000) + build_path = /obj/item/device/modular_computer/laptop/buildable + category = list("initial", "Miscellaneous") + +/datum/design/tablet + name = "Tablet Frame" + id = "tablet" + build_type = AUTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1000) + build_path = /obj/item/device/modular_computer/tablet + category = list("initial", "Miscellaneous") \ No newline at end of file From 8c7993b9d59ef547b6fbe9f9a2473ac6635e4f76 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sat, 4 Mar 2017 15:43:28 -0300 Subject: [PATCH 17/22] Crazylemon's suggestion. --- code/__DEFINES/misc.dm | 10 ++++ code/__HELPERS/text.dm | 8 ++-- .../computers/item/computer.dm | 29 ++++++------ .../computers/machinery/modular_console.dm | 45 +++++++++--------- .../file_system/programs/command/card.dm | 2 + .../file_system/programs/command/comms.dm | 2 +- .../programs/generic/file_browser.dm | 46 ++----------------- .../research/designs/computer_part_designs.dm | 6 +-- nano/templates/computer_fabricator.tmpl | 2 +- 9 files changed, 60 insertions(+), 90 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 75f953f6c79..b3263a93425 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -58,6 +58,16 @@ #define COLOR_WHITE "#FFFFFF" #define COLOR_GRAY "#808080" +//FONTS: +// Used by Paper and PhotoCopier (and PaperBin once a year). +// Used by PDA's Notekeeper. +// Used by NewsCaster and NewsPaper. +// Used by Modular Computers +#define PEN_FONT "Verdana" +#define CRAYON_FONT "Comic Sans MS" +#define PRINTER_FONT "Times New Roman" +#define SIGNFONT "Times New Roman" + //some arbitrary defines to be used by self-pruning global lists. (see master_controller) #define PROCESS_KILL 26 //Used to trigger removal from a processing list diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index adff1623e12..6fdeeb04aa1 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -456,7 +456,7 @@ proc/checkhtml(var/t) // Pencode -/proc/pencode_to_html(text, mob/user, var/obj/item/weapon/pen/P = null, deffont = "Verdana", signfont = "Times New Roman", crayonfont = "Comic Sans MS") +/proc/pencode_to_html(text, mob/user, obj/item/weapon/pen/P = null, sign = 1, fields = 1, deffont = PEN_FONT, signfont = SIGNFONT, crayonfont = CRAYON_FONT) text = replacetext(text, "\n", "
    ") text = replacetext(text, "\[center\]", "
    ") text = replacetext(text, "\[/center\]", "
    ") @@ -469,8 +469,10 @@ proc/checkhtml(var/t) text = replacetext(text, "\[/u\]", "") text = replacetext(text, "\[large\]", "") text = replacetext(text, "\[/large\]", "") - text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") - text = replacetext(text, "\[field\]", "") + if(sign) + text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") + if(field) + text = replacetext(text, "\[field\]", "") text = replacetext(text, "\[h1\]", "

    ") text = replacetext(text, "\[/h1\]", "

    ") diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 60af73bdb98..cbdd46eb2b2 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -249,11 +249,6 @@ if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) active_program.event_networkfailure(0) // Active program requires NTNet to run but we've just lost connection. Crash. - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) - P.event_networkfailure(1) - if(active_program) if(active_program.program_state != PROGRAM_STATE_KILLED) active_program.process_tick() @@ -264,17 +259,18 @@ for(var/I in idle_threads) var/datum/computer_file/program/P = I if(P.program_state != PROGRAM_STATE_KILLED) + if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) + P.event_networkfailure(1) P.process_tick() P.ntnet_status = get_ntnet_status() else idle_threads.Remove(P) handle_power() // Handles all computer power interaction - //check_update_ui_need() // Relays kill program request to currently active program. Use this to quit current program. /obj/item/device/modular_computer/proc/kill_program(forced = FALSE) - if(active_program) + if(active_program && active_program.program_state != PROGRAM_STATE_KILLED) active_program.kill_program(forced) active_program = null var/mob/user = usr @@ -297,14 +293,15 @@ return ntnet_global.add_log(text, network_card) /obj/item/device/modular_computer/proc/shutdown_computer(loud = 1) - kill_program(forced = TRUE) - for(var/datum/computer_file/program/P in idle_threads) - P.kill_program(forced = TRUE) - idle_threads.Remove(P) - if(loud) - physical.visible_message("\The [src] shuts down.") - enabled = 0 - update_icon() + if(enabled) + kill_program(forced = TRUE) + for(var/datum/computer_file/program/P in idle_threads) + P.kill_program(forced = TRUE) + idle_threads.Remove(P) + if(loud) + physical.visible_message("\The [src] shuts down.") + enabled = 0 + update_icon() /obj/item/device/modular_computer/attackby(obj/item/weapon/W, mob/user) @@ -323,7 +320,7 @@ if(all_components.len) to_chat(user, "Remove all components from \the [src] before disassembling it.") return - new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost ) + new /obj/item/stack/sheet/metal(get_turf(loc), steel_sheet_cost) physical.visible_message("\The [src] has been disassembled by [user].") relay_qdel() qdel(src) diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm index 60315198ca6..2c330913353 100644 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -8,7 +8,6 @@ icon_state_unpowered = "console-off" screen_icon_state_menu = "menu" hardware_flag = PROGRAM_CONSOLE - var/console_department = "" // Used in New() to set network tag according to our area. anchored = 1 density = 1 base_idle_power_usage = 100 @@ -20,15 +19,11 @@ max_integrity = 300 integrity_failure = 150 -/obj/machinery/modular_computer/console/buildable/New() - ..() - // User-built consoles start as empty frames. - var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] - var/obj/item/weapon/computer_hardware/hard_drive/network_card = cpu.all_components[MC_NET] - var/obj/item/weapon/computer_hardware/hard_drive/recharger = cpu.all_components[MC_CHARGE] - qdel(recharger) - qdel(network_card) - qdel(hard_drive) + var/console_department = "" // Used in New() to set network tag according to our area. + var/components = TRUE // Install basic components? + +/obj/machinery/modular_computer/console/buildable + components = FALSE /obj/machinery/modular_computer/console/New() ..() @@ -36,22 +31,24 @@ if(battery_module) qdel(battery_module) - var/obj/item/weapon/computer_hardware/network_card/wired/network_card = new() + if(components) + var/obj/item/weapon/computer_hardware/network_card/wired/network_card = new() - cpu.install_component(network_card) - cpu.install_component(new /obj/item/weapon/computer_hardware/recharger/APC) - cpu.install_component(new /obj/item/weapon/computer_hardware/hard_drive/super) // Consoles generally have better HDDs due to lower space limitations + cpu.install_component(network_card) + cpu.install_component(new /obj/item/weapon/computer_hardware/recharger/APC) + cpu.install_component(new /obj/item/weapon/computer_hardware/hard_drive/super) // Consoles generally have better HDDs due to lower space limitations + + var/area/A = get_area(src) + // Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too. + if(A && console_department) + network_card.identification_string = replacetext(replacetext(replacetext("[A.name] [console_department] Console", " ", "_"), "-", ""), "__", "_") // Replace spaces with "_" + else if(A) + network_card.identification_string = replacetext(replacetext(replacetext("[A.name] Console", " ", "_"), "-", ""), "__", "_") + else if(console_department) + network_card.identification_string = replacetext(replacetext(replacetext("[console_department] Console", " ", "_"), "-", ""), "__", "_") + else + network_card.identification_string = "Unknown Console" - var/area/A = get_area(src) - // Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too. - if(A && console_department) - network_card.identification_string = replacetext(replacetext(replacetext("[A.name] [console_department] Console", " ", "_"), "-", ""), "__", "_") // Replace spaces with "_" - else if(A) - network_card.identification_string = replacetext(replacetext(replacetext("[A.name] Console", " ", "_"), "-", ""), "__", "_") - else if(console_department) - network_card.identification_string = replacetext(replacetext(replacetext("[console_department] Console", " ", "_"), "-", ""), "__", "_") - else - network_card.identification_string = "Unknown Console" if(cpu) cpu.screen_on = 1 update_icon() \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 9b02391eb4b..9497392383e 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -304,6 +304,7 @@ time_last_changed_position = world.time / 10 j.total_positions++ opened_positions[edit_job_target]++ + log_game("[key_name(usr)] has opened a job slot for job \"[j]\".") if("PRG_make_job_unavailable") // MAKE JOB POSITION UNAVAILABLE FOR LATE JOINERS @@ -319,6 +320,7 @@ time_last_changed_position = world.time / 10 j.total_positions-- opened_positions[edit_job_target]-- + log_game("[key_name(usr)] has closed a job slot for job \"[j]\".") if(modify) modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") diff --git a/code/modules/modular_computers/file_system/programs/command/comms.dm b/code/modules/modular_computers/file_system/programs/command/comms.dm index 52b8a154570..d97e2f725fc 100644 --- a/code/modules/modular_computers/file_system/programs/command/comms.dm +++ b/code/modules/modular_computers/file_system/programs/command/comms.dm @@ -1,6 +1,6 @@ /datum/computer_file/program/comm filename = "comm" - filedesc = "Command and communications program." + filedesc = "Command and communications" program_icon_state = "comm" extended_desc = "Used to command and control the station. Can relay long-range communications. This program can not be run on tablet computers." required_access = access_heads diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 2f19757f2f4..137d4adebe9 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -110,7 +110,7 @@ if(!printer) error = "Missing Hardware: Your computer does not have required hardware to complete this operation." return 1 - if(!printer.print_text("" + prepare_printjob(F.stored_data) + "", open_file)) + if(!printer.print_text(prepare_printjob(F.stored_data, computer.emagged ? CRAYON_FONT : PRINTER_FONT), open_file)) error = "Hardware error: Printer was unable to print the file. It may be out of paper." return 1 if("PRG_copytousb") @@ -132,48 +132,10 @@ var/datum/computer_file/C = F.clone(0) HDD.store_file(C) -/datum/computer_file/program/filemanager/proc/parse_tags(t) - t = replacetext(t, "\[center\]", "
    ") - t = replacetext(t, "\[/center\]", "
    ") - t = replacetext(t, "\[br\]", "
    ") - t = replacetext(t, "\n", "
    ") - t = replacetext(t, "\[b\]", "") - t = replacetext(t, "\[/b\]", "") - t = replacetext(t, "\[i\]", "") - t = replacetext(t, "\[/i\]", "") - t = replacetext(t, "\[u\]", "") - t = replacetext(t, "\[/u\]", "") - t = replacetext(t, "\[time\]", "[worldtime2text()]") - t = replacetext(t, "\[date\]", "[current_date_string] [worldtime2text()]") - t = replacetext(t, "\[large\]", "") - t = replacetext(t, "\[/large\]", "") - t = replacetext(t, "\[h1\]", "

    ") - t = replacetext(t, "\[/h1\]", "

    ") - t = replacetext(t, "\[h2\]", "

    ") - t = replacetext(t, "\[/h2\]", "

    ") - t = replacetext(t, "\[h3\]", "

    ") - t = replacetext(t, "\[/h3\]", "

    ") - t = replacetext(t, "\[*\]", "
  • ") - t = replacetext(t, "\[hr\]", "
    ") - t = replacetext(t, "\[small\]", "") - t = replacetext(t, "\[/small\]", "") - t = replacetext(t, "\[list\]", "
      ") - t = replacetext(t, "\[/list\]", "
    ") - t = replacetext(t, "\[table\]", "") - t = replacetext(t, "\[/table\]", "
    ") - t = replacetext(t, "\[grid\]", "") - t = replacetext(t, "\[/grid\]", "
    ") - t = replacetext(t, "\[row\]", "") - t = replacetext(t, "\[tr\]", "") - t = replacetext(t, "\[td\]", "") - t = replacetext(t, "\[cell\]", "") - t = replacetext(t, "\[tab\]", "    ") - return t - -/datum/computer_file/program/filemanager/proc/prepare_printjob(t) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. +/datum/computer_file/program/filemanager/proc/prepare_printjob(t, font = PRINTER_FONT) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. t = replacetext(t, "\[field\]", "") t = replacetext(t, "\[sign\]", "") - t = parse_tags(t) + t = pencode_to_html(t, P = /obj/item/weapon/pen, sign = 0, fields = 0, deffont = font) return t /datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -202,7 +164,7 @@ if(!istype(file)) data["error"] = "I/O ERROR: Unable to open file." else - data["filedata"] = parse_tags(file.stored_data) + data["filedata"] = pencode_to_html(file.stored_data, P = /obj/item/weapon/pen, sign = 0, fields = 0) data["filename"] = "[file.filename].[file.filetype]" else if(!computer || !HDD) diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm index 5108cdfeaee..278adb3cf25 100644 --- a/code/modules/research/designs/computer_part_designs.dm +++ b/code/modules/research/designs/computer_part_designs.dm @@ -63,7 +63,7 @@ id = "netcard_basic" req_tech = list("programming" = 2, "engineering" = 1) build_type = IMPRINTER - materials = list(MAT_METAL = 250, MAT_GLASS = 100, "sacid" = 20) + materials = list(MAT_GLASS = 100, "sacid" = 20) build_path = /obj/item/weapon/computer_hardware/network_card category = list("Computer Parts") @@ -72,7 +72,7 @@ id = "netcard_advanced" req_tech = list("programming" = 4, "engineering" = 2) build_type = IMPRINTER - materials = list(MAT_METAL = 500, MAT_GLASS = 200, "sacid" = 20) + materials = list(MAT_GLASS = 200, "sacid" = 20) build_path = /obj/item/weapon/computer_hardware/network_card/advanced category = list("Computer Parts") @@ -81,7 +81,7 @@ id = "netcard_wired" req_tech = list("programming" = 5, "engineering" = 3) build_type = IMPRINTER - materials = list(MAT_METAL = 2500, MAT_GLASS = 400, "sacid" = 20) + materials = list(MAT_GLASS = 400, "sacid" = 20) build_path = /obj/item/weapon/computer_hardware/network_card/wired category = list("Computer Parts") diff --git a/nano/templates/computer_fabricator.tmpl b/nano/templates/computer_fabricator.tmpl index d262beb4f6f..14c0e5965af 100644 --- a/nano/templates/computer_fabricator.tmpl +++ b/nano/templates/computer_fabricator.tmpl @@ -4,7 +4,7 @@

    Step 1: Select your device type

    {{:helper.link('Laptop', 'laptop', {'action' : 'pick_device', 'pick' : 1})}} - {{:helper.link('LTablet', 'tablet', {'action' : 'pick_device', 'pick' : 2})}} + {{:helper.link('Tablet', 'tablet', {'action' : 'pick_device', 'pick' : 2})}}
    {{else data.state == 1}}
    From 5fb5e1eedd2267293a2fee644d341809439699a9 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sat, 4 Mar 2017 15:57:51 -0300 Subject: [PATCH 18/22] Damn. --- code/__HELPERS/text.dm | 3 ++- .../file_system/programs/generic/file_browser.dm | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index eec3c73c523..b5171ef1144 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -471,7 +471,7 @@ proc/checkhtml(var/t) text = replacetext(text, "\[/large\]", "") if(sign) text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") - if(field) + if(fields) text = replacetext(text, "\[field\]", "") text = replacetext(text, "\[h1\]", "

    ") @@ -549,3 +549,4 @@ proc/checkhtml(var/t) text = replacetext(text, "", "\[cell\]") text = replacetext(text, "", "\[logo\]") return text + diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 137d4adebe9..0bd549c683f 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -135,7 +135,7 @@ /datum/computer_file/program/filemanager/proc/prepare_printjob(t, font = PRINTER_FONT) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. t = replacetext(t, "\[field\]", "") t = replacetext(t, "\[sign\]", "") - t = pencode_to_html(t, P = /obj/item/weapon/pen, sign = 0, fields = 0, deffont = font) + t = pencode_to_html(t, sign = 0, fields = 0, deffont = font) return t /datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -164,7 +164,7 @@ if(!istype(file)) data["error"] = "I/O ERROR: Unable to open file." else - data["filedata"] = pencode_to_html(file.stored_data, P = /obj/item/weapon/pen, sign = 0, fields = 0) + data["filedata"] = pencode_to_html(file.stored_data, sign = 0, fields = 0) data["filename"] = "[file.filename].[file.filetype]" else if(!computer || !HDD) From 8496523a6cdbca9688384822cc4eedc806937c2e Mon Sep 17 00:00:00 2001 From: davipatury Date: Sat, 4 Mar 2017 18:40:16 -0300 Subject: [PATCH 19/22] Battery ETA --- .../computers/item/computer.dm | 2 +- .../computers/item/computer_power.dm | 12 +++++++++++ .../programs/generic/configurator.dm | 21 +++++++------------ .../hardware/battery_module.dm | 11 +++------- nano/templates/laptop_configuration.tmpl | 8 +++++-- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index cbdd46eb2b2..9f867692153 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -191,7 +191,7 @@ icon_state = icon_state_unpowered else icon_state = icon_state_powered - if(active_program) + if(active_program && active_program.program_state != PROGRAM_STATE_KILLED) overlays += active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu else overlays += icon_state_menu diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm index c5aab99ce57..8483e9c4fc6 100644 --- a/code/modules/modular_computers/computers/item/computer_power.dm +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -56,6 +56,18 @@ power_failure() return FALSE +/obj/item/device/modular_computer/proc/get_power_eta() + var/obj/item/weapon/computer_hardware/battery/battery_module = all_components[MC_CELL] + if(battery_module && battery_module.battery) + var/hours = 0 + var/minutes = (battery_module.battery.charge) / (last_power_usage / 20) // 20 = obj processing interval + if(minutes > 60) + hours = minutes/60 + minutes = (hours - round(hours)) * 60 + var/seconds = (minutes - round(minutes)) * 60 + return list("hours" = hours, "minutes" = minutes, "seconds" = seconds) + return list() + // Used by child types if they have other power source than battery or recharger /obj/item/device/modular_computer/proc/check_power_override() return FALSE diff --git a/code/modules/modular_computers/file_system/programs/generic/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm index e46b9e3d09c..154caa103e0 100644 --- a/code/modules/modular_computers/file_system/programs/generic/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/generic/configurator.dm @@ -12,7 +12,6 @@ size = 4 available_on_ntnet = 0 requires_ntnet = 0 - var/obj/item/device/modular_computer/movable = null /datum/computer_file/program/computerconfig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -25,32 +24,26 @@ ui.open() /datum/computer_file/program/computerconfig/ui_data(mob/user) - movable = computer - var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = movable.all_components[MC_HDD] - var/obj/item/weapon/computer_hardware/battery/battery_module = movable.all_components[MC_CELL] - if(!istype(movable)) - movable = null - - // No computer connection, we can't get data from that. - if(!movable) - return 0 + var/obj/item/weapon/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/battery/battery_module = computer.all_components[MC_CELL] var/list/data = get_header_data() data["disk_size"] = hard_drive.max_capacity data["disk_used"] = hard_drive.used_capacity - data["power_usage"] = movable.last_power_usage + data["power_usage"] = computer.last_power_usage data["battery_exists"] = battery_module ? 1 : 0 if(battery_module && battery_module.battery) data["battery_rating"] = battery_module.battery.maxcharge data["battery_percent"] = round(battery_module.battery.percent()) + data["eta"] = computer.get_power_eta() if(battery_module && battery_module.battery) data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge)) var/list/all_entries[0] - for(var/I in movable.all_components) - var/obj/item/weapon/computer_hardware/H = movable.all_components[I] + for(var/I in computer.all_components) + var/obj/item/weapon/computer_hardware/H = computer.all_components[I] all_entries.Add(list(list( "name" = H.name, "desc" = H.desc, @@ -68,7 +61,7 @@ return switch(href_list["action"]) if("PC_toggle_component") - var/obj/item/weapon/computer_hardware/H = movable.find_hardware_by_name(href_list["name"]) + var/obj/item/weapon/computer_hardware/H = computer.find_hardware_by_name(href_list["name"]) if(H && istype(H)) H.enabled = !H.enabled . = TRUE diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 562fb8c6cad..5ef74b74f4b 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -69,11 +69,7 @@ return FALSE - - - - - +// Stock parts /obj/item/weapon/stock_parts/cell/computer name = "standard battery" desc = "A standard power cell, commonly seen in high-end portable microcomputers or low-end laptops." @@ -83,13 +79,11 @@ w_class = WEIGHT_CLASS_TINY maxcharge = 750 - /obj/item/weapon/stock_parts/cell/computer/advanced name = "advanced battery" desc = "An advanced power cell, often used in most laptops. It is too large to be fitted into smaller devices." icon_state = "cell" origin_tech = "powerstorage=2;engineering=2" - w_class = WEIGHT_CLASS_SMALL maxcharge = 1500 /obj/item/weapon/stock_parts/cell/computer/super @@ -97,17 +91,18 @@ desc = "An advanced power cell, often used in high-end laptops." icon_state = "cell" origin_tech = "powerstorage=3;engineering=3" - w_class = WEIGHT_CLASS_SMALL maxcharge = 2000 /obj/item/weapon/stock_parts/cell/computer/micro name = "micro battery" desc = "A small power cell, commonly seen in most portable microcomputers." icon_state = "cell_micro" + w_class = WEIGHT_CLASS_TINY maxcharge = 500 /obj/item/weapon/stock_parts/cell/computer/nano name = "nano battery" desc = "A tiny power cell, commonly seen in low-end portable microcomputers." icon_state = "cell_micro" + w_class = WEIGHT_CLASS_TINY maxcharge = 300 \ No newline at end of file diff --git a/nano/templates/laptop_configuration.tmpl b/nano/templates/laptop_configuration.tmpl index 07c499d7158..95dd315b90e 100644 --- a/nano/templates/laptop_configuration.tmpl +++ b/nano/templates/laptop_configuration.tmpl @@ -10,6 +10,10 @@
    Battery Rating
    {{:data.battery.max}}

    +
    +
    ETA
    +
    {{:data.eta.hours ? helper.smoothRound(data.eta.hours) + "h " : ""}}{{:data.eta.minutes ? helper.smoothRound(data.eta.minutes) + "m " : ""}}{{:helper.smoothRound(data.eta.seconds)}}s
    +
    Battery Charge
    {{:helper.displayBar(data.battery.charge, 0, data.battery.max, (data.battery.charge > data.battery.max/2) ? 'good' : (data.battery.charge > data.battery.max/4) ? 'average' : 'bad')}} @@ -29,7 +33,7 @@
    {{:data.power_usage}}W
    - +

    File System

    @@ -40,7 +44,7 @@
    - +

    Computer Components

    {{for data.hardware}} From 6473d0c79fa9c831ce2a04beb63b2cb7ebf61070 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sat, 4 Mar 2017 21:29:24 -0300 Subject: [PATCH 20/22] Crazylemons's ntnet downloader suggestion. --- code/modules/modular_computers/file_system/program.dm | 1 + .../file_system/programs/generic/ntdownloader.dm | 3 ++- nano/templates/ntnet_downloader.tmpl | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index b2158304d36..8d917e4cb81 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -138,6 +138,7 @@ // Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. /datum/computer_file/program/proc/kill_program(forced = FALSE) program_state = PROGRAM_STATE_KILLED + computer.update_icon() if(network_destination) generate_network_log("Connection to [network_destination] closed.") return 1 diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index ca4ea4eace0..d71f744d694 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -144,7 +144,8 @@ "filedesc" = P.filedesc, "fileinfo" = P.extended_desc, "compatibility" = check_compatibility(P), - "size" = P.size + "size" = P.size, + "status" = !hard_drive || !hard_drive.can_store_file(P) ? "disabled" : null ))) data["hackedavailable"] = 0 if(computer.emagged) // If we are running on emagged computer we have access to some "bonus" software diff --git a/nano/templates/ntnet_downloader.tmpl b/nano/templates/ntnet_downloader.tmpl index 3582a48bf7c..3e4e90569f5 100644 --- a/nano/templates/ntnet_downloader.tmpl +++ b/nano/templates/ntnet_downloader.tmpl @@ -68,7 +68,7 @@
    File controls
    -
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename})}}
    +
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename, value.status})}}
    {{/for}} From a47fda8b31514737e4fadb8ec0814874325849b1 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sat, 4 Mar 2017 22:02:36 -0300 Subject: [PATCH 21/22] Layout fix. --- .../computers/item/computer_ui.dm | 2 +- .../file_system/programs/antagonist/dos.dm | 1 + .../programs/antagonist/revelation.dm | 1 + .../file_system/programs/command/card.dm | 1 + .../file_system/programs/command/comms.dm | 1 + .../file_system/programs/engineering/alarm.dm | 1 + .../programs/engineering/power_monitor.dm | 1 + .../programs/generic/configurator.dm | 1 + .../programs/generic/file_browser.dm | 125 +++++++-------- .../programs/generic/ntdownloader.dm | 1 + .../programs/generic/ntnrc_client.dm | 142 +++++++++--------- .../programs/generic/nttransfer.dm | 1 + .../programs/research/airestorer.dm | 1 + .../programs/research/ntmonitor.dm | 2 +- nano/layouts/layout_default.tmpl | 38 ----- nano/layouts/layout_program.tmpl | 75 +++++++++ nano/templates/computer_fabricator.tmpl | 37 +++-- nano/templates/ntnet_downloader.tmpl | 2 +- 18 files changed, 240 insertions(+), 193 deletions(-) create mode 100644 nano/layouts/layout_program.tmpl diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 7df664a1bb4..4044e2530bf 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -35,6 +35,7 @@ assets.send(user) ui = new(user, src, ui_key, "computer_main.tmpl", "NTOS Main menu", 400, 500) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() @@ -105,7 +106,6 @@ data["PC_programheaders"] = program_headers data["PC_stationtime"] = worldtime2text() - data["PC_hasheader"] = 1 data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen return data diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 17e2187eb81..feaee3be63d 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -42,6 +42,7 @@ assets.send(user) ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 575, 250) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 62a0c102b4a..148766ef59d 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -67,6 +67,7 @@ assets.send(user) ui = new(user, src, ui_key, "revelation.tmpl", "Revelation Virus", 575, 250) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/revelation/ui_data(mob/user) diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 9497392383e..8cd1043bea6 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -142,6 +142,7 @@ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) assets.send(user) ui = new(user, src, ui_key, "card_prog.tmpl", "ID card modification program", 775, 700) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/card_mod/Topic(href, href_list) diff --git a/code/modules/modular_computers/file_system/programs/command/comms.dm b/code/modules/modular_computers/file_system/programs/command/comms.dm index d97e2f725fc..84cc2ec130a 100644 --- a/code/modules/modular_computers/file_system/programs/command/comms.dm +++ b/code/modules/modular_computers/file_system/programs/command/comms.dm @@ -99,6 +99,7 @@ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) assets.send(user) ui = new(user, src, ui_key, "comm_program.tmpl", "Command and communications program", 575, 500) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/comm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm index 66e3d1b120a..db5966314da 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm @@ -41,6 +41,7 @@ assets.send(user) ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/alarm_monitor/ui_data(mob/user) diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 0b057f7aa4f..dca2fabd499 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -30,6 +30,7 @@ assets.send(user) ui = new(user, src, ui_key, "power_monitor.tmpl", "Alarm Monitoring", 800, 700) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/power_monitor/ui_data() diff --git a/code/modules/modular_computers/file_system/programs/generic/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm index 154caa103e0..0630d1a129a 100644 --- a/code/modules/modular_computers/file_system/programs/generic/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/generic/configurator.dm @@ -21,6 +21,7 @@ assets.send(user) ui = new(user, src, ui_key, "laptop_configuration.tmpl", "NTOS Configuration Utility", 575, 700) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/computerconfig/ui_data(mob/user) diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 0bd549c683f..20c4e92fd98 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -10,6 +10,68 @@ var/open_file var/error +/datum/computer_file/program/filemanager/proc/prepare_printjob(t, font = PRINTER_FONT) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. + t = replacetext(t, "\[field\]", "") + t = replacetext(t, "\[sign\]", "") + t = pencode_to_html(t, sign = 0, fields = 0, deffont = font) + return t + +/datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700) + ui.set_auto_update(1) + ui.set_layout_key("program") + ui.open() + +/datum/computer_file/program/filemanager/ui_data(mob/user) + var/list/data = get_header_data() + + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.all_components[MC_SDD] + if(error) + data["error"] = error + if(open_file) + var/datum/computer_file/data/file + + if(!computer || !HDD) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + file = HDD.find_file_by_name(open_file) + if(!istype(file)) + data["error"] = "I/O ERROR: Unable to open file." + else + data["filedata"] = pencode_to_html(file.stored_data, sign = 0, fields = 0) + data["filename"] = "[file.filename].[file.filetype]" + else + if(!computer || !HDD) + data["error"] = "I/O ERROR: Unable to access hard drive." + else + 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 + + return data + /datum/computer_file/program/filemanager/Topic(href, list/href_list) if(..()) return 1 @@ -130,65 +192,4 @@ if(!F || !istype(F)) return 1 var/datum/computer_file/C = F.clone(0) - HDD.store_file(C) - -/datum/computer_file/program/filemanager/proc/prepare_printjob(t, font = PRINTER_FONT) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. - t = replacetext(t, "\[field\]", "") - t = replacetext(t, "\[sign\]", "") - t = pencode_to_html(t, sign = 0, fields = 0, deffont = font) - return t - -/datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700) - ui.set_auto_update(1) - ui.open() - -/datum/computer_file/program/filemanager/ui_data(mob/user) - var/list/data = get_header_data() - - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] - var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.all_components[MC_SDD] - if(error) - data["error"] = error - if(open_file) - var/datum/computer_file/data/file - - if(!computer || !HDD) - data["error"] = "I/O ERROR: Unable to access hard drive." - else - file = HDD.find_file_by_name(open_file) - if(!istype(file)) - data["error"] = "I/O ERROR: Unable to open file." - else - data["filedata"] = pencode_to_html(file.stored_data, sign = 0, fields = 0) - data["filename"] = "[file.filename].[file.filetype]" - else - if(!computer || !HDD) - data["error"] = "I/O ERROR: Unable to access hard drive." - else - 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 - - return data + HDD.store_file(C) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index d71f744d694..1ce9fb38bd1 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -110,6 +110,7 @@ assets.send(user) ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/ntnetdownload/ui_data(mob/user) diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm index 753eadbdb2a..bfb90ab881b 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm @@ -16,8 +16,79 @@ 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/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(forced = FALSE) + if(channel) + channel.remove_client(src) + channel = null + ..() + +/datum/computer_file/program/chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) + assets.send(user) + ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700) + ui.set_auto_update(1) + ui.set_layout_key("program") + ui.open() + + +/datum/computer_file/program/chatclient/ui_data(mob/user) + if(!ntnet_global || !ntnet_global.chat_channels) + return + + var/list/data = get_header_data() + + data["adminmode"] = netadmin_mode + if(channel) + data["title"] = channel.title + var/list/messages[0] + for(var/M in channel.messages) + messages.Add(list(list( + "msg" = M + ))) + data["messages"] = messages + var/list/clients[0] + for(var/C in channel.clients) + var/datum/computer_file/program/chatclient/cl = C + clients.Add(list(list( + "name" = cl.username + ))) + data["clients"] = clients + operator_mode = (channel.operator == src) ? 1 : 0 + data["is_operator"] = operator_mode || netadmin_mode + + else // Channel selection screen + var/list/all_channels[0] + for(var/C in ntnet_global.chat_channels) + var/datum/ntnet_conversation/conv = C + if(conv && conv.title) + all_channels.Add(list(list( + "chan" = conv.title, + "id" = conv.id + ))) + data["all_channels"] = all_channels + + return data + /datum/computer_file/program/chatclient/Topic(href, list/href_list) if(..()) return 1 @@ -157,73 +228,4 @@ 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(forced = FALSE) - if(channel) - channel.remove_client(src) - channel = null - ..() - -/datum/computer_file/program/chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700) - ui.set_auto_update(1) - ui.open() - - -/datum/computer_file/program/chatclient/ui_data(mob/user) - if(!ntnet_global || !ntnet_global.chat_channels) - return - - var/list/data = get_header_data() - - data["adminmode"] = netadmin_mode - if(channel) - data["title"] = channel.title - var/list/messages[0] - for(var/M in channel.messages) - messages.Add(list(list( - "msg" = M - ))) - data["messages"] = messages - var/list/clients[0] - for(var/C in channel.clients) - var/datum/computer_file/program/chatclient/cl = C - clients.Add(list(list( - "name" = cl.username - ))) - data["clients"] = clients - operator_mode = (channel.operator == src) ? 1 : 0 - data["is_operator"] = operator_mode || netadmin_mode - - else // Channel selection screen - var/list/all_channels[0] - for(var/C in ntnet_global.chat_channels) - var/datum/ntnet_conversation/conv = C - if(conv && conv.title) - all_channels.Add(list(list( - "chan" = conv.title, - "id" = conv.id - ))) - data["all_channels"] = all_channels - - return data \ No newline at end of file + channel.password = newpassword \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm index d35a5127e18..1ddabcd1151 100644 --- a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm +++ b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm @@ -92,6 +92,7 @@ var/global/nttransfer_uid = 0 assets.send(user) ui = new(user, src, ui_key, "ntnet_transfer.tmpl", "NTNet P2P Transfer Client", 575, 700) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/nttransfer/Topic(href, list/href_list) diff --git a/code/modules/modular_computers/file_system/programs/research/airestorer.dm b/code/modules/modular_computers/file_system/programs/research/airestorer.dm index 21cfdec4972..55bbcb3b2b2 100644 --- a/code/modules/modular_computers/file_system/programs/research/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/research/airestorer.dm @@ -129,6 +129,7 @@ assets.send(user) ui = new(user, src, ui_key, "ai_restorer.tmpl", "Integrity Restorer", 600, 400) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() /datum/computer_file/program/aidiag/kill_program(forced) diff --git a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm index 969a931ac71..e3809cdcc37 100644 --- a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm @@ -15,9 +15,9 @@ assets.send(user) ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700) ui.set_auto_update(1) + ui.set_layout_key("program") ui.open() - /datum/computer_file/program/ntnetmonitor/Topic(href, list/href_list) if(..()) return 1 diff --git a/nano/layouts/layout_default.tmpl b/nano/layouts/layout_default.tmpl index 812b974a1ab..1c270ee4c18 100644 --- a/nano/layouts/layout_default.tmpl +++ b/nano/layouts/layout_default.tmpl @@ -31,44 +31,6 @@ -{{if data.PC_hasheader}} -
    -
    - - {{if data.PC_batteryicon && data.PC_showbatteryicon}} -
    - {{/if}} - {{if data.PC_batterypercent && data.PC_showbatteryicon}} - {{:data.PC_batterypercent}} - {{/if}} - {{if data.PC_ntneticon}} - - {{/if}} - {{if data.PC_apclinkicon}} - - {{/if}} - {{if data.PC_stationtime}} - {{:data.PC_stationtime}} - {{/if}} - {{for data.PC_programheaders}} - - {{/for}} -
    -
    -
    -
    -
    - -
    {{:helper.link('Shutdown', 'power-off', {'action' : 'PC_shutdown'})}} - {{if data.PC_showexitprogram}} - {{:helper.link('EXIT PROGRAM', 'times', {'action' : 'PC_exit'})}} - {{:helper.link('Minimize Program', 'minus', {'action' : 'PC_minimize'})}} - {{/if}} -
    -
    -
    -
    -{{/if}}
    Initiating...
    diff --git a/nano/layouts/layout_program.tmpl b/nano/layouts/layout_program.tmpl new file mode 100644 index 00000000000..66d1605c7c0 --- /dev/null +++ b/nano/layouts/layout_program.tmpl @@ -0,0 +1,75 @@ +
    + +
    {{:config.title}}
    +
    + +    +
    + +
    +
    + + {{if data.PC_batteryicon && data.PC_showbatteryicon}} +
    + {{/if}} + {{if data.PC_batterypercent && data.PC_showbatteryicon}} + {{:data.PC_batterypercent}} + {{/if}} + {{if data.PC_ntneticon}} + + {{/if}} + {{if data.PC_apclinkicon}} + + {{/if}} + {{if data.PC_stationtime}} + {{:data.PC_stationtime}} + {{/if}} + {{for data.PC_programheaders}} + + {{/for}} +
    +
    +
    +
    +
    + +
    {{:helper.link('Shutdown', 'power-off', {'action' : 'PC_shutdown'})}} + {{if data.PC_showexitprogram}} + {{:helper.link('EXIT PROGRAM', 'times', {'action' : 'PC_exit'})}} + {{:helper.link('Minimize Program', 'minus', {'action' : 'PC_minimize'})}} + {{/if}} +
    +
    +
    + + + +
    +
    Initiating...
    +
    +
    \ No newline at end of file diff --git a/nano/templates/computer_fabricator.tmpl b/nano/templates/computer_fabricator.tmpl index 14c0e5965af..b5d80101b70 100644 --- a/nano/templates/computer_fabricator.tmpl +++ b/nano/templates/computer_fabricator.tmpl @@ -42,28 +42,25 @@ {{:helper.link('None', 'times', {'action' : 'hw_card', 'card' : 0}, data.hw_card == 0 ? "selected" : null)}} {{:helper.link('Standard', 'credit-card', {'action' : 'hw_card', 'card' : 1}, data.hw_card == 1 ? "selected" : null)}} - - {{if data.devtype != 2}} - - - - - -
    Processor Unit: - {{:helper.link('Standard', 'tasks', {'action' : 'hw_cpu', 'cpu' : 1}, data.hw_cpu == 1 ? "selected" : null)}} - {{:helper.link('Advanced', 'tasks', {'action' : 'hw_cpu', 'cpu' : 2}, data.hw_cpu == 2 ? "selected" : null)}} -
    Tesla Relay: - {{:helper.link('None', 'times', {'action' : 'hw_tesla', 'tesla' : 0}, data.hw_tesla == 0 ? "selected" : null)}} - {{:helper.link('Standard', 'plug', {'action' : 'hw_tesla', 'tesla' : 1}, data.hw_tesla == 1 ? "selected" : null)}} -
    - {{/if}} - - -
    Confirm Order: - {{:helper.link('CONFIRM', 'check', {'action' : 'confirm_order'})}} + Processor Unit: + {{:helper.link('Standard', 'tasks', {'action' : 'hw_cpu', 'cpu' : 1}, data.hw_cpu == 1 ? "selected" : null)}} + {{:helper.link('Advanced', 'tasks', {'action' : 'hw_cpu', 'cpu' : 2}, data.hw_cpu == 2 ? "selected" : null)}}
    + + Tesla Relay: + {{:helper.link('None', 'times', {'action' : 'hw_tesla', 'tesla' : 0}, data.hw_tesla == 0 ? "selected" : null)}} + {{:helper.link('Standard', 'plug', {'action' : 'hw_tesla', 'tesla' : 1}, data.hw_tesla == 1 ? "selected" : null)}} + + {{/if}} + + + + +
    Confirm Order: + {{:helper.link('CONFIRM', 'check', {'action' : 'confirm_order'})}} +

    Battery allows your device to operate without external utility power source. Advanced batteries increase battery life.
    @@ -78,7 +75,7 @@

    Step 3: Payment

    Your device is now ready for fabrication..
    Please swipe your identification card to finish purchase.
    - Total price: {{:data.totalprice}}T + Total price: {{:data.totalprice}}C {{else data.state == 3}}

    Step 4: Thank you for your purchase

    Should you experience any issues with your new device, contact technical support at support@computerservice.nt diff --git a/nano/templates/ntnet_downloader.tmpl b/nano/templates/ntnet_downloader.tmpl index 3e4e90569f5..a64ae3452b2 100644 --- a/nano/templates/ntnet_downloader.tmpl +++ b/nano/templates/ntnet_downloader.tmpl @@ -68,7 +68,7 @@
    File controls
    -
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename, value.status})}}
    +
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename}, value.status)}}
    {{/for}} From 443df9e7de622e2c49663c859da03e15dc003b82 Mon Sep 17 00:00:00 2001 From: davipatury Date: Sat, 4 Mar 2017 22:31:20 -0300 Subject: [PATCH 22/22] Metastation changes. --- _maps/map_files/MetaStation/MetaStation.v41A.II.dmm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm index 9ecf54e59c3..d7e57e1b26f 100644 --- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm +++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm @@ -3457,7 +3457,7 @@ "boy" = (/obj/item/weapon/storage/box/lights/mixed,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plating,/area/maintenance/maintcentral{name = "Central Maintenance"}) "boz" = (/obj/item/clothing/mask/gas,/turf/simulated/floor/plating,/area/maintenance/maintcentral{name = "Central Maintenance"}) "boA" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/effect/spawner/window,/turf/simulated/floor/plating,/area/civilian/barber) -"boB" = (/obj/structure/cable/yellow{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/computer/card,/turf/simulated/floor/plasteel{icon_state = "dark"},/area/bridge) +"boB" = (/obj/structure/cable/yellow{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/modular_computer/console/preset/command,/turf/simulated/floor/plasteel{icon_state = "dark"},/area/bridge) "boC" = (/obj/structure/stool/bed/chair/office/dark{dir = 8},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/bridge) "boD" = (/obj/structure/stool/bed/chair/office/dark{dir = 1},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/bridge) "boE" = (/turf/simulated/floor/plasteel{icon_state = "dark"},/area/bridge) @@ -5344,7 +5344,7 @@ "bYN" = (/obj/machinery/atmospherics/trinary/filter{dir = 1; on = 1},/obj/structure/window/reinforced{dir = 4; pixel_x = 0},/obj/structure/window/reinforced,/turf/simulated/floor/plasteel{icon_state = "purplefull"},/area/atmos) "bYO" = (/obj/machinery/atmospherics/unary/outlet_injector/on{dir = 8; frequency = 1441; id = "tox_in"; pixel_y = 1},/turf/simulated/floor/engine{carbon_dioxide = 0; name = "plasma floor"; nitrogen = 0; oxygen = 0; toxins = 70000},/area/atmos) "bYP" = (/obj/machinery/camera{c_tag = "Atmospherics Tank - Toxins"; dir = 8; network = list("SS13"); pixel_x = 0; pixel_y = 0},/turf/simulated/floor/engine{carbon_dioxide = 0; name = "plasma floor"; nitrogen = 0; oxygen = 0; toxins = 70000},/area/atmos) -"bYQ" = (/obj/machinery/light/small,/obj/machinery/camera{c_tag = "Telecoms - Server Room - Aft"; dir = 1; network = list("SS13","tcomm")},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/bluegrid{icon_state = "dark"; name = "Mainframe Floor"; nitrogen = 100; oxygen = 0; temperature = 80},/area/tcommsat/server) +"bYQ" = (/obj/machinery/light/small,/obj/machinery/camera{c_tag = "Telecoms - Server Room - Aft"; dir = 1; network = list("SS13","tcomm")},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/ntnet_relay,/turf/simulated/floor/bluegrid{icon_state = "dark"; name = "Mainframe Floor"; nitrogen = 100; oxygen = 0; temperature = 80},/area/tcommsat/server) "bYR" = (/obj/structure/cable/yellow{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/cable/yellow{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plating{icon_plating = "warnplate"; icon_state = "warnplate"},/area/maintenance/fpmaint2{name = "Port Maintenance"}) "bYS" = (/obj/structure/cable/yellow{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 5},/turf/simulated/floor/plating,/area/maintenance/fpmaint2{name = "Port Maintenance"}) "bYT" = (/obj/structure/cable/yellow{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/effect/spawner/lootdrop{loot = list(/obj/structure/grille,/obj/structure/grille,/obj/structure/grille,/obj/structure/grille,/obj/structure/grille,/obj/item/weapon/cigbutt,/obj/item/trash/cheesie,/obj/item/trash/candy,/obj/item/trash/chips,/obj/item/trash/pistachios,/obj/item/trash/plate,/obj/item/trash/popcorn,/obj/item/trash/raisins,/obj/item/trash/sosjerky,/obj/item/trash/syndi_cakes); name = "maint grille or trash spawner"},/turf/simulated/floor/plating,/area/maintenance/fpmaint2{name = "Port Maintenance"})