From cb151844eab0218875e3cdbce5b862ce2c759c11 Mon Sep 17 00:00:00 2001 From: Francinum <5572280+francinum@users.noreply.github.com> Date: Wed, 14 Jan 2026 04:36:45 -0500 Subject: [PATCH 1/3] HTML -> ANSI-based styling A lot of computer4 programs need to be altered for this. More helpers need to be made. It's 5am. I'm going to sleep and kapu has tired of my bullshit. --- code/__DEFINES/computer4_defines.dm | 31 +++++++++++++- code/modules/computer4/computer4.dm | 16 +++---- .../data/terminal/directive/directman.dm | 42 +++++++++---------- .../data/terminal/operating_system.dm | 2 +- .../data/terminal/test_progs/dbg_ansi.dm | 26 ++++++++++++ .../data/terminal/thinkdos/thinkdos.dm | 30 ++++++------- daedalus.dme | 1 + tgui/package.json | 1 + .../Terminal/TerminalOutputSection.tsx | 9 +++- tgui/packages/tgui/package.json | 1 + tgui/yarn.lock | 18 ++++++++ 11 files changed, 130 insertions(+), 47 deletions(-) create mode 100644 code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm diff --git a/code/__DEFINES/computer4_defines.dm b/code/__DEFINES/computer4_defines.dm index c18764ef04fa..0ed882adf3aa 100644 --- a/code/__DEFINES/computer4_defines.dm +++ b/code/__DEFINES/computer4_defines.dm @@ -31,6 +31,35 @@ // Well-Known Directories #define THINKDOS_BIN_DIRECTORY "/bin" // Constants -#define THINKDOS_MAX_COMMANDS 3 //! The maximum amount of commands +#define THINKDOS_MAX_COMMANDS 10 //! The maximum amount of commands // Symbols #define THINKDOS_SYMBOL_SEPARATOR ";" //! Lets you split stdin into distinct commands + +//ANSI color helpers. + +/// ANSI CSI seq `ESC [` +#define ANSI_CSI "\x1b\x5B" + +/// Generate an ANSI SGR escape code dynamically. +#define ANSI_SGR(ID) "[ANSI_CSI][ID]m" +/// Internal only, Variant of `ANSI_SGR` that can const fold and stringifies literally. +#define _ANSI_SGR_CONSTFOLD(ID) (ANSI_CSI + #ID + "m") + +#define ANSI_FULL_RESET _ANSI_SGR_CONSTFOLD(0) + +#define ANSI_BOLD _ANSI_SGR_CONSTFOLD(1) +#define ANSI_UNBOLD _ANSI_SGR_CONSTFOLD(22) + + +#define ANSI_FG_RED _ANSI_SGR_CONSTFOLD(31) +#define ANSI_FG_GREEN _ANSI_SGR_CONSTFOLD(32) +#define ANSI_FG_YELLOW _ANSI_SGR_CONSTFOLD(33) +#define ANSI_FG_BLUE _ANSI_SGR_CONSTFOLD(34) + +#define ANSI_FG_RESET _ANSI_SGR_CONSTFOLD(39) + +// ANSI wrapper helpers. + +// Please understand that these might have unexpected styling side effects, as ANSI isn't inherently closed like HTML. + +#define ANSI_WRAP_BOLD(bolded_text) (ANSI_BOLD+bolded_text+ANSI_UNBOLD) diff --git a/code/modules/computer4/computer4.dm b/code/modules/computer4/computer4.dm index 07e463627058..d22925021729 100644 --- a/code/modules/computer4/computer4.dm +++ b/code/modules/computer4/computer4.dm @@ -371,10 +371,10 @@ TYPEINFO_DEF(/obj/machinery/computer4) /obj/machinery/computer4/proc/post_system() text_buffer = "" - text_buffer += "Initializing system...
" + text_buffer += "Initializing system...\n" if(!internal_disk) - text_buffer = "1701 - NO FIXED DISK
" + text_buffer = "1701 - NO FIXED DISK\n" // Os already known. if(operating_system) @@ -387,25 +387,25 @@ TYPEINFO_DEF(/obj/machinery/computer4) var/datum/c4_file/terminal_program/operating_system/new_os = locate() in inserted_disk?.root.contents if(new_os) - text_buffer += "Booting from inserted drive...
" + text_buffer += "Booting from inserted drive...\n" set_operating_system(new_os) else - text_buffer += "Non-system disk or disk error.
" + text_buffer += "Non-system disk or disk error.\n" // Okay how about the internal drive? if(!operating_system && internal_disk) var/datum/c4_file/terminal_program/operating_system/new_os = locate() in internal_disk?.root.contents if(new_os) - text_buffer += "Booting from fixed drive...
" + text_buffer += "Booting from fixed drive...\n" set_operating_system(new_os) else - text_buffer += "Unable to boot from fixed drive.
" + text_buffer += "Unable to boot from fixed drive.\n" // Fuck. if(!operating_system) - text_buffer += "ERR - BOOT FAILURE
" + text_buffer += "ERR - BOOT FAILURE\n" SStgui.update_uis(src) @@ -416,7 +416,7 @@ TYPEINFO_DEF(/obj/machinery/computer4) if(operating_system) set_operating_system(null) - text_buffer = "Rebooting system...
" + text_buffer = "Rebooting system...\n" tgui_input_history = list() tgui_input_index = list() diff --git a/code/modules/computer4/data/terminal/directive/directman.dm b/code/modules/computer4/data/terminal/directive/directman.dm index b6a822d8709e..7d7f5cb59f81 100644 --- a/code/modules/computer4/data/terminal/directive/directman.dm +++ b/code/modules/computer4/data/terminal/directive/directman.dm @@ -103,23 +103,23 @@ current_menu = DIRECTMAN_MENU_HOME var/list/out = list( - @"
 ┌┬┐┬┬─┐┌─┐┌─┐┌┬┐╔╦╗╔═╗╔╗╔
", - @"
  │││├┬┘├┤ │   │ ║║║╠═╣║║║
", - @"
 ─┴┘┴┴└─└─┘└─┘ ┴ ╩ ╩╩ ╩╝╚╝
", - "Commands:
", - "\[1\] View Current Directives
", + @" ┌┬┐┬┬─┐┌─┐┌─┐┌┬┐╔╦╗╔═╗╔╗╔", + @" │││├┬┘├┤ │ │ ║║║╠═╣║║║", + @" ─┴┘┴┴└─└─┘└─┘ ┴ ╩ ╩╩ ╩╝╚╝", + "Commands:", + "\[1\] View Current Directives", ) if(SSdirectives.get_directives_for_selection()) - out += "\[2\] View New Directives
" + out += "\[2\] View New Directives" - out += "
\[R\] Refresh" + out += "\[R\] Refresh" - get_os().println(jointext(out, null)) + get_os().println(jointext(out, "\n")) /datum/c4_file/terminal_program/directman/proc/view_current() if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) - get_os().println("Error: Unable to locate wireless adapter.") + get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return current_menu = DIRECTMAN_MENU_CURRENT @@ -130,13 +130,13 @@ for(var/datum/directive/directive as anything in SSdirectives.get_active_directives()) i++ out += "\[[fit_with_zeros("[i]", 2)]\] [directive.name]" - out += "
\[B\] Return" + out += "\n\[B\] Return" - get_os().println(jointext(out, "
")) + get_os().println(jointext(out, "\n")) /datum/c4_file/terminal_program/directman/proc/view_directive(index) if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) - get_os().println("Error: Unable to locate wireless adapter.") + get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return current_menu = DIRECTMAN_MENU_ACTIVE_DIRECTIVE @@ -148,18 +148,18 @@ "Description: [directive.desc]", "Severity: [directive.severity]", "Time Given: [active_directives[directive]]", - "
\[B\] Return", + "\n\[B\] Return", ) - get_os().println(jointext(out, "
")) + get_os().println(jointext(out, "\n")) /datum/c4_file/terminal_program/directman/proc/view_new() if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) - get_os().println("Error: Unable to locate wireless adapter.") + get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return if(!SSdirectives.get_directives_for_selection()) - get_os().println("Error: No new directives to display.") + get_os().println("[ANSI_WRAP_BOLD("Error:")] No new directives to display.") return current_menu = DIRECTMAN_MENU_NEW_DIRECTIVES @@ -170,14 +170,14 @@ for(var/datum/directive/directive as anything in SSdirectives.get_directives_for_selection()) i++ out += "\[[fit_with_zeros("[i]", 2)]\] [directive.name]" - out += "
\[B\] Return" + out += "\n\[B\] Return" - get_os().println(jointext(out, "
")) + get_os().println(jointext(out, "\n")) /datum/c4_file/terminal_program/directman/proc/view_new_directive(index) if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) view_home() - get_os().println("Error: Unable to locate wireless adapter.") + get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return get_os().clear_screen(TRUE) @@ -190,7 +190,7 @@ "Description: [viewing_directive.desc]", "Payout: [viewing_directive.reward] mark\s", "Severity: [viewing_directive.severity]", - "
\[B\] Return | \[S\] Select" + "\n\[B\] Return | \[S\] Select" ) - get_os().println(jointext(out, "
")) + get_os().println(jointext(out, "\n")) diff --git a/code/modules/computer4/data/terminal/operating_system.dm b/code/modules/computer4/data/terminal/operating_system.dm index 02c84426ad4c..91601d2aa73b 100644 --- a/code/modules/computer4/data/terminal/operating_system.dm +++ b/code/modules/computer4/data/terminal/operating_system.dm @@ -109,7 +109,7 @@ var/obj/machinery/computer4/computer = get_computer() - computer.text_buffer += "[text]
" + computer.text_buffer += "[text]\n" if(update_ui) SStgui.update_uis(computer) return TRUE diff --git a/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm b/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm new file mode 100644 index 000000000000..10eccb6b714b --- /dev/null +++ b/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm @@ -0,0 +1,26 @@ +/datum/c4_file/terminal_program/ansi_test + name = "dbg_ansi" + size = 1 + +/datum/c4_file/terminal_program/ansi_test/execute(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/parsed_cmdline/cmdline) + . = ..() + //Iterate and test all SGR parameters in standard form, or special form as needed. + + var/print_all = !!cmdline.options.Find("a") + + + system.println("#----------#-----------#-----------#") + for(var/param_id in 0 to 107) + switch(param_id) + //Unsupported/undefined + if(5-7,20,25-27,50,52,56-57,60-72,76-89,98-99,) + if(print_all) + system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | NOT SUPPORTED |") + continue + if(38,48,58) + system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | Uses Extended Color |") + else + system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | Test Text | [ANSI_SGR(param_id)]Test Text[ANSI_FULL_RESET] |") + system.println("#----------#-----------#-----------#") + system.unload_program(src) + return diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm b/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm index 1fa7e3ef8682..da6cb978b4ed 100644 --- a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm +++ b/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm @@ -32,21 +32,21 @@ /datum/c4_file/terminal_program/operating_system/thinkdos/execute() if(!initialize_logs()) - println("Log system failure.") + println("[ANSI_FG_RED]Log system failure.[ANSI_FG_RESET]") if(!needs_login) println("Account system disabled.") else if(!initialize_accounts()) - println("Unable to start account system.") + println("[ANSI_FG_RED]Unable to start account system.[ANSI_FG_RESET]") change_dir(containing_folder) var/title_text = list( - @"
 ___  _    _       _    ___  ___  ___
", - @"
|_ _|| |_ <_>._ _ | |__| . \| . |/ __>
", - @"
 | | | . || || ' || / /| | || | |\__ \
", - @"
 |_| |_|_||_||_|_||_\_\|___/`___'<___/
", + "[ANSI_FG_RED]", @" ___ _ _ _ ___ ___ ___ ", "\n", + "[ANSI_FG_GREEN]", @"|_ _|| |_ <_>._ _ | |__| . \| . |/ __>", "\n", + "[ANSI_FG_YELLOW]",@" | | | . || || ' || / /| | || | |\__ \", "\n", + "[ANSI_FG_BLUE]", @" |_| |_|_||_||_|_||_\_\|___/`___'<___/", "[ANSI_FG_RESET]" ).Join("") println(title_text) @@ -172,16 +172,16 @@ login_user.access = text2access(account_access) set_current_user(login_user) - write_log("LOGIN: [html_encode(account_name)] | [html_encode(account_occupation)]") - println("Welcome [html_encode(account_name)]!
Current Directory: [current_directory.path_to_string()]") + write_log("[ANSI_WRAP_BOLD("LOGIN")]: [account_name] | [account_occupation]") + println("Welcome [html_encode(account_name)]!\n[ANSI_WRAP_BOLD("Current Directory: [current_directory.path_to_string()]")]") return TRUE /datum/c4_file/terminal_program/operating_system/thinkdos/proc/logout() if(!current_user) - print_error("Error: Account system inactive.") + print_error("[ANSI_WRAP_BOLD("Error")]: Account system inactive.") return FALSE - write_log("LOGOUT: [html_encode(current_user.registered_name)]") + write_log("[ANSI_WRAP_BOLD("LOGOUT")]: [current_user.registered_name]") set_current_user(null) return TRUE @@ -189,7 +189,7 @@ var/datum/c4_file/folder/account_dir = parse_directory("users") if(!istype(account_dir)) if(account_dir && !account_dir.containing_folder.try_delete_file(account_dir)) - print_error("Error: Unable to write account folder.") + print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account folder.") return FALSE account_dir = new @@ -197,7 +197,7 @@ if(!containing_folder.try_add_file(account_dir)) qdel(account_dir) - print_error("Error: Unable to write account folder.") + print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account folder.") return FALSE RegisterSignal(account_dir, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_COMPUTER4_FILE_REMOVED), PROC_REF(user_folder_gone)) @@ -205,7 +205,7 @@ var/datum/c4_file/user/user_data = account_dir.get_file("admin", FALSE) if(!istype(user_data)) if(user_data && !user_data.containing_folder.try_delete_file(user_data)) - print_error("Error: Unable to write account folder.") + print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account folder.") return FALSE user_data = new @@ -213,7 +213,7 @@ if(!account_dir.try_add_file(user_data)) qdel(user_data) - print_error("Error: Unable to write account file.") + print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account file.") return FALSE //set_current_user(user_data) @@ -255,7 +255,7 @@ command_log = log_file RegisterSignal(command_log, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_PARENT_QDELETING), PROC_REF(log_file_gone)) - log_file.data += "
STARTUP: [stationtime2text()], [stationdate2text()]" + log_file.data += "\n[ANSI_WRAP_BOLD("STARTUP")]: [stationtime2text()], [stationdate2text()]" return TRUE diff --git a/daedalus.dme b/daedalus.dme index 686efc0d1264..325b240c7dd6 100644 --- a/daedalus.dme +++ b/daedalus.dme @@ -3027,6 +3027,7 @@ #include "code\modules\computer4\data\terminal\rtos\pincode_door.dm" #include "code\modules\computer4\data\terminal\rtos\simple_door_control.dm" #include "code\modules\computer4\data\terminal\rtos\slave.dm" +#include "code\modules\computer4\data\terminal\test_progs\dbg_ansi.dm" #include "code\modules\computer4\data\terminal\test_progs\flagtest.dm" #include "code\modules\computer4\data\terminal\thinkdos\thinkdos.dm" #include "code\modules\computer4\data\terminal\thinkdos\thinkdos_commands.dm" diff --git a/tgui/package.json b/tgui/package.json index 7abbb0584e8a..878ed0972ea7 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -38,6 +38,7 @@ "eslint-plugin-simple-import-sort": "^12.1.0", "eslint-plugin-typescript-sort-keys": "^3.2.0", "eslint-plugin-unused-imports": "^3.2.0", + "fancy-ansi": "^0.1.3", "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-circus": "^29.7.0", diff --git a/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx b/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx index 551ad1712e92..9fde7561b5b6 100644 --- a/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx +++ b/tgui/packages/tgui/interfaces/Terminal/TerminalOutputSection.tsx @@ -6,6 +6,7 @@ */ import { BooleanLike } from 'common/react'; +import { FancyAnsi } from 'fancy-ansi'; import { useEffect } from 'react'; import { Box, Section } from '../../components'; @@ -16,6 +17,8 @@ type TerminalOutputSectionProps = Pick< 'bgColor' | 'displayHTML' | 'fontColor' > & { noscroll?: BooleanLike }; +const fancyAnsi = new FancyAnsi(); + export const TerminalOutputSection = (props: TerminalOutputSectionProps) => { const { displayHTML, fontColor, bgColor, noscroll } = props; @@ -30,6 +33,10 @@ export const TerminalOutputSection = (props: TerminalOutputSectionProps) => { sectionContentElement.scrollTop = sectionContentElement.scrollHeight; }, [displayHTML]); + /* Whoops, lummox' JSON encoder is shoddy! We're being sent invalid UTF-16 + and need to go back and fix it before passing it into the ANSI decoder. */ + let fixed_html = displayHTML.replaceAll('\udc1b', '\u001b'); //Bad surrogate. + return (
{ color={fontColor} fontSize="1.2em" preserveWhitespace - dangerouslySetInnerHTML={{ __html: displayHTML }} + dangerouslySetInnerHTML={{ __html: fancyAnsi.toHtml(fixed_html) }} />
); diff --git a/tgui/packages/tgui/package.json b/tgui/packages/tgui/package.json index 350943ad9f09..e8ed605d16ba 100644 --- a/tgui/packages/tgui/package.json +++ b/tgui/packages/tgui/package.json @@ -8,6 +8,7 @@ "common": "workspace:*", "dateformat": "^5.0.3", "dompurify": "^3.2.6", + "fancy-ansi": "^0.1.3", "js-yaml": "^4.1.0", "marked": "^4.3.0", "react": "^19.1.0", diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 4ba4bd0acd6b..bacfbb1fd5d5 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -3772,6 +3772,13 @@ __metadata: languageName: node linkType: hard +"escape-html@npm:^1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3 + languageName: node + linkType: hard + "escape-string-regexp@npm:^2.0.0": version: 2.0.0 resolution: "escape-string-regexp@npm:2.0.0" @@ -4106,6 +4113,15 @@ __metadata: languageName: node linkType: hard +"fancy-ansi@npm:^0.1.3": + version: 0.1.3 + resolution: "fancy-ansi@npm:0.1.3" + dependencies: + escape-html: "npm:^1.0.3" + checksum: 10c0/691fdb86bbbe12318c2908fc6b725e08c17874b84f11dd22b5833663e5c77c03c5eb1142c6c779f66ab2f60f47038a09c822ee7fe7c0f043fa3c16f1dbb83216 + languageName: node + linkType: hard + "fantasticon@npm:^3.0.0": version: 3.0.0 resolution: "fantasticon@npm:3.0.0" @@ -8329,6 +8345,7 @@ __metadata: eslint-plugin-simple-import-sort: "npm:^12.1.0" eslint-plugin-typescript-sort-keys: "npm:^3.2.0" eslint-plugin-unused-imports: "npm:^3.2.0" + fancy-ansi: "npm:^0.1.3" file-loader: "npm:^6.2.0" jest: "npm:^29.7.0" jest-circus: "npm:^29.7.0" @@ -8357,6 +8374,7 @@ __metadata: common: "workspace:*" dateformat: "npm:^5.0.3" dompurify: "npm:^3.2.6" + fancy-ansi: "npm:^0.1.3" js-yaml: "npm:^4.1.0" marked: "npm:^4.3.0" react: "npm:^19.1.0" From 560f29af34f7f2321478dccb4a145629b78f33ab Mon Sep 17 00:00:00 2001 From: Francinum <5572280+francinum@users.noreply.github.com> Date: Thu, 15 Jan 2026 01:44:02 -0500 Subject: [PATCH 2/3] Purges most (probably missed something) remaining HTML styling --- code/__DEFINES/computer4_defines.dm | 3 + .../data/terminal/_terminal_program.dm | 4 +- .../data/terminal/directive/directman.dm | 6 +- .../terminal/directive/directman_commands.dm | 4 +- .../data/terminal/medtrak/medtrak.dm | 55 ++++--- .../terminal/medtrak/medtrak_edit_record.dm | 14 +- .../terminal/medtrak/medtrak_menu_commands.dm | 4 +- .../medtrak/medtrak_record_commands.dm | 4 +- .../data/terminal/netpage/netpage.dm | 14 +- .../data/terminal/netpage/netpage_commands.dm | 4 +- .../data/terminal/notepad/notepad.dm | 14 +- .../data/terminal/notepad/notepad_commands.dm | 24 +-- .../computer4/data/terminal/probe/probe.dm | 2 +- .../data/terminal/probe/probe_commands.dm | 8 +- .../computer4/data/terminal/rtos/_rtos.dm | 2 +- .../data/terminal/thinkdos/thinkdos.dm | 8 +- .../terminal/thinkdos/thinkdos_commands.dm | 154 +++++++++--------- 17 files changed, 165 insertions(+), 159 deletions(-) diff --git a/code/__DEFINES/computer4_defines.dm b/code/__DEFINES/computer4_defines.dm index 0ed882adf3aa..4c0d6a398dd3 100644 --- a/code/__DEFINES/computer4_defines.dm +++ b/code/__DEFINES/computer4_defines.dm @@ -55,6 +55,9 @@ #define ANSI_FG_GREEN _ANSI_SGR_CONSTFOLD(32) #define ANSI_FG_YELLOW _ANSI_SGR_CONSTFOLD(33) #define ANSI_FG_BLUE _ANSI_SGR_CONSTFOLD(34) +#define ANSI_FG_MAGENTA _ANSI_SGR_CONSTFOLD(35) +#define ANSI_FG_CYAN _ANSI_SGR_CONSTFOLD(36) +#define ANSI_FG_WHITE _ANSI_SGR_CONSTFOLD(37) #define ANSI_FG_RESET _ANSI_SGR_CONSTFOLD(39) diff --git a/code/modules/computer4/data/terminal/_terminal_program.dm b/code/modules/computer4/data/terminal/_terminal_program.dm index e0ba95f0136f..a8d776e4c917 100644 --- a/code/modules/computer4/data/terminal/_terminal_program.dm +++ b/code/modules/computer4/data/terminal/_terminal_program.dm @@ -17,11 +17,11 @@ return TRUE if(!system.current_user) - system.println("Error: Unable to locate credentials.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate credentials.") return FALSE if(length(req_access & system.current_user.access) != length(req_access)) - system.println("Error: User '[html_encode(system.current_user.registered_name)]' does not have the required access credentials.") + system.println("[ANSI_WRAP_BOLD("Error:")] User '[html_encode(system.current_user.registered_name)]' does not have the required access credentials.") return FALSE return TRUE diff --git a/code/modules/computer4/data/terminal/directive/directman.dm b/code/modules/computer4/data/terminal/directive/directman.dm index 7d7f5cb59f81..ceb1caffe0f6 100644 --- a/code/modules/computer4/data/terminal/directive/directman.dm +++ b/code/modules/computer4/data/terminal/directive/directman.dm @@ -24,7 +24,7 @@ . = ..() if(!system.get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) - system.println("Error: Unable to locate wireless adapter.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") system.clear_screen(TRUE) view_home() @@ -81,7 +81,7 @@ system.clear_screen(TRUE) if(!system.get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) view_home() - system.println("Error: Unable to locate wireless adapter.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return view_new() return TRUE @@ -90,7 +90,7 @@ if(lowertext(parsed_cmdline.raw) == "s") if(!system.get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) view_home() - system.println("Error: Unable to locate wireless adapter.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return addtimer(CALLBACK(SSdirectives, TYPE_PROC_REF(/datum/controller/subsystem/directives, enact_directive), viewing_directive), rand(3, 10) SECONDS) diff --git a/code/modules/computer4/data/terminal/directive/directman_commands.dm b/code/modules/computer4/data/terminal/directive/directman_commands.dm index b8a2a212e9b0..012c952c2931 100644 --- a/code/modules/computer4/data/terminal/directive/directman_commands.dm +++ b/code/modules/computer4/data/terminal/directive/directman_commands.dm @@ -29,7 +29,7 @@ system.clear_screen(TRUE) if(!system.get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) directman.view_home() - system.println("Error: Unable to locate wireless adapter.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return directman.view_current() @@ -42,7 +42,7 @@ system.clear_screen(TRUE) if(!system.get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD)) directman.view_home() - system.println("Error: Unable to locate wireless adapter.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.") return directman.view_new() diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak.dm b/code/modules/computer4/data/terminal/medtrak/medtrak.dm index 14d57b2effc3..5f99f1a46339 100644 --- a/code/modules/computer4/data/terminal/medtrak/medtrak.dm +++ b/code/modules/computer4/data/terminal/medtrak/medtrak.dm @@ -51,7 +51,7 @@ medical_records = SSdatacore.library[DATACORE_RECORDS_MEDICAL] var/datum/c4_file/folder/log_dir = system.get_log_folder() if(!log_dir) - system.println("Error: Unable to locate logging directory.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate logging directory.") var/datum/c4_file/text/record_log = log_dir.get_file("medtrak_logs") if(!istype(record_log)) @@ -59,7 +59,7 @@ log_file.set_name("medtrak_logs") if(!log_dir.try_add_file(log_file)) qdel(log_file) - system.println("Error: Unable to write log file.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to write log file.") write_log("[system.current_user.registered_name] accessed the records database.") @@ -123,7 +123,7 @@ return TRUE if(!(input_num in 1 to length(medical_records.records))) - system.println("Error: Array index out of bounds.") + system.println("[ANSI_WRAP_BOLD("Error:")] Array index out of bounds.") return TRUE var/datum/data/record/R = medical_records.records[input_num] @@ -157,13 +157,16 @@ /// Prints the home menu options /datum/c4_file/terminal_program/medtrak/proc/home_text() var/title_text = list( - @(eol)"
 __  __              _    _____                    _
"eol, - @(eol)"
|  \/  |   ___    __| |  |_   _|    _ _   __ _    | |__
"eol, - @(eol)"
| |\/| |  / -_)  / _` |    | |     | '_| / _` |   | / /
"eol, - @(eol)"
|_|__|_|  \___|  \__,_|   _|_|_   _|_|_  \__,_|   |_\_\
"eol, - @(eol)"
_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|
"eol, - @(eol)"
 `-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
"eol, - ).Join("") + "[ANSI_FG_CYAN]", + @(eol)" __ __ _ _____ _ "eol, + @(eol)"| \/ | ___ __| | |_ _| _ _ __ _ | |__ "eol, + @(eol)"| |\/| | / -_) / _` | | | | '_| / _` | | / / "eol, + @(eol)"|_|__|_| \___| \__,_| _|_|_ _|_|_ \__,_| |_\_\ "eol, + "[ANSI_FG_WHITE]", + @(eol)"_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|"eol, + @(eol)" `-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"eol, + "[ANSI_FG_RESET]", + ).Join("\n") current_menu = MEDTRAK_MENU_HOME var/list/out = list( @@ -173,7 +176,7 @@ "(2) Record Search", "(0) Quit", ) - get_os().println(jointext(out, "
")) + get_os().println(jointext(out, "\n")) /datum/c4_file/terminal_program/medtrak/proc/await_input(text, datum/callback/on_input) var/datum/c4_file/terminal_program/operating_system/thinkdos/system = get_os() @@ -196,7 +199,7 @@ system.clear_screen(TRUE) if(!length(medical_records.records)) - system.println("Error: No records found in database.
Enter 'back' to return.") + system.println("[ANSI_WRAP_BOLD("Error:")] No records found in database.\nEnter 'back' to return.") return var/list/out = list() @@ -206,10 +209,10 @@ for(var/i in 1 to length(medical_records.records)) var/datum/data/record/R = medical_records.records[i] - out +="\[[fit_with_zeros("[i]", zeros)]\][R.fields[DATACORE_ID]]: [R.fields[DATACORE_NAME]]" + out +="[ANSI_WRAP_BOLD("\[[fit_with_zeros("[i]", zeros)]\]")][R.fields[DATACORE_ID]]: [R.fields[DATACORE_NAME]]" - out += "
(#) View record | (new) New record | (back) Return to home" - system.println(jointext(out, "
")) + out += "\n(#) View record | (new) New record | (back) Return to home" + system.println(jointext(out, "\n")) /datum/c4_file/terminal_program/medtrak/proc/view_record(datum/data/record/R) if(isnull(R)) @@ -223,7 +226,7 @@ var/list/fields = current_record.fields var/list/out = list( - "Record Data:", + "[ANSI_WRAP_BOLD("Record Data:")]", "\[01\] Name: [fields[DATACORE_NAME]] | ID: [fields[DATACORE_ID]]", "\[02\] Sex: [fields[DATACORE_GENDER]]", "\[03\] Age: [fields[DATACORE_AGE]]", @@ -236,11 +239,11 @@ "\[10\] Physical Status: [fields[DATACORE_PHYSICAL_HEALTH]]", "\[11\] Mental Status: [fields[DATACORE_MENTAL_HEALTH]]", "\[12\] Notes: [fields[DATACORE_NOTES]]", - "
Enter field number to edit a field", + "\nEnter field number to edit a field", "(C) Comments | (R) Refresh | (D) Delete | (P) Print | (0) Return to index" ) - get_os().println(jointext(out, "
")) + get_os().println(jointext(out, "\n")) /datum/c4_file/terminal_program/medtrak/proc/record_input_num(number) switch(number) @@ -264,19 +267,19 @@ await_input("Enter new Allergies (Max Length: [MAX_MESSAGE_LEN])", CALLBACK(src, PROC_REF(edit_allergies))) if(10) await_input( - {"Edit Physical Status
- \[1\] [PHYSHEALTH_OK]
- \[2\] [PHYSHEALTH_CARE]
+ {"Edit Physical Status\n + \[1\] [PHYSHEALTH_OK]\n + \[2\] [PHYSHEALTH_CARE]\n \[3\] [PHYSHEALTH_DECEASED] "}, CALLBACK(src, PROC_REF(edit_physical_health)) ) if(11) await_input( - {"Edit Mental Status
- \[1\] [MENHEALTH_OK]
- \[2\] [MENHEALTH_WATCH]
- \[3\] [MENHEALTH_UNSTABLE]
+ {"Edit Mental Status\n + \[1\] [MENHEALTH_OK]\n + \[2\] [MENHEALTH_WATCH]\n + \[3\] [MENHEALTH_UNSTABLE]\n \[4\] [MENHEALTH_INSANE] "}, CALLBACK(src, PROC_REF(edit_mental_health)) @@ -301,6 +304,6 @@ count++ out += "\[[fit_with_zeros("[count]", 2)]\] [comment]" - system.println(jointext(out, "
")) + system.println(jointext(out, "\n")) system.println("(N) New comment | (0) Return to record") diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak_edit_record.dm b/code/modules/computer4/data/terminal/medtrak/medtrak_edit_record.dm index 0bdc6fb99c3d..d20da4383e6f 100644 --- a/code/modules/computer4/data/terminal/medtrak/medtrak_edit_record.dm +++ b/code/modules/computer4/data/terminal/medtrak/medtrak_edit_record.dm @@ -98,9 +98,9 @@ if(!(choice in 1 to length(options))) await_input( - {"Edit Physical Status
- \[1\] [PHYSHEALTH_OK]
- \[2\] [PHYSHEALTH_CARE]
+ {"Edit Physical Status\n + \[1\] [PHYSHEALTH_OK]\n + \[2\] [PHYSHEALTH_CARE]\n \[3\] [PHYSHEALTH_DECEASED] "}, CALLBACK(src, PROC_REF(edit_physical_health)) @@ -121,10 +121,10 @@ if(!(choice in 1 to length(options))) await_input( - {"Edit Mental Status
- \[1\] [MENHEALTH_OK]
- \[2\] [MENHEALTH_WATCH]
- \[3\] [MENHEALTH_UNSTABLE]
+ {"Edit Mental Status\n + \[1\] [MENHEALTH_OK]\n + \[2\] [MENHEALTH_WATCH]\n + \[3\] [MENHEALTH_UNSTABLE]\n \[4\] [MENHEALTH_INSANE] "}, CALLBACK(src, PROC_REF(edit_mental_health)) diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak_menu_commands.dm b/code/modules/computer4/data/terminal/medtrak/medtrak_menu_commands.dm index 46f9c29ce9ba..d9ea0ef160f0 100644 --- a/code/modules/computer4/data/terminal/medtrak/medtrak_menu_commands.dm +++ b/code/modules/computer4/data/terminal/medtrak/medtrak_menu_commands.dm @@ -47,9 +47,9 @@ var/i for(var/datum/data/record/found_record as anything in results) i++ - out += "\[[fit_with_zeros("[i]", 3)]\] [found_record.fields[DATACORE_ID]]: [found_record.fields[DATACORE_NAME]]" + out += "[ANSI_WRAP_BOLD("\[[fit_with_zeros("[i]", 3)]\]")] [found_record.fields[DATACORE_ID]]: [found_record.fields[DATACORE_NAME]]" - medtrak.await_input(jointext(out, "
"), CALLBACK(src, PROC_REF(fulfill_search), results)) + medtrak.await_input(jointext(out, "\n"), CALLBACK(src, PROC_REF(fulfill_search), results)) return /datum/shell_command/medtrak/home/search/proc/fulfill_search(list/results, datum/c4_file/terminal_program/medtrak/medtrak, datum/parsed_cmdline/stdin) diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak_record_commands.dm b/code/modules/computer4/data/terminal/medtrak/medtrak_record_commands.dm index 9c64a13fd9e9..ec60b8104100 100644 --- a/code/modules/computer4/data/terminal/medtrak/medtrak_record_commands.dm +++ b/code/modules/computer4/data/terminal/medtrak/medtrak_record_commands.dm @@ -54,11 +54,11 @@ var/datum/c4_file/terminal_program/medtrak/medtrak = program var/obj/item/peripheral/printer/printer = system.get_computer().get_peripheral(PERIPHERAL_TYPE_PRINTER) if(!printer) - system.println("Error: Unable to locate printer.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate printer.") return if(printer.busy) - system.println("Error: Printer is busy.") + system.println("[ANSI_WRAP_BOLD("Error:")] Printer is busy.") return var/list/fields = medtrak.current_record.fields diff --git a/code/modules/computer4/data/terminal/netpage/netpage.dm b/code/modules/computer4/data/terminal/netpage/netpage.dm index 1e662ac54345..ace88ba60797 100644 --- a/code/modules/computer4/data/terminal/netpage/netpage.dm +++ b/code/modules/computer4/data/terminal/netpage/netpage.dm @@ -24,10 +24,10 @@ system.clear_screen(TRUE) var/title_text = list( - @"
      ___ ___  __        __   ___
", - @"
|\ | |__   |  |__)  /\  / _` |__ 
", - @"
| \| |___  |  |    /~~\ \__> |___
", - ).Join("") + @" ___ ___ __ __ ___", + @"|\ | |__ | |__) /\ / _` |__ ", + @"| \| |___ | | /~~\ \__> |___", + ).Join("\n") system.println(title_text) check_for_errors() @@ -48,18 +48,18 @@ var/datum/c4_file/terminal_program/operating_system/thinkdos/system = get_os() if(!get_adapter()) - system.println("Error: Unable to locate network adapter.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate network adapter.") . = TRUE if(system.needs_login) return . if(!get_reader()) - system.println("Error: Unable to locate card reader.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate card reader.") return TRUE if(!get_reader().inserted_card) - system.println("Error: No card inserted.") + system.println("[ANSI_WRAP_BOLD("Error:")] No card inserted.") return TRUE return FALSE diff --git a/code/modules/computer4/data/terminal/netpage/netpage_commands.dm b/code/modules/computer4/data/terminal/netpage/netpage_commands.dm index ee84fc9373cc..1bf27221595d 100644 --- a/code/modules/computer4/data/terminal/netpage/netpage_commands.dm +++ b/code/modules/computer4/data/terminal/netpage/netpage_commands.dm @@ -15,7 +15,7 @@ return if(!options["network"]) - system.println("Syntax: post --network=\[network ID\] \[message\].
Type 'networks' to view networks you may broadcast on.") + system.println("Syntax: post --network=\[network ID\] \[message\].\nType 'networks' to view networks you may broadcast on.") return var/list/valid_arg_options = list() @@ -23,7 +23,7 @@ valid_arg_options[info.arg_name] = info.pager_class if(!(options["network"] in valid_arg_options)) - system.println("Error: Invalid network ID.") + system.println("[ANSI_WRAP_BOLD("Error:")] Invalid network ID.") return var/message = "[stationtime2text("hh:mm")] | [jointext(arguments, " ")]" diff --git a/code/modules/computer4/data/terminal/notepad/notepad.dm b/code/modules/computer4/data/terminal/notepad/notepad.dm index 6fa24200ab38..6d37ca528dd9 100644 --- a/code/modules/computer4/data/terminal/notepad/notepad.dm +++ b/code/modules/computer4/data/terminal/notepad/notepad.dm @@ -24,13 +24,13 @@ note_list = list() var/title_text = list( - @"
__________________________________________________
", - @"
    _____                _____                    
", - @"
    /    )               /    )               /   
", - @"
---/----/----__----__---/----/----__----__---/-__-
", - @"
  /    /   /   ) /   ' /    /   /   ) /   ' /(    
", - @"
_/____/___(___/_(___ _/____/___(___/_(___ _/___\__
", - ).Join("") + @"__________________________________________________", + @" _____ _____ ", + @" / ) / ) / ", + @"---/----/----__----__---/----/----__----__---/-__-", + @" / / / ) / ' / / / ) / ' /( ", + @"_/____/___(___/_(___ _/____/___(___/_(___ _/___\__", + ).Join("\n") system.clear_screen(TRUE) system.println(title_text) system.println("Welcome to DocDock, type !help to get started.") diff --git a/code/modules/computer4/data/terminal/notepad/notepad_commands.dm b/code/modules/computer4/data/terminal/notepad/notepad_commands.dm index e0520a1f45c4..c8766411878e 100644 --- a/code/modules/computer4/data/terminal/notepad/notepad_commands.dm +++ b/code/modules/computer4/data/terminal/notepad/notepad_commands.dm @@ -40,13 +40,13 @@ sortTim(output, GLOBAL_PROC_REF(cmp_text_asc)) output.Insert(1, "Typing text without a '!' prefix will write to the current line.", - "You can change lines by typing '!\[number\]'. Zero will change to highest line number.
", + "You can change lines by typing '!\[number\]'. Zero will change to highest line number.\n", "Use help \[command\] to see specific information about a command.", "List of available commands:" ) - system.println(jointext(output, "
")) + system.println(jointext(output, "\n")) /datum/shell_command/notepad/edit_cmd/quit aliases = list("quit", "q", "exit") @@ -75,7 +75,7 @@ /datum/shell_command/notepad/edit_cmd/delete/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/datum/c4_file/terminal_program/notepad/notepad = program if(!length(notepad.note_list)) - system.println("Error: Nothing to delete.") + system.println("[ANSI_WRAP_BOLD("Error:")] Nothing to delete.") return if(notepad.working_line == 0) @@ -102,7 +102,7 @@ print += "\[[fit_with_zeros("[i]", 3)]\] [note] [assoc_note ? "=[assoc_note]" : ""]" system.clear_screen(TRUE) - system.println(jointext(print, "
")) + system.println(jointext(print, "\n")) /datum/shell_command/notepad/edit_cmd/load_note aliases = list("load", "l") @@ -123,7 +123,7 @@ else if(istype(found_file, /datum/c4_file/text)) var/datum/c4_file/text/text = found_file - notepad.note_list = splittext(text.data, "
") + notepad.note_list = splittext(text.data, "\n") else system.println("Error: File not found.") return @@ -145,12 +145,12 @@ var/datum/c4_file/record/existing_file = system.resolve_filepath(path_info.raw) if(existing_file && !istype(existing_file, /datum/c4_file/record)) - system.println("Error: Name in use.") + system.println("[ANSI_WRAP_BOLD("Error:")] Name in use.") return if(existing_file) if(existing_file.drive.read_only) - system.println("Error: Cannot open file for write.") + system.println("[ANSI_WRAP_BOLD("Error")]: Cannot open file for write.") return existing_file.stored_record.fields = notepad.note_list.Copy() @@ -159,7 +159,7 @@ var/datum/c4_file/folder/dest_folder = system.parse_directory(path_info.directory, system.current_directory) if(!dest_folder || dest_folder.drive.read_only) - system.println("Error: Cannot open directory for write.") + system.println("[ANSI_WRAP_BOLD("Error")]: Cannot open directory for write.") return existing_file = new @@ -169,7 +169,7 @@ system.println("File saved to [existing_file.path_to_string()].") else qdel(existing_file) - system.println("Error: Unable to save to directory.") + system.println("[ANSI_WRAP_BOLD("Error")]: Unable to save to directory.") /datum/shell_command/notepad/edit_cmd/print aliases = list("print", "p") @@ -179,12 +179,12 @@ var/datum/c4_file/terminal_program/notepad/notepad = program var/obj/item/peripheral/printer/printer = system.get_computer().get_peripheral(PERIPHERAL_TYPE_PRINTER) if(!printer) - system.println("Error: Unable to locate printer.") + system.println("[ANSI_WRAP_BOLD("Error:")] Unable to locate printer.") return if(printer.busy) - system.println("Error: Printer is busy.") + system.println("[ANSI_WRAP_BOLD("Error:")] Printer is busy.") return - printer.print(jointext(notepad.note_list, "
"), html_encode(trim(jointext(arguments, ""))) || "printout") + printer.print(jointext(notepad.note_list, "\n"), html_encode(trim(jointext(arguments, ""))) || "printout") system.println("Printing...") diff --git a/code/modules/computer4/data/terminal/probe/probe.dm b/code/modules/computer4/data/terminal/probe/probe.dm index bf154cf32df5..22df1555777c 100644 --- a/code/modules/computer4/data/terminal/probe/probe.dm +++ b/code/modules/computer4/data/terminal/probe/probe.dm @@ -22,7 +22,7 @@ system.println("Welcome to NetProbe, type 'help' to get started.") if(!get_adapter()) - system.println("Error: No network adapter found.") + system.println("[ANSI_WRAP_BOLD("Error:")] No network adapter found.") /// Getter for a network adapter. /datum/c4_file/terminal_program/probe/proc/get_adapter() diff --git a/code/modules/computer4/data/terminal/probe/probe_commands.dm b/code/modules/computer4/data/terminal/probe/probe_commands.dm index 09b2a4f834f7..b2904e96cfcb 100644 --- a/code/modules/computer4/data/terminal/probe/probe_commands.dm +++ b/code/modules/computer4/data/terminal/probe/probe_commands.dm @@ -6,7 +6,7 @@ var/obj/item/peripheral/network_card/wireless/adapter = probe.get_adapter() if(!adapter) - system.println("Error: No network adapter found.") + system.println("[ANSI_WRAP_BOLD("Error:")] No network adapter found.") return if(adapter.ping()) @@ -18,7 +18,7 @@ /datum/shell_command/probe_cmd/view/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/datum/c4_file/terminal_program/probe/probe = program if(!length(probe.ping_replies)) - system.println("Error: No replies found.") + system.println("[ANSI_WRAP_BOLD("Error:")] No replies found.") return var/list/out = list("Reply list:") @@ -29,7 +29,7 @@ out += "\[[reply_netclass]\]-TYPE: [reply_id]" - system.println(jointext(out, "
")) + system.println(jointext(out, "\n")) /datum/shell_command/probe_cmd/quit aliases = list("quit", "q") @@ -45,4 +45,4 @@ if(system.try_background_program(program)) system.println("Moved [program.name] to background processes.") else - system.println("Error: RAM is full.") + system.println("[ANSI_WRAP_BOLD("Error: RAM is full.")]") diff --git a/code/modules/computer4/data/terminal/rtos/_rtos.dm b/code/modules/computer4/data/terminal/rtos/_rtos.dm index 0f16f1984f3e..592edb60c0c1 100644 --- a/code/modules/computer4/data/terminal/rtos/_rtos.dm +++ b/code/modules/computer4/data/terminal/rtos/_rtos.dm @@ -118,7 +118,7 @@ */ /datum/c4_file/terminal_program/operating_system/rtos/proc/redraw_screen(update_ui) var/obj/machinery/computer4/computer = get_computer() - computer?.text_buffer = jointext(print_history,"
") + computer?.text_buffer = jointext(print_history,"\n") if(update_ui) SStgui.update_uis(computer) return TRUE diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm b/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm index da6cb978b4ed..928d4bb6d18b 100644 --- a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm +++ b/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm @@ -43,10 +43,10 @@ change_dir(containing_folder) var/title_text = list( - "[ANSI_FG_RED]", @" ___ _ _ _ ___ ___ ___ ", "\n", - "[ANSI_FG_GREEN]", @"|_ _|| |_ <_>._ _ | |__| . \| . |/ __>", "\n", - "[ANSI_FG_YELLOW]",@" | | | . || || ' || / /| | || | |\__ \", "\n", - "[ANSI_FG_BLUE]", @" |_| |_|_||_||_|_||_\_\|___/`___'<___/", "[ANSI_FG_RESET]" + "[ANSI_FG_RED]", @" ___ _ _ _ ___ ___ ___ ", "\n", + "[ANSI_FG_YELLOW]", @"|_ _|| |_ <_>._ _ | |__| . \| . |/ __>", "\n", + "[ANSI_FG_BLUE]", @" | | | . || || ' || / /| | || | |\__ \", "\n", + "[ANSI_FG_CYAN]", @" |_| |_|_||_||_|_||_\_\|___/`___'<___/", "[ANSI_FG_RESET]" ).Join("") println(title_text) diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm b/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm index abf075a28dc7..97895c05ff7f 100644 --- a/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm +++ b/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm @@ -50,7 +50,7 @@ output.Insert(1, "Use help \[command\] to see specific information about a command.", "List of available commands:") - system.println(jointext(output, "
")) + system.println(jointext(output, "\n")) /// Clear the screen /datum/shell_command/thinkdos/home @@ -63,16 +63,16 @@ /// Print the contents of the current directory. /datum/shell_command/thinkdos/dir aliases = list("dir", "catalog", "ls") - help_text = "Prints the contents of a directory.
Usage: 'dir \[directory?\]'" + help_text = "Prints the contents of a directory.\nUsage: 'dir \[directory?\]'" /datum/shell_command/thinkdos/dir/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/inputted_path = jointext(arguments, " ") var/datum/c4_file/folder/targeted_dir = system.parse_directory(inputted_path, system.current_directory) if(!targeted_dir) - system.print_error("Error: Invalid directory or path.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Invalid directory or path.") return - system.println("Contents of [targeted_dir.path_to_string()]:", FALSE) + system.println("[ANSI_WRAP_BOLD("Contents of [targeted_dir.path_to_string()]:")]", FALSE) var/list/directory_text = list() var/list/cache_spaces = new /list(16) @@ -102,7 +102,7 @@ directory_text += str if(length(directory_text)) - system.println(jointext(directory_text, "
")) + system.println(jointext(directory_text, "\n")) /// Change the current directory to the root of the current folder. /datum/shell_command/thinkdos/root @@ -111,48 +111,48 @@ /datum/shell_command/thinkdos/root/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) system.change_dir(system.current_directory.drive.root) - system.println("Current Directory is now [system.current_directory.path_to_string()]") + system.println("[ANSI_WRAP_BOLD("Current Directory is now [system.current_directory.path_to_string()]")]") return /// Change directory. /datum/shell_command/thinkdos/cd aliases = list("cd", "chdir") - help_text = "Changes the current directory.
Usage: 'cd \[directory\]'

'.' refers to the current directory.
'../' refers to the parent directory." + help_text = "Changes the current directory.\nUsage: 'cd \[directory\]'\n\n'.' refers to the current directory.\n'../' refers to the parent directory." /datum/shell_command/thinkdos/cd/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"cd \[directory string\]\".") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"cd \[directory string\]\".") return var/target_dir = jointext(arguments, " ") var/datum/c4_file/folder/new_dir = system.parse_directory(target_dir, system.current_directory) if(!new_dir) - system.print_error("Error: Invalid directory or path.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Invalid directory or path.") return system.change_dir(new_dir) - system.println("Current Directory is now [system.current_directory.path_to_string()]") + system.println("[ANSI_WRAP_BOLD("Current Directory is now [system.current_directory.path_to_string()]")]") /// Create a folder. /datum/shell_command/thinkdos/makedir aliases = list("makedir", "mkdir") - help_text = "Creates a new folder.
Usage: 'makedir \[directory\]'

See 'cd' for more information." + help_text = "Creates a new folder.\nUsage: 'makedir \[directory\]'\n\nSee 'cd' for more information." /datum/shell_command/thinkdos/makedir/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"makedir \[new directory name\]\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"makedir \[new directory name\]\"") return var/folder_name = trim(jointext(arguments, ""), 16) if(system.resolve_filepath(folder_name)) - system.print_error("Error: File name in use.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] File name in use.") return if(!system.validate_file_name(folder_name)) - system.print_error("Error: Invalid character(s).") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Invalid character(s).") return var/datum/c4_file/folder/new_folder = new @@ -160,7 +160,7 @@ if(!system.current_directory.try_add_file(new_folder)) qdel(new_folder) - system.print_error("Error: Unable to create new directory.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to create new directory.") return system.println("New directory created.") @@ -168,11 +168,11 @@ /// Rename a file /datum/shell_command/thinkdos/rename aliases = list("move","mv", "rename", "ren") - help_text = "Moves or renames a file or folder.
Usage: 'move \[options?\] \[path\] \[destination path\]'

See 'cd' for more information.
-f, --force       Overwrite any existing files in the destination location." + help_text = "Moves or renames a file or folder.\nUsage: 'move \[options?\] \[path\] \[destination path\]'\n\nSee 'cd' for more information.\n-f, --force       Overwrite any existing files in the destination location." /datum/shell_command/thinkdos/rename/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(length(arguments) != 2) - system.println("Syntax: \"rename \[name of target] \[new name]\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"rename \[name of target] \[new name]\"") return var/old_path = arguments[1] @@ -182,7 +182,7 @@ var/datum/c4_file/file = system.resolve_filepath(old_path) if(!file) - system.print_error("Error: Source file not found.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Source file not found.") return var/datum/file_path/destination_info = system.text_to_filepath(new_path) @@ -190,11 +190,11 @@ var/datum/c4_file/folder/destination_folder = system.parse_directory(destination_info.directory, system.current_directory) if(!destination_folder) - system.print_error("Error: Target directory not found.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Target directory not found.") return if(desired_name && !system.validate_file_name(desired_name)) - system.print_error("Error: Invalid character in name.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Invalid character in name.") return var/old_name = file.name @@ -211,17 +211,17 @@ if(err == "Target in use.") err += " Use -f to overwrite." - system.print_error("Error: [err]") + system.print_error("[ANSI_WRAP_BOLD("Error:")] [err]") return var/datum/c4_file/shares_name = destination_folder.get_file(desired_name) if(shares_name) if(!overwrite) - system.print_error("Error: Target in use. Use -f to overwrite.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Target in use. Use -f to overwrite.") return if(!destination_folder.try_delete_file(shares_name)) - system.print_error("Error: Unable to delete target.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to delete target.") return file.set_name(desired_name) @@ -230,11 +230,11 @@ /// Copy a file (opens can of worms and begins eating them). /datum/shell_command/thinkdos/copy aliases = list("copy","cp") - help_text = "Copies a file to another location.
Usage: 'move \[options?\] \[path\] \[destination path\]'

See 'cd' for more information.
-f, --force       Overwrite any existing files in the destination location." + help_text = "Copies a file to another location.\nUsage: 'move \[options?\] \[path\] \[destination path\]'\n\nSee 'cd' for more information.\n-f, --force       Overwrite any existing files in the destination location." /datum/shell_command/thinkdos/copy/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(length(arguments) != 2) - system.println("Syntax: \"copy \[name of target] \[new location]\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"copy \[name of target] \[new location]\"") return var/old_path = arguments[1] @@ -244,11 +244,11 @@ var/datum/c4_file/to_copy = system.resolve_filepath(old_path) if(!to_copy) - system.print_error("Error: Source file not found.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Source file not found.") return if(to_copy.size + system.drive.root.size > system.drive.disk_capacity) - system.print_error("Error: Copy operation would exceed disk storage.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Copy operation would exceed disk storage.") return var/datum/file_path/destination_info = system.text_to_filepath(new_path) @@ -256,11 +256,11 @@ var/datum/c4_file/folder/destination_folder = system.parse_directory(destination_info.directory, system.current_directory) if(!destination_folder) - system.print_error("Error: Target directory not found.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Target directory not found.") return if(desired_name && !system.validate_file_name(desired_name)) - system.print_error("Error: Invalid character in name.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Invalid character in name.") return // Preserve the existing file name if we didn't specify a new name. @@ -269,27 +269,27 @@ var/datum/c4_file/shares_name = destination_folder.get_file(desired_name) if(shares_name) if(shares_name == to_copy) - system.print_error("Error: Cannot copy in-place.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Cannot copy in-place.") return if(!overwrite) - system.print_error("Error: Target in use. Use -f to overwrite.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Target in use. Use -f to overwrite.") return if(!destination_folder.try_delete_file(shares_name)) - system.print_error("Error: Unable to delete target.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to delete target.") return var/datum/c4_file/copy = to_copy.copy() copy?.set_name(desired_name) if(isnull(copy)) - system.print_error("Error: Unable to copy file.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to copy file.") return if(!destination_folder.try_add_file(copy)) qdel(copy) - system.print_error("Error: Unable to copy file.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to copy file.") return system.println("Copied [to_copy.name] to [copy.path_to_string()].") @@ -303,15 +303,15 @@ var/list/help_list = list( "Deletes the specified file from the drive.", "Usage: 'delete \[options?\] \[path\]'", - "
See 'cd' for more information.", + "\nSee 'cd' for more information.", ) help_list += "[fit_with("-f, --force", 20, " ", TRUE)]Overwrite any existing files in the destination location." help_list += "[fit_with("-r, -R, --recursive", 20, " ", TRUE)]Allow deletion of folders." - help_text = jointext(help_list, "
") + help_text = jointext(help_list, "\n") /datum/shell_command/thinkdos/delete/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"del \[-f\] \[file name].\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"del \[-f\] \[file name].\"") return var/force = !!length(options & list("f", "force")) @@ -320,21 +320,21 @@ var/datum/c4_file/file = system.resolve_filepath(jointext(arguments, "")) if(!file) - system.print_error("Error: File not found.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] File not found.") return if(istype(file, /datum/c4_file/folder)) if(!recursive) - system.print_error("Error: Use -r option to delete folders.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Use -r option to delete folders.") return var/datum/c4_file/folder/to_delete = file if(length(to_delete.contents) && !force) - system.print_error("Error: Folder is not empty. Use -f to delete anyway.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Folder is not empty. Use -f to delete anyway.") return if(file == system && !force) - system.print_error("Error: Access denied.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Access denied.") return if(!file.containing_folder) // is root @@ -349,7 +349,7 @@ if(file.containing_folder.try_delete_file(file)) system.println("File deleted.") else - system.print_error("Error: Unable to delete file.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to delete file.") /datum/shell_command/thinkdos/initlogs aliases = list("initlogs") @@ -357,11 +357,11 @@ /datum/shell_command/thinkdos/initlogs/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(system.command_log) - system.print_error("Error: File already exists.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] File already exists.") return if(!system.initialize_logs()) - system.print_error("Error: File already exists.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] File already exists.") return system.println("Logging re-initialized.") @@ -372,7 +372,7 @@ /datum/shell_command/thinkdos/print/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"print \[text to be printed]\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"print \[text to be printed]\"") return var/text = html_encode(jointext(arguments, " ")) @@ -380,16 +380,16 @@ /datum/shell_command/thinkdos/read aliases = list("read", "type") - help_text = "Displays the contents of a file.
Usage: 'read \[directory\]'" + help_text = "Displays the contents of a file.\nUsage: 'read \[directory\]'" /datum/shell_command/thinkdos/read/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"read \[file name].\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"read \[file name].\"") return var/datum/c4_file/file = system.resolve_filepath(jointext(arguments, "")) if(!file) - system.println("Error: No file found.") + system.println("[ANSI_WRAP_BOLD("Error")]: No file found.") return system.println(html_encode(file.to_string())) @@ -399,7 +399,7 @@ help_text = "Displays the version of the operating system." /datum/shell_command/thinkdos/version/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) - system.println("[system.system_version]
Copyright Thinktronic Systems, LTD.") + system.println("[system.system_version]\nCopyright Thinktronic Systems, LTD.") /datum/shell_command/thinkdos/time aliases = list("time") @@ -410,16 +410,16 @@ /datum/shell_command/thinkdos/sizeof aliases = list("sizeof", "du") - help_text = "Displays the size of a file on disk.
Usage: 'sizeof \[directory\]'" + help_text = "Displays the size of a file on disk.\nUsage: 'sizeof \[directory\]'" /datum/shell_command/thinkdos/sizeof/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"sizeof \[file path].\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"sizeof \[file path].\"") return var/datum/c4_file/file = system.resolve_filepath(jointext(arguments, "")) if(!file) - system.print_error("Error: File does not exist.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] File does not exist.") return system.println(file.size) @@ -427,47 +427,47 @@ /// Renames the drive title /datum/shell_command/thinkdos/title aliases = list("title") - help_text = "Changes the name of the current .
Usage: 'title \[new name\]'" + help_text = "Changes the name of the current .\nUsage: 'title \[new name\]'" /datum/shell_command/thinkdos/title/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"title \[title name]\" Set name of active drive to given title.") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"title \[title name]\" Set name of active drive to given title.") return if(system.drive.read_only) - system.print_error("Error: Unable to set title string.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to set title string.") return var/new_title = sanitize(trim(jointext(arguments, ""), 8)) system.drive.title = new_title - system.println("Drive title set to [new_title].") + system.println("Drive title set to [ANSI_WRAP_BOLD("[new_title]")].") /datum/shell_command/thinkdos/run_prog aliases = list("run") - help_text = "Runs an executable file.
Usage: 'run \[file\]'" + help_text = "Runs an executable file.\nUsage: 'run \[file\]'" /datum/shell_command/thinkdos/run_prog/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: \"run \[program filepath].\"") + system.println("[ANSI_WRAP_BOLD("Syntax:")] \"run \[program filepath].\"") return var/file_path = jointext(arguments, "") var/datum/c4_file/terminal_program/program_to_run = system.resolve_filepath(file_path, system.current_directory) if(!istype(program_to_run) || istype(program_to_run, /datum/c4_file/terminal_program/operating_system)) - system.print_error("Error: Cannot find executable.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Cannot find executable.") return system.execute_program(program_to_run) /datum/shell_command/thinkdos/tree aliases = list("tree") - help_text = "Displays the file system structure relative to a directory.
Usage: 'tree \[options?\] \[directory?\]'

-f, --file       Display files." + help_text = "Displays the file system structure relative to a directory.\nUsage: 'tree \[options?\] \[directory?\]'\n\n-f, --file       Display files." /datum/shell_command/thinkdos/tree/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/datum/c4_file/folder/targeted_dir = system.parse_directory(jointext(arguments, " "), system.current_directory) if(!targeted_dir) - system.print_error("Error: Invalid directory or path.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Invalid directory or path.") return var/show_files = !!length(options & list("f", "files")) @@ -476,7 +476,7 @@ search_dir(targeted_dir, output, show_files, 1) - system.println(jointext(output, "
")) + system.println(jointext(output, "\n")) /datum/shell_command/thinkdos/tree/proc/search_dir(datum/c4_file/folder/folder, list/output, show_files, depth) var/spaces = jointext(new /list((depth * 2) + 1), " ") @@ -507,11 +507,11 @@ help_list += "[fit_with("k, kill", 20, " ", TRUE)]Terminate a background process." help_list += "[fit_with("s, switch", 20, " ", TRUE)]Focus a background process." help_list += "[fit_with("v, view", 20, " ", TRUE)]Display background processes." - help_text = jointext(help_list, "
") + help_text = jointext(help_list, "\n") /datum/shell_command/thinkdos/backprog/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(!length(arguments)) - system.println("Syntax: backprog \[argument\]
Valid arguments: view, kill, switch") + system.println("[ANSI_WRAP_BOLD("Syntax:")] backprog \[argument\]\n[ANSI_WRAP_BOLD("Valid arguments:")] view, kill, switch") return var/sub_name = arguments[1] @@ -522,20 +522,20 @@ if(sub_command.try_exec(sub_name, system, program, inner_arguments, null)) return - system.println("Syntax: backprog \[argument\]
Valid arguments: view, kill, switch") + system.println("[ANSI_WRAP_BOLD("Syntax:")] backprog \[argument\]\n[ANSI_WRAP_BOLD("Valid arguments:")] view, kill, switch") /datum/shell_command/thinkdos_backprog/view aliases = list("view", "v") /datum/shell_command/thinkdos_backprog/view/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) - var/list/out = list("Current programs in memory:") + var/list/out = list("[ANSI_WRAP_BOLD("Current programs in memory:")]") var/count = 0 for(var/datum/c4_file/terminal_program/running_program as anything in system.processing_programs) count++ - out += "ID: [count] [running_program == system ? "SYSTEM" : running_program.name]" + out += "[ANSI_WRAP_BOLD("ID: [count]")] [running_program == system ? "SYSTEM" : running_program.name]" - system.println(jointext(out, "
")) + system.println(jointext(out, "\n")) /datum/shell_command/thinkdos_backprog/kill aliases = list("kill", "k") @@ -543,16 +543,16 @@ /datum/shell_command/thinkdos_backprog/kill/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/id = text2num(jointext(arguments, "")) if(isnull(id)) - system.println("Syntax: backprog kill \[program id\]") + system.println("[ANSI_WRAP_BOLD("Syntax:")] backprog kill \[program id\]") return if(!(id in 1 to length(system.processing_programs))) - system.print_error("Error: Array index out of bounds.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Array index out of bounds.") return var/datum/c4_file/terminal_program/to_kill = system.processing_programs[id] if(to_kill == system) - system.print_error("Error: Unable to terminate process.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Unable to terminate process.") return system.unload_program(to_kill) @@ -564,16 +564,16 @@ /datum/shell_command/thinkdos_backprog/switch_prog/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/id = text2num(jointext(arguments, "")) if(isnull(id)) - system.println("Syntax: backprog switch \[program id\]") + system.println("[ANSI_WRAP_BOLD("Syntax:")] backprog switch \[program id\]") return if(!(id in 1 to length(system.processing_programs))) - system.print_error("Error: Array index out of bounds.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Array index out of bounds.") return var/datum/c4_file/terminal_program/to_run = system.processing_programs[id] if(to_run == system) - system.print_error("Error: Process already focused.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] Process already focused.") return system.execute_program(to_run) @@ -588,7 +588,7 @@ var/obj/item/peripheral/card_reader/reader = system.get_computer().get_peripheral(PERIPHERAL_TYPE_CARD_READER) if(!reader) - system.println("Error: No card reader detected.") + system.println("[ANSI_WRAP_BOLD("Error:")] No card reader detected.") return var/datum/signal/login_packet = reader.scan_card() @@ -596,11 +596,11 @@ system.login(login_packet.data["name"], login_packet.data["job"], login_packet.data["access"]) else if(login_packet == "nocard") - system.print_error("Error: No ID card inserted.") + system.print_error("[ANSI_WRAP_BOLD("Error:")] No ID card inserted.") /datum/shell_command/thinkdos/logout aliases = list("logout", "logoff") /datum/shell_command/thinkdos/logout/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) system.logout() - system.println("Logout complete. Have a secure day.

Authentication required.
Please insert card and 'login'.") + system.println("Logout complete. Have a secure day.\n\nAuthentication required.\nPlease insert card and 'login'.") From f50554e52f2fef03abb48866f93d5ca832da907c Mon Sep 17 00:00:00 2001 From: Francinum <5572280+francinum@users.noreply.github.com> Date: Thu, 15 Jan 2026 03:22:31 -0500 Subject: [PATCH 3/3] Various program patches New font --- code/modules/asset_cache/assets/webfonts.dm | 9 + .../data/terminal/medtrak/medtrak.dm | 14 +- .../data/terminal/notepad/notepad.dm | 7 +- .../data/terminal/test_progs/dbg_ansi.dm | 16 +- .../data/terminal/thinkdos/thinkdos.dm | 4 +- .../terminal/thinkdos/thinkdos_commands.dm | 20 +- code/modules/tgui/tgui.dm | 1 + code/modules/tgui_panel/tgui_panel.dm | 1 + fonts/oldschool_pc_fonts/LICENSE.TXT | 432 ++++++++++++++++++ .../WebPlus_IBM_VGA_9x16.css | 7 + .../WebPlus_IBM_VGA_9x16.woff | Bin 0 -> 22712 bytes .../Terminal/TerminalOutputSection.tsx | 5 +- .../tgui/styles/themes/retro-dark.scss | 13 + 13 files changed, 496 insertions(+), 33 deletions(-) create mode 100644 fonts/oldschool_pc_fonts/LICENSE.TXT create mode 100644 fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.css create mode 100644 fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.woff diff --git a/code/modules/asset_cache/assets/webfonts.dm b/code/modules/asset_cache/assets/webfonts.dm index 26b2557f473c..1e80f1e4aa71 100644 --- a/code/modules/asset_cache/assets/webfonts.dm +++ b/code/modules/asset_cache/assets/webfonts.dm @@ -36,3 +36,12 @@ parents = list( "Yrsa.css" = file("fonts/Yrsa.css"), ) + +/datum/asset/simple/namespaced/ibm_vga9x16 + assets = list( + "WebPlus_IBM_VGA_9x16.woff" = file("fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.woff"), + ) + + parents = list( + "WebPlus_IBM_VGA_9x16.css" = file("fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.css"), + ) diff --git a/code/modules/computer4/data/terminal/medtrak/medtrak.dm b/code/modules/computer4/data/terminal/medtrak/medtrak.dm index 5f99f1a46339..92e21adb9129 100644 --- a/code/modules/computer4/data/terminal/medtrak/medtrak.dm +++ b/code/modules/computer4/data/terminal/medtrak/medtrak.dm @@ -158,15 +158,15 @@ /datum/c4_file/terminal_program/medtrak/proc/home_text() var/title_text = list( "[ANSI_FG_CYAN]", - @(eol)" __ __ _ _____ _ "eol, - @(eol)"| \/ | ___ __| | |_ _| _ _ __ _ | |__ "eol, - @(eol)"| |\/| | / -_) / _` | | | | '_| / _` | | / / "eol, - @(eol)"|_|__|_| \___| \__,_| _|_|_ _|_|_ \__,_| |_\_\ "eol, + @(eol) __ __ _ _____ _ eol,"\n", + @(eol)| \/ | ___ __| | |_ _| _ _ __ _ | |__ eol,"\n", + @(eol)| |\/| | / -_) / _` | | | | '_| / _` | | / / eol,"\n", + @(eol)|_|__|_| \___| \__,_| _|_|_ _|_|_ \__,_| |_\_\ eol,"\n", "[ANSI_FG_WHITE]", - @(eol)"_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|"eol, - @(eol)" `-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"eol, + @(eol)_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|eol,"\n", + @(eol) `-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'eol,"\n", "[ANSI_FG_RESET]", - ).Join("\n") + ).Join("") current_menu = MEDTRAK_MENU_HOME var/list/out = list( diff --git a/code/modules/computer4/data/terminal/notepad/notepad.dm b/code/modules/computer4/data/terminal/notepad/notepad.dm index 6d37ca528dd9..bf664c13540c 100644 --- a/code/modules/computer4/data/terminal/notepad/notepad.dm +++ b/code/modules/computer4/data/terminal/notepad/notepad.dm @@ -35,18 +35,15 @@ system.println(title_text) system.println("Welcome to DocDock, type !help to get started.") -/datum/c4_file/terminal_program/notepad/parse_cmdline(text) - return splittext(text, " ") - /datum/c4_file/terminal_program/notepad/std_in(text) . = ..() - var/list/arguments = parse_cmdline(text) + var/list/arguments = splittext(text, " ") var/command = arguments[1] arguments.Cut(1,2) var/datum/c4_file/terminal_program/operating_system/os = get_os() - if(command[1] == "!") + if(text[1] == "!") command = copytext(command, 2) for(var/datum/shell_command/potential_command as anything in edit_commands) if(potential_command.try_exec(command, os, src, arguments, null)) diff --git a/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm b/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm index 10eccb6b714b..f5873a19647f 100644 --- a/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm +++ b/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm @@ -9,18 +9,20 @@ var/print_all = !!cmdline.options.Find("a") - system.println("#----------#-----------#-----------#") + system.println("╒══════════╤═══════════╤═══════════╕") for(var/param_id in 0 to 107) switch(param_id) //Unsupported/undefined - if(5-7,20,25-27,50,52,56-57,60-72,76-89,98-99,) + if(5-7,20,25-27,50,52,56-57,60-72,76-89,98-99) if(print_all) - system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | NOT SUPPORTED |") - continue + system.println("│ SGR [fit_with_zeros("[param_id]", 3)]: │ NOT SUPPORTED │") if(38,48,58) - system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | Uses Extended Color |") + system.println("│ SGR [fit_with_zeros("[param_id]", 3)]: │ Uses Extended Color │") else - system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | Test Text | [ANSI_SGR(param_id)]Test Text[ANSI_FULL_RESET] |") - system.println("#----------#-----------#-----------#") + system.println("│ SGR [fit_with_zeros("[param_id]", 3)]: │ Test Text │ [ANSI_SGR(param_id)]Test Text[ANSI_FULL_RESET] │") + switch(param_id) //Print a nice visual divider in a few places. + if(29,39,49,89,99) + system.println("╞══════════╪═══════════╪═══════════╡") + system.println("╘══════════╧═══════════╧═══════════╛") system.unload_program(src) return diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm b/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm index 928d4bb6d18b..666da6090100 100644 --- a/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm +++ b/code/modules/computer4/data/terminal/thinkdos/thinkdos.dm @@ -135,7 +135,7 @@ if(!command_log || drive.read_only) return FALSE - command_log.data += text + command_log.data += "[text]\n" return TRUE /// Write to the command log if it's enabled, then print to the screen. @@ -255,7 +255,7 @@ command_log = log_file RegisterSignal(command_log, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_PARENT_QDELETING), PROC_REF(log_file_gone)) - log_file.data += "\n[ANSI_WRAP_BOLD("STARTUP")]: [stationtime2text()], [stationdate2text()]" + log_file.data += "[ANSI_WRAP_BOLD("STARTUP")]: [stationtime2text()], [stationdate2text()]\n" return TRUE diff --git a/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm b/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm index 97895c05ff7f..1982d538fa5b 100644 --- a/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm +++ b/code/modules/computer4/data/terminal/thinkdos/thinkdos_commands.dm @@ -95,7 +95,7 @@ var/name_length = length(file.name) if(name_length < longest_name_length) if(isnull(cache_spaces[name_length])) - cache_spaces[name_length] = jointext(new /list(longest_name_length - name_length + 1), " ") + cache_spaces[name_length] = jointext(new /list(longest_name_length - name_length + 1), " ") str = "[cache_spaces[name_length]][str]" @@ -168,7 +168,7 @@ /// Rename a file /datum/shell_command/thinkdos/rename aliases = list("move","mv", "rename", "ren") - help_text = "Moves or renames a file or folder.\nUsage: 'move \[options?\] \[path\] \[destination path\]'\n\nSee 'cd' for more information.\n-f, --force       Overwrite any existing files in the destination location." + help_text = "Moves or renames a file or folder.\nUsage: 'move \[options?\] \[path\] \[destination path\]'\n\nSee 'cd' for more information.\n-f, --force Overwrite any existing files in the destination location." /datum/shell_command/thinkdos/rename/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(length(arguments) != 2) @@ -230,7 +230,7 @@ /// Copy a file (opens can of worms and begins eating them). /datum/shell_command/thinkdos/copy aliases = list("copy","cp") - help_text = "Copies a file to another location.\nUsage: 'move \[options?\] \[path\] \[destination path\]'\n\nSee 'cd' for more information.\n-f, --force       Overwrite any existing files in the destination location." + help_text = "Copies a file to another location.\nUsage: 'move \[options?\] \[path\] \[destination path\]'\n\nSee 'cd' for more information.\n-f, --force Overwrite any existing files in the destination location." /datum/shell_command/thinkdos/copy/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) if(length(arguments) != 2) @@ -305,8 +305,8 @@ "Usage: 'delete \[options?\] \[path\]'", "\nSee 'cd' for more information.", ) - help_list += "[fit_with("-f, --force", 20, " ", TRUE)]Overwrite any existing files in the destination location." - help_list += "[fit_with("-r, -R, --recursive", 20, " ", TRUE)]Allow deletion of folders." + help_list += "[fit_with("-f, --force", 20, " ", TRUE)]Overwrite any existing files in the destination location." + help_list += "[fit_with("-r, -R, --recursive", 20, " ", TRUE)]Allow deletion of folders." help_text = jointext(help_list, "\n") /datum/shell_command/thinkdos/delete/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) @@ -462,7 +462,7 @@ /datum/shell_command/thinkdos/tree aliases = list("tree") - help_text = "Displays the file system structure relative to a directory.\nUsage: 'tree \[options?\] \[directory?\]'\n\n-f, --file       Display files." + help_text = "Displays the file system structure relative to a directory.\nUsage: 'tree \[options?\] \[directory?\]'\n\n-f, --file Display files." /datum/shell_command/thinkdos/tree/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) var/datum/c4_file/folder/targeted_dir = system.parse_directory(jointext(arguments, " "), system.current_directory) @@ -479,7 +479,7 @@ system.println(jointext(output, "\n")) /datum/shell_command/thinkdos/tree/proc/search_dir(datum/c4_file/folder/folder, list/output, show_files, depth) - var/spaces = jointext(new /list((depth * 2) + 1), " ") + var/spaces = jointext(new /list((depth * 2) + 1), " ") for(var/datum/c4_file/file as anything in folder.contents) var/is_folder = istype(file, /datum/c4_file/folder) @@ -504,9 +504,9 @@ "Manage background processes.", "Usage: 'backprog \[argument 1\] \[argument 2?\]'", ) - help_list += "[fit_with("k, kill", 20, " ", TRUE)]Terminate a background process." - help_list += "[fit_with("s, switch", 20, " ", TRUE)]Focus a background process." - help_list += "[fit_with("v, view", 20, " ", TRUE)]Display background processes." + help_list += "[fit_with("k, kill", 20, " ", TRUE)]Terminate a background process." + help_list += "[fit_with("s, switch", 20, " ", TRUE)]Focus a background process." + help_list += "[fit_with("v, view", 20, " ", TRUE)]Display background processes." help_text = jointext(help_list, "\n") /datum/shell_command/thinkdos/backprog/exec(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/c4_file/terminal_program/program, list/arguments, list/options) diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 06d466c5398b..e79331cb6e1f 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -107,6 +107,7 @@ flush_queue |= window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/libre_baskerville)) flush_queue |= window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/jost)) flush_queue |= window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/yrsa)) + flush_queue |= window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/ibm_vga9x16)) flush_queue |= window.send_asset(get_asset_datum(/datum/asset/json/icon_ref_map)) for(var/datum/asset/asset in src_object.ui_assets(user)) diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm index a6ffb822c10c..7ee73d293e4f 100644 --- a/code/modules/tgui_panel/tgui_panel.dm +++ b/code/modules/tgui_panel/tgui_panel.dm @@ -53,6 +53,7 @@ window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/libre_baskerville)) window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/jost)) window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/yrsa)) + window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/ibm_vga9x16)) window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat)) window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/chat_icons)) diff --git a/fonts/oldschool_pc_fonts/LICENSE.TXT b/fonts/oldschool_pc_fonts/LICENSE.TXT new file mode 100644 index 000000000000..3bb213246ed7 --- /dev/null +++ b/fonts/oldschool_pc_fonts/LICENSE.TXT @@ -0,0 +1,432 @@ +Retrieved from https://int10h.org + +© 2015-2020 VileR + +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.css b/fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.css new file mode 100644 index 000000000000..7bffcdacee4b --- /dev/null +++ b/fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.css @@ -0,0 +1,7 @@ +@font-face { + font-family: "IBM VGA 9x16"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("WebPlus_IBM_VGA_9x16.woff") format("woff"); +} diff --git a/fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.woff b/fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.woff new file mode 100644 index 0000000000000000000000000000000000000000..969fb6566f456c7b420fc86b1c043bbe0b2ee75a GIT binary patch literal 22712 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 { container_id="terminalOutput" > diff --git a/tgui/packages/tgui/styles/themes/retro-dark.scss b/tgui/packages/tgui/styles/themes/retro-dark.scss index 446286d6bced..8c07071b740e 100644 --- a/tgui/packages/tgui/styles/themes/retro-dark.scss +++ b/tgui/packages/tgui/styles/themes/retro-dark.scss @@ -66,4 +66,17 @@ $lightorange: #fda751; '../layouts/TitleBar.scss', $with: ('background-color': color.scale($darkorange, $whiteness: +10%)) ); + + // Fancy-ANSI Control Variables + + // Alt Fonts + --ansi-font-1: 'Consolas'; + // --ansi-font-2 + // --ansi-font-3 + // --ansi-font-4 + // --ansi-font-5 + // --ansi-font-6 + // --ansi-font-7 + // --ansi-font-8 + // --ansi-font-9 }