diff --git a/code/__DEFINES/computer4_defines.dm b/code/__DEFINES/computer4_defines.dm index c18764ef04fa..4c0d6a398dd3 100644 --- a/code/__DEFINES/computer4_defines.dm +++ b/code/__DEFINES/computer4_defines.dm @@ -31,6 +31,38 @@ // 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_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) + +// 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/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/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/_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 b6a822d8709e..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) @@ -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/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..92e21adb9129 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,12 +157,15 @@ /// 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, + "[ANSI_FG_CYAN]", + @(eol) __ __ _ _____ _ eol,"\n", + @(eol)| \/ | ___ __| | |_ _| _ _ __ _ | |__ eol,"\n", + @(eol)| |\/| | / -_) / _` | | | | '_| / _` | | / / eol,"\n", + @(eol)|_|__|_| \___| \__,_| _|_|_ _|_|_ \__,_| |_\_\ eol,"\n", + "[ANSI_FG_WHITE]", + @(eol)_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|eol,"\n", + @(eol) `-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'eol,"\n", + "[ANSI_FG_RESET]", ).Join("") current_menu = MEDTRAK_MENU_HOME @@ -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..bf664c13540c 100644 --- a/code/modules/computer4/data/terminal/notepad/notepad.dm +++ b/code/modules/computer4/data/terminal/notepad/notepad.dm @@ -24,29 +24,26 @@ 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.") -/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/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/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/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/test_progs/dbg_ansi.dm b/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm new file mode 100644 index 000000000000..f5873a19647f --- /dev/null +++ b/code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm @@ -0,0 +1,28 @@ +/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 │") + 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] │") + 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 1fa7e3ef8682..666da6090100 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_YELLOW]", @"|_ _|| |_ <_>._ _ | |__| . \| . |/ __>", "\n", + "[ANSI_FG_BLUE]", @" | | | . || || ' || / /| | || | |\__ \", "\n", + "[ANSI_FG_CYAN]", @" |_| |_|_||_||_|_||_\_\|___/`___'<___/", "[ANSI_FG_RESET]" ).Join("") println(title_text) @@ -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. @@ -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 += "[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 abf075a28dc7..1982d538fa5b 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) @@ -95,14 +95,14 @@ 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]" 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_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) 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,10 +476,10 @@ 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), " ") + 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,14 +504,14 @@ "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_text = jointext(help_list, "
") + 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) 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'.") 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/daedalus.dme b/daedalus.dme index 758d238ffa60..8d764fe9b8f0 100644 --- a/daedalus.dme +++ b/daedalus.dme @@ -3033,6 +3033,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/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 000000000000..969fb6566f45 Binary files /dev/null and b/fonts/oldschool_pc_fonts/WebPlus_IBM_VGA_9x16.woff differ 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..1b726a77fc05 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 (
{ container_id="terminalOutput" >
); 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/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 } 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"