From d5b98c4d8d57dd6e32f7e6191d84c1810168c5ec Mon Sep 17 00:00:00 2001 From: MrPerson Date: Tue, 7 Jan 2020 02:25:47 -0600 Subject: [PATCH] Unicode support Part 1 (#48304) * Unicode support Part 1 Makes all calls to ascii2text() and text2ascii() unicode aware as well as all calls in code/__HELPERS/text.dm. Adds defines for the footext_char() procs to maintain 512 support. I did some cleanup on the text helpers since a lot of them were really, really bad. reject_bad_text() and reject_bad_name() have an arg to reject non-ascii chars which defaults to TRUE. * Give travis a more recent beta * Ultimate compat Added the _char procs I was missing Test the build instead of the version because Lummox is a goddamn moron * Suggested fixes, removes the last of the dd_ helpers * Reading the reference is very important! * Minor code improvements and a comment * Typo --- Dockerfile | 2 +- code/__HELPERS/sanitize_values.dm | 24 +- code/__HELPERS/text.dm | 451 +++++++++--------- code/__HELPERS/type2type.dm | 23 - code/__HELPERS/unsorted.dm | 10 +- code/_compile_options.dm | 20 + code/datums/brain_damage/mild.dm | 22 +- code/datums/wires/_wires.dm | 2 +- .../game/machinery/computer/communications.dm | 2 +- code/game/machinery/doors/brigdoors.dm | 2 +- code/game/objects/items/devices/PDA/cart.dm | 10 +- code/modules/admin/check_antagonists.dm | 4 +- code/modules/mob/living/carbon/alien/alien.dm | 3 +- .../carbon/alien/special/alien_embryo.dm | 3 +- .../chemistry/reagents/other_reagents.dm | 12 +- code/modules/shuttle/shuttle.dm | 2 +- code/modules/surgery/organs/vocal_cords.dm | 6 +- 17 files changed, 294 insertions(+), 304 deletions(-) diff --git a/Dockerfile b/Dockerfile index 952e27eb562..50d5316df56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM tgstation/byond:513.1490 as base +FROM tgstation/byond:513.1503 as base FROM base as build_base diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index 467deee6e37..5588b0309f1 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -49,24 +49,24 @@ if(!istext(color)) color = "" - var/start = 1 + (text2ascii(color,1)==35) + var/start = 1 + (text2ascii(color, 1) == 35) var/len = length(color) - var/step_size = 1 + ((len+1)-start != desired_format) + var/char = "" . = "" - for(var/i=start, i<=len, i+=step_size) - var/ascii = text2ascii(color,i) - switch(ascii) - if(48 to 57) - . += ascii2text(ascii) //numbers 0 to 9 - if(97 to 102) - . += ascii2text(ascii) //letters a to f - if(65 to 70) - . += ascii2text(ascii+32) //letters A to F - translates to lowercase + for(var/i = start, i <= len, i += length(char)) + char = color[i] + switch(text2ascii(char)) + if(48 to 57) //numbers 0 to 9 + . += char + if(97 to 102) //letters a to f + . += char + if(65 to 70) //letters A to F + . += lowertext(char) else break - if(length(.) != desired_format) + if(length_char(.) != desired_format) if(default) return default return crunch + repeat_string(desired_format, "0") diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 79a722c7b57..62d4c65737e 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -40,8 +40,8 @@ for(var/char in repl_chars) var/index = findtext(t, char) while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1) - index = findtext(t, char, index+1) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index + length(char)) + index = findtext(t, char, index + length(char)) return t /proc/sanitize_filename(t) @@ -73,22 +73,28 @@ //Returns null if there is any bad text in the string -/proc/reject_bad_text(text, max_length=512) - if(length(text) > max_length) - return //message too long - var/non_whitespace = 0 - for(var/i=1, i<=length(text), i++) - switch(text2ascii(text,i)) - if(62,60,92,47) - return //rejects the text if it contains these bad characters: <, >, \ or / - if(127 to 255) - return //rejects weird letters like � +/proc/reject_bad_text(text, max_length = 512, ascii_only = TRUE) + var/char_count = 0 + var/non_whitespace = FALSE + var/lenbytes = length(text) + var/char = "" + for(var/i = 1, i <= lenbytes, i += length(char)) + char = text[i] + char_count++ + if(char_count > max_length) + return + switch(text2ascii(char)) + if(62, 60, 92, 47) // <, >, \, / + return if(0 to 31) - return //more weird stuff + return if(32) - continue //whitespace + continue + if(127 to INFINITY) + if(ascii_only) + return else - non_whitespace = 1 + non_whitespace = TRUE if(non_whitespace) return text //only accepts the text if it has some non-spaces @@ -109,73 +115,84 @@ else return trim(html_encode(name), max_length) +#define NO_CHARS_DETECTED 0 +#define SPACES_DETECTED 1 +#define SYMBOLS_DETECTED 2 +#define NUMBERS_DETECTED 3 +#define LETTERS_DETECTED 4 + //Filters out undesirable characters from names -/proc/reject_bad_name(t_in, allow_numbers=0, max_length=MAX_NAME_LEN) - if(!t_in || length(t_in) > max_length) - return //Rejects the input if it is null or if it is longer then the max length allowed +/proc/reject_bad_name(t_in, allow_numbers = FALSE, max_length = MAX_NAME_LEN, ascii_only = TRUE) + if(!t_in) + return //Rejects the input if it is null - var/number_of_alphanumeric = 0 - var/last_char_group = 0 + var/number_of_alphanumeric = 0 + var/last_char_group = NO_CHARS_DETECTED var/t_out = "" + var/t_len = length(t_in) + var/charcount = 0 + var/char = "" - for(var/i=1, i<=length(t_in), i++) - var/ascii_char = text2ascii(t_in,i) - switch(ascii_char) + + for(var/i = 1, i <= t_len, i += length(char)) + char = t_in[i] + + switch(text2ascii(char)) // A .. Z if(65 to 90) //Uppercase Letters - t_out += ascii2text(ascii_char) number_of_alphanumeric++ - last_char_group = 4 + last_char_group = LETTERS_DETECTED // a .. z if(97 to 122) //Lowercase Letters - if(last_char_group<2) - t_out += ascii2text(ascii_char-32) //Force uppercase first character - else - t_out += ascii2text(ascii_char) + if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED || last_char_group == SYMBOLS_DETECTED) //start of a word + char = uppertext(char) number_of_alphanumeric++ - last_char_group = 4 + last_char_group = LETTERS_DETECTED // 0 .. 9 if(48 to 57) //Numbers - if(!last_char_group) - continue //suppress at start of string - if(!allow_numbers) + if(last_char_group == NO_CHARS_DETECTED || !allow_numbers) //suppress at start of string continue - t_out += ascii2text(ascii_char) number_of_alphanumeric++ - last_char_group = 3 + last_char_group = NUMBERS_DETECTED // ' - . if(39,45,46) //Common name punctuation - if(!last_char_group) + if(last_char_group == NO_CHARS_DETECTED) continue - t_out += ascii2text(ascii_char) - last_char_group = 2 + last_char_group = SYMBOLS_DETECTED // ~ | @ : # $ % & * + if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI) - if(!last_char_group) - continue //suppress at start of string - if(!allow_numbers) + if(last_char_group == NO_CHARS_DETECTED || !allow_numbers) //suppress at start of string continue - t_out += ascii2text(ascii_char) - last_char_group = 2 + last_char_group = SYMBOLS_DETECTED //Space if(32) - if(last_char_group <= 1) - continue //suppress double-spaces and spaces at start of string - t_out += ascii2text(ascii_char) - last_char_group = 1 + if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED) //suppress double-spaces and spaces at start of string + continue + last_char_group = SPACES_DETECTED + + if(127 to INFINITY) + if(ascii_only) + continue + last_char_group = SYMBOLS_DETECTED //for now, we'll treat all non-ascii characters like symbols even though most are letters + else - return + continue + + t_out += char + charcount++ + if(charcount >= max_length) + break if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" - if(last_char_group == 1) - t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) + if(last_char_group == SPACES_DETECTED) + t_out = copytext_char(t_out, 1, -1) //removes the last character (in this case a space) for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai")) //prevents these common metagamey names if(cmptext(t_out,bad_name)) @@ -183,6 +200,13 @@ return t_out +#undef NO_CHARS_DETECTED +#undef SPACES_DETECTED +#undef NUMBERS_DETECTED +#undef LETTERS_DETECTED + + + //html_encode helper proc that returns the smallest non null of two numbers //or 0 if they're both null (needed because of findtext returning 0 when a value is not present) /proc/non_zero_min(a, b) @@ -192,39 +216,6 @@ return a return (a < b ? a : b) -/* - * Text searches - */ - -//Checks the beginning of a string for a specified sub-string -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtext(text, prefix, start, end) - -//Checks the beginning of a string for a specified sub-string. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix_case(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtextEx(text, prefix, start, end) - -//Checks the end of a string for a specified substring. -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtext(text, suffix, start, null) - return - -//Checks the end of a string for a specified substring. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix_case(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtextEx(text, suffix, start, null) - //Checks if any of a given list of needles is in the haystack /proc/text_in_list(haystack, list/needle_list, start=1, end=0) for(var/needle in needle_list) @@ -239,23 +230,17 @@ return 1 return 0 -//Adds 'u' number of zeros ahead of the text 't' -/proc/add_zero(t, u) - while (length(t) < u) - t = "0[t]" - return t +//Adds 'char' ahead of 'text' until there are 'count' characters total +/proc/add_leading(text, count, char = " ") + var/charcount = count - length_char(text) + var/list/chars_to_add[max(charcount + 1, 0)] + return jointext(chars_to_add, char) + text -//Adds 'u' number of spaces ahead of the text 't' -/proc/add_lspace(t, u) - while(length(t) < u) - t = " [t]" - return t - -//Adds 'u' number of spaces behind the text 't' -/proc/add_tspace(t, u) - while(length(t) < u) - t = "[t] " - return t +//Adds 'char' behind 'text' until there are 'count' characters total +/proc/add_trailing(text, count, char = " ") + var/charcount = count - length_char(text) + var/list/chars_to_add[max(charcount + 1, 0)] + return text + jointext(chars_to_add, char) //Returns a string with reserved characters and spaces before the first letter removed /proc/trim_left(text) @@ -269,63 +254,45 @@ for (var/i = length(text), i > 0, i--) if (text2ascii(text, i) > 32) return copytext(text, 1, i + 1) - return "" //Returns a string with reserved characters and spaces before the first word and after the last word removed. /proc/trim(text, max_length) if(max_length) - text = copytext(text, 1, max_length) + text = copytext_char(text, 1, max_length) return trim_left(trim_right(text)) //Returns a string with the first element of the string capitalized. /proc/capitalize(t as text) - return uppertext(copytext(t, 1, 2)) + copytext(t, 2) - -//Centers text by adding spaces to either side of the string. -/proc/dd_centertext(message, length) - var/new_message = message - var/size = length(message) - var/delta = length - size - if(size == length) - return new_message - if(size > length) - return copytext(new_message, 1, length + 1) - if(delta == 1) - return new_message + " " - if(delta % 2) - new_message = " " + new_message - delta-- - var/spaces = add_lspace("",delta/2-1) - return spaces + new_message + spaces - -//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose -/proc/dd_limittext(message, length) - var/size = length(message) - if(size <= length) - return message - return copytext(message, 1, length + 1) - + . = t[1] + return uppertext(.) + copytext(t, 1 + length(.)) /proc/stringmerge(text,compare,replace = "*") //This proc fills in all spaces with the "replace" var (* by default) with whatever //is in the other string at the same spot (assuming it is not a replace char). //This is used for fingerprints var/newtext = text - if(length(text) != length(compare)) - return 0 - for(var/i = 1, i < length(text), i++) - var/a = copytext(text,i,i+1) - var/b = copytext(compare,i,i+1) + var/text_it = 1 //iterators + var/comp_it = 1 + var/newtext_it = 1 + var/text_length = length(text) + var/comp_length = length(compare) + while(comp_it <= comp_length && text_it <= text_length) + var/a = text[text_it] + var/b = compare[comp_it] //if it isn't both the same letter, or if they are both the replacement character //(no way to know what it was supposed to be) if(a != b) if(a == replace) //if A is the replacement char - newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) + newtext = copytext(newtext, 1, newtext_it) + b + copytext(newtext, newtext_it + length(newtext[newtext_it])) else if(b == replace) //if B is the replacement char - newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) + newtext = copytext(newtext, 1, newtext_it) + a + copytext(newtext, newtext_it + length(newtext[newtext_it])) else //The lists disagree, Uh-oh! return 0 + text_it += length(a) + comp_it += length(b) + newtext_it += length(newtext[newtext_it]) + return newtext /proc/stringpercent(text,character = "*") @@ -334,16 +301,21 @@ if(!text || !character) return 0 var/count = 0 - for(var/i = 1, i <= length(text), i++) - var/a = copytext(text,i,i+1) + var/lentext = length(text) + var/a = "" + for(var/i = 1, i <= lentext, i += length(a)) + a = text[i] if(a == character) count++ return count /proc/reverse_text(text = "") var/new_text = "" - for(var/i = length(text); i > 0; i--) - new_text += copytext(text, i, i+1) + var/lentext = length(text) + var/letter = "" + for(var/i = 1, i <= lentext, i += length(letter)) + letter = text[i] + new_text = letter + new_text return new_text GLOBAL_LIST_INIT(zero_character_only, list("0")) @@ -366,15 +338,6 @@ GLOBAL_LIST_INIT(binary, list("0","1")) /proc/random_color() return random_string(6, GLOB.hex_characters) -/proc/add_zero2(t, u) - var/temp1 - while (length(t) < u) - t = "0[t]" - temp1 = t - if (length(t) > u) - temp1 = copytext(t,2,u+1) - return temp1 - //merges non-null characters (3rd argument) from "from" into "into". Returns result //e.g. into = "Hello World" // from = "Seeya______" @@ -387,41 +350,48 @@ GLOBAL_LIST_INIT(binary, list("0","1")) into = "" if(!istext(from)) from = "" - var/null_ascii = istext(null_char) ? text2ascii(null_char,1) : null_char - - var/previous = 0 + var/null_ascii = istext(null_char) ? text2ascii(null_char, 1) : null_char + var/copying_into = FALSE + var/char = "" var/start = 1 - var/end = length(into) + 1 - - for(var/i=1, i= text2ascii("a") && ca <= text2ascii("m")) - ca += 13 - else if(ca >= text2ascii("n") && ca <= text2ascii("z")) - ca -= 13 - else if(ca >= text2ascii("A") && ca <= text2ascii("M")) - ca += 13 - else if(ca >= text2ascii("N") && ca <= text2ascii("Z")) - ca -= 13 - result += ascii2text(ca) - return jointext(result, "") + var/lentext = length(text) + var/char = "" + var/ascii = 0 + . = "" + for(var/i = 1, i <= lentext, i += length(char)) + char = text[i] + ascii = text2ascii(char) + switch(ascii) + if(65 to 77, 97 to 109) //A to M, a to m + ascii += 13 + if(78 to 90, 110 to 122) //N to Z, n to z + ascii -= 13 + . += ascii2text(ascii) //Takes a list of values, sanitizes it down for readability and character count, //then exports it as a json file at data/npc_saves/[filename].json. @@ -607,7 +582,8 @@ GLOBAL_LIST_INIT(binary, list("0","1")) return //Regular expressions are, as usual, absolute magic - var/regex/all_invalid_symbols = new("\[^ -~]+") + //Any characters outside of 32 (space) to 126 (~) because treating things you don't understand as "magic" is really stupid + var/regex/all_invalid_symbols = new(@"[^ -~]{1}") var/list/accepted = list() for(var/string in proposed) @@ -615,34 +591,44 @@ GLOBAL_LIST_INIT(binary, list("0","1")) continue var/buffer = "" var/early_culling = TRUE - for(var/pos = 1, pos <= length(string), pos++) - var/let = copytext(string, pos, (pos + 1) % length(string)) - if(early_culling && !findtext(let,GLOB.is_alphanumeric)) + var/lentext = length(string) + var/let = "" + + for(var/pos = 1, pos <= lentext, pos += length(let)) + let = string[pos] + if(!findtext(let, GLOB.is_alphanumeric)) continue early_culling = FALSE - buffer += let - if(!findtext(buffer,GLOB.is_alphanumeric)) + buffer = copytext(string, pos) + break + if(early_culling) //Never found any letters! Bail! continue + var/punctbuffer = "" - var/cutoff = length(buffer) - for(var/pos = length(buffer), pos >= 0, pos--) - var/let = copytext(buffer, pos, (pos + 1) % length(buffer)) - if(findtext(let,GLOB.is_alphanumeric)) + var/cutoff = 0 + lentext = length_char(buffer) + for(var/pos = 1, pos <= lentext, pos++) + let = copytext_char(buffer, -pos, -pos + 1) + if(!findtext(let, GLOB.is_punctuation)) //This won't handle things like Nyaaaa!~ but that's fine break - if(findtext(let,GLOB.is_punctuation)) - punctbuffer = let + punctbuffer //Note this isn't the same thing as using += - cutoff = pos + punctbuffer += let + cutoff += length(let) if(punctbuffer) //We clip down excessive punctuation to get the letter count lower and reduce repeats. It's not perfect but it helps. var/exclaim = FALSE var/question = FALSE var/periods = 0 - for(var/pos = length(punctbuffer), pos >= 0, pos--) - var/punct = copytext(punctbuffer, pos, (pos + 1) % length(punctbuffer)) - if(!exclaim && findtext(punct,"!")) + lentext = length(punctbuffer) + for(var/pos = 1, pos <= lentext, pos += length(let)) + let = punctbuffer[pos] + if(!exclaim && findtext(let, "!")) exclaim = TRUE - if(!question && findtext(punct,"?")) + if(question) + break + if(!question && findtext(let, "?")) question = TRUE - if(!exclaim && !question && findtext(punct,".")) + if(exclaim) + break + if(!exclaim && !question && findtext(let, ".")) //? and ! take priority over periods periods += 1 if(exclaim) if(question) @@ -651,15 +637,13 @@ GLOBAL_LIST_INIT(binary, list("0","1")) punctbuffer = "!" else if(question) punctbuffer = "?" - else if(periods) - if(periods > 1) - punctbuffer = "..." - else - punctbuffer = "" //Grammer nazis be damned - buffer = copytext(buffer, 1, cutoff) + punctbuffer - if(!findtext(buffer,GLOB.is_alphanumeric)) - continue - if(!buffer || length(buffer) > 280 || length(buffer) <= cullshort || buffer in accepted) + else if(periods > 1) + punctbuffer = "..." + else + punctbuffer = "" //Grammer nazis be damned + buffer = copytext(buffer, 1, -cutoff) + punctbuffer + lentext = length_char(buffer) + if(!buffer || lentext > 280 || lentext <= cullshort || (buffer in accepted)) continue accepted += buffer @@ -696,7 +680,7 @@ GLOBAL_LIST_INIT(binary, list("0","1")) var/leng = length(string) - var/next_space = findtext(string, " ", next_backslash + 1) + var/next_space = findtext(string, " ", next_backslash + length(string[next_backslash])) if(!next_space) next_space = leng - next_backslash @@ -704,8 +688,8 @@ GLOBAL_LIST_INIT(binary, list("0","1")) return string var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash) - var/macro = lowertext(copytext(string, next_backslash + 1, next_space)) - var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1) + var/macro = lowertext(copytext(string, next_backslash + length(string[next_backslash]), next_space)) + var/rest = next_backslash > leng ? "" : copytext(string, next_space + length(string[next_space])) //See https://secure.byond.com/docs/ref/info.html#/DM/text/macros switch(macro) @@ -781,38 +765,35 @@ GLOBAL_LIST_INIT(binary, list("0","1")) return uppertext(pick(GLOB.alphabet)) /proc/unintelligize(message) - var/prefix=copytext(message,1,2) + var/regex/word_boundaries = regex(@"\b[\S]+\b", "g") + var/prefix = message[1] if(prefix == ";") - message = copytext(message,2) - else if(prefix in list(":","#")) - prefix += copytext(message,2,3) - message = copytext(message,3) + message = copytext(message, 1 + length(prefix)) + else if(prefix in list(":", "#")) + prefix += message[1 + length(prefix)] + message = copytext(message, length(prefix)) else - prefix="" + prefix = "" - var/list/words = splittext(message," ") var/list/rearranged = list() - for(var/i=1;i<=words.len;i++) - var/cword = pick(words) - words.Remove(cword) - var/suffix = copytext(cword,length(cword)-1,length(cword)) - while(length(cword)>0 && suffix in list(".",",",";","!",":","?")) - cword = copytext(cword,1 ,length(cword)-1) - suffix = copytext(cword,length(cword)-1,length(cword) ) + while(word_boundaries.Find(message)) + var/cword = word_boundaries.match if(length(cword)) rearranged += cword - message = "[prefix][jointext(rearranged," ")]" - . = message + shuffle_inplace(rearranged) + return "[prefix][jointext(rearranged, " ")]" /proc/readable_corrupted_text(text) var/list/corruption_options = list("..", "£%", "~~\"", "!!", "*", "^", "$!", "-", "}", "?") var/corrupted_text = "" + var/lentext = length(text) + var/letter = "" // Have every letter have a chance of creating corruption on either side // Small chance of letters being removed in place of corruption - still overall readable - for(var/letter_index = 1; letter_index <= length(text); letter_index++) - var/letter = text[letter_index] + for(var/letter_index = 1, letter_index <= lentext, letter_index += length(letter)) + letter = text[letter_index] if (prob(15)) corrupted_text += pick(corruption_options) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 9cd1dd50af7..9495d2fb174 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -549,26 +549,3 @@ else //regex everything else (works for /proc too) return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", "")) -/proc/strtohex(str) - if(!istext(str)||!str) - return - var/r - var/c - for(var/i = 1 to length(str)) - c= text2ascii(str,i) - r+= num2hex(c) - return r - -// Decodes hex to raw byte string. -// If safe=TRUE, returns null on incorrect input strings instead of CRASHing -/proc/hextostr(str, safe=FALSE) - if(!istext(str)||!str) - return - var/r - var/c - for(var/i = 1 to length(str)/2) - c = hex2num(copytext(str,i*2-1,i*2+1), safe) - if(isnull(c)) - return null - r += ascii2text(c) - return r diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 76c2ad78b6e..f49e49e2895 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -181,15 +181,15 @@ Turf and target are separate in case you want to teleport some distance from a t //Returns whether or not a player is a guest using their ckey as an input /proc/IsGuestKey(key) if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx - return 0 + return FALSE var/i, ch, len = length(key) - for (i = 7, i <= len, ++i) + for (i = 7, i <= len, ++i) //we know the first 6 chars are Guest- ch = text2ascii(key, i) - if (ch < 48 || ch > 57) - return 0 - return 1 + if (ch < 48 || ch > 57) //0-9 + return FALSE + return TRUE //Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() /mob/proc/apply_pref_name(role, client/C) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 3ec2510cc78..15aa07c7b9d 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -39,6 +39,26 @@ #error You need version 512 or higher #endif +//Compatability -- These procs were added in 513.1493, not 513.1490 +//Which really shoulda bumped us up to 514 right then and there but instead Lummox is a dumb dumb +#if DM_BUILD < 1493 +#define length_char(args...) length(args) +#define text2ascii_char(args...) text2ascii(args) +#define copytext_char(args...) copytext(args) +#define splittext_char(args...) splittext(args) +#define spantext_char(args...) spantext(args) +#define nonspantext_char(args...) nonspantext(args) +#define findtext_char(args...) findtext(args) +#define findtextEx_char(args...) findtextEx(args) +#define findlasttext_char(args...) findlasttext(args) +#define findlasttextEx_char(args...) findlasttextEx(args) +#define replacetext_char(args...) replacetext(args) +#define replacetextEx_char(args...) replacetextEx(args) +// /regex procs +#define Find_char(args...) Find(args) +#define Replace_char(args...) Replace(args) +#endif + //Additional code for the above flags. #ifdef TESTING #warn compiling in TESTING mode. testing() debug messages will be visible. diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 7d25fe56b95..1137c83d3da 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -199,14 +199,16 @@ var/list/new_message = list() for(var/word in message_split) - var/suffix = copytext(word,-1) - - // Check if we have a suffix and break it out of the word - if(suffix in list("." , "," , ";" , "!" , ":" , "?")) - word = copytext(word,1,-1) - else - suffix = "" + var/suffix = "" + var/suffix_foundon = 0 + for(var/potential_suffix in list("." , "," , ";" , "!" , ":" , "?")) + suffix_foundon = findtext(word, potential_suffix, -length(potential_suffix)) + if(suffix_foundon) + suffix = potential_suffix + break + if(suffix_foundon) + word = copytext(word, 1, suffix_foundon) word = html_decode(word) if(lowertext(word) in common_words) @@ -216,10 +218,10 @@ new_message += pick("uh","erm") break else - var/list/charlist = string2charlist(word) // Stupid shit code + var/list/charlist = text2charlist(word) + charlist.len = round(charlist.len * 0.5, 1) shuffle_inplace(charlist) - charlist.len = round(charlist.len * 0.5,1) - new_message += html_encode(jointext(charlist,"")) + suffix + new_message += jointext(charlist, "") + suffix message = jointext(new_message, " ") diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm index 28bd71c3d66..53f7336d954 100644 --- a/code/datums/wires/_wires.dm +++ b/code/datums/wires/_wires.dm @@ -120,7 +120,7 @@ return TRUE /datum/wires/proc/is_dud(wire) - return dd_hasprefix(wire, WIRE_DUD_PREFIX) + return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1) /datum/wires/proc/is_dud_color(color) return is_dud(get_wire(color)) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index f2b760cafae..32423e67ee4 100755 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -433,7 +433,7 @@ var/dat = "" if(SSshuttle.emergency.mode == SHUTTLE_CALL) var/timeleft = SSshuttle.emergency.timeLeft() - dat += "Emergency shuttle\n
\nETA: [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + dat += "Emergency shuttle\n
\nETA: [timeleft / 60 % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]" var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500) diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 18384a611bf..53c64d9e1c2 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -165,7 +165,7 @@ if(timing) var/disp1 = id var/time_left = time_left(seconds = TRUE) - var/disp2 = "[add_zero(num2text((time_left / 60) % 60),2)]:[add_zero(num2text(time_left % 60), 2)]" + var/disp2 = "[add_leading(num2text((time_left / 60) % 60), 2, "0")]:[add_leading(num2text(time_left % 60), 2, "0")]" if(length(disp2) > CHARS_PER_LINE) disp2 = "Error" update_display(disp1, disp2) diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index f4a9f4f8d84..4ba26fd5ff8 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -306,10 +306,14 @@ Code: var/list/S = list(" Off","AOff"," On", " AOn") var/list/chg = list("N","C","F") - +//Neither copytext nor copytext_char is appropriate here; neither 30 UTF-8 code units nor 30 code points equates to 30 columns of output. +//Some glyphs are very tall or very wide while others are small or even take up no space at all. +//Emojis can take modifiers which are many characters but render as only one glyph. +//A proper solution here (as far as Unicode goes, maybe not ideal as far as markup goes, a table would be better) +//would be to use [A.area.name] for(var/obj/machinery/power/apc/A in L) - menu += copytext(add_tspace(A.area.name, 30), 1, 30) - menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(DisplayPower(A.lastused_total), 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]
" + menu += copytext_char(add_trailing(A.area.name, 30, " "), 1, 30) + menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_leading(DisplayPower(A.lastused_total), 6, " ")] [A.cell ? "[add_leading(round(A.cell.percent()), 3, " ")]% [chg[A.charging+1]]" : " N/C"]
" menu += "" diff --git a/code/modules/admin/check_antagonists.dm b/code/modules/admin/check_antagonists.dm index 97279bb6eb5..0bf418f3516 100644 --- a/code/modules/admin/check_antagonists.dm +++ b/code/modules/admin/check_antagonists.dm @@ -151,10 +151,10 @@ else var/timeleft = SSshuttle.emergency.timeLeft() if(SSshuttle.emergency.mode == SHUTTLE_CALL) - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
" + dat += "ETA: [(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]
" dat += "Send Back
" else - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
" + dat += "ETA: [(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]
" dat += "Continuous Round Status
" dat += "[CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]" if(CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag]) diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 87501bc44f8..a5c6b61be6f 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -121,7 +121,8 @@ Des: Removes all infected images from the alien. /mob/living/carbon/alien/proc/RemoveInfectionImages() if (client) for(var/image/I in client.images) - if(dd_hasprefix_case(I.icon_state, "infected")) + var/searchfor = "infected" + if(findtext(I.icon_state, searchfor, 1, length(searchfor) + 1)) qdel(I) return diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 126342c19ab..6090f5b35fa 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -129,5 +129,6 @@ Des: Removes all images from the mob infected by this embryo /obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages() for(var/mob/living/carbon/alien/alien in GLOB.player_list) for(var/image/I in alien.client.images) - if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner) + var/searchfor = "infected" + if(I.loc == owner && findtext(I.icon_state, searchfor, 1, length(searchfor) + 1)) qdel(I) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 3aabfa69335..07cba3d680b 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -391,9 +391,13 @@ if(MUTCOLORS in N.dna.species.species_traits) //take current alien color and darken it slightly var/newcolor = "" - var/len = length(N.dna.features["mcolor"]) - for(var/i=1, i<=len, i+=1) - var/ascii = text2ascii(N.dna.features["mcolor"],i) + var/string = N.dna.features["mcolor"] + var/len = length(string) + var/char = "" + var/ascii = 0 + for(var/i=1, i<=len, i += length(char)) + char = string[i] + ascii = text2ascii(char) switch(ascii) if(48) newcolor += "0" @@ -404,7 +408,7 @@ if(98 to 102) newcolor += ascii2text(ascii-1) //letters b to f lowercase if(65) - newcolor +="9" + newcolor += "9" if(66 to 70) newcolor += ascii2text(ascii+31) //letters B to F - translates to lowercase else diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 5be23f5a9d0..c17cf6a4e52 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -686,7 +686,7 @@ if(timeleft > 1 HOURS) return "--:--" else if(timeleft > 0) - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, " ")]" else return "00:00" diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 14d2a3d22f8..e92ef603588 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -188,17 +188,17 @@ //Cut out the name so it doesn't trigger commands message = copytext(message, 0, start)+copytext(message, start + length(devilinfo.truename), length(message) + 1) break - else if(dd_hasprefix(message, L.real_name)) + else if(findtext(message, L.real_name, 1, length(L.real_name) + 1)) specific_listeners += L //focus on those with the specified name //Cut out the name so it doesn't trigger commands found_string = L.real_name - else if(dd_hasprefix(message, L.first_name())) + else if(findtext(message, L.first_name(), 1, length(L.first_name()) + 1)) specific_listeners += L //focus on those with the specified name //Cut out the name so it doesn't trigger commands found_string = L.first_name() - else if(L.mind && L.mind.assigned_role && dd_hasprefix(message, L.mind.assigned_role)) + else if(L.mind && L.mind.assigned_role && findtext(message, L.mind.assigned_role, 1, length(L.mind.assigned_role) + 1)) specific_listeners += L //focus on those with the specified job //Cut out the job so it doesn't trigger commands found_string = L.mind.assigned_role