Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork630
feat(core): add the ability display outputs with multiple lines to console#1370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Conversation
…nsoleResolves#1301This commit adds the ability to display output strings with multiplelines to the console, making output strings will not always getoverlapped even if there is no room for displaying in single line.Uses binary search to avoid taking too long time to calculate where tosplit strings.Do note that this commit itself does not make it possible to displaylong strings that needs to be pushed with more than 4 substrings wherethe length is up to 99 excluding the null bytes. This is probablybecause END_TEXT_COMMAND_DISPLAY_TEXT cannot display strings with morethan 4 variable substrings. Maybe ScriptTextLinesFourSubstringsThreeNumbersin gameconfig.xml is relevant a bit?
kagikn commentedJan 9, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Gosh, the display text script command, well I mean
|
In script display text natives/commands, For those who wants to parse HTML // When you see '<', make sure the following character is not '!' or '/' before parsing as a start element.// When you skip HTML comments, unlike html documents in the Web, keep in mind that hyphens do not have to be placed other than before an ending greater than character// (e.g. `<!br-->` will be skipped as comments but `<!--br->` will be parsed as an HTML br tag).// See `GFxSGMLParser<Char>::SkipComment` in `GFxSGMLParser.h` in that secret code to verify how the game would skip html comments.// When you parse an starting HTML element, you may have to skip space characters and the set below contains all the characters that the scaleform treats as space characters!// This set contains all the values where `G_iswspace` in `Gstd.h` would return non-zero values and the game would treat as space characters!HashSet<char>SpaceCharactersForScaleform=new(){'\u0009','\u000a','\u000b','\u000c','\u000d','\u0020','\u00a0','\u1680','\u2000','\u2001','\u2002','\u2003','\u2004','\u2005','\u2006','\u2007','\u2008','\u2009','\u200a','\u200b','\u2028','\u2029','\u202f','\u3000'}; |
kagikn commentedJan 15, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
as a memo, some stuff to parse tokens output strings to avoid tearing tokens apart: publicenumTokenStateOfParseToken{/// <summary>/// standard text (not found a token yet)/// </summary>StandardArea,/// <summary>/// inside a token, gathering all the token text/// </summary>InsideTokenArea,/// <summary>/// fully completed, ready to go back to standard text again/// </summary>Completed,}constcharTokenEscape='\\';constcharTokenDelimiter='~';publicstaticList<string>SplitStringByNewLineTokens(stringstr){// same max length as `MAX_TOKEN_LEN` macro in `TextFormat.h` but without the null terminatorconstintMaxTokenLen=63;StringBuildertokenNameBuffer=newStringBuilder(MaxTokenLen);TokenStateOfParseTokentokenState=TokenStateOfParseToken.StandardArea;charprevChar='\0';inti=0;intstartPosOfSubstring=0;intendPosOfSubstring=0;List<string>substrings=newList<string>();while(i<str.Length){charnewCharacter=str[i];if(newCharacter==TokenEscape&&(i+1)<str.Length){// Get the next character. If valid (not null), check if it is the delimiter.// If it is the delimiter, skip this escape character. The code below// will handle treating the delimiter as a normal character.// NOTE: the next token delimiter will be added as a part of the token name if the token state is// inside token area, but this behavior is exactly the same as how the game parses.charpeek=str[i+1];if(peek==TokenDelimiter){prevChar=newCharacter;i++;continue;}}if(newCharacter==TokenDelimiter&&(i==0||prevChar!=TokenEscape)){if(tokenState==TokenStateOfParseToken.StandardArea){tokenState=TokenStateOfParseToken.InsideTokenArea;prevChar=newCharacter;endPosOfSubstring=i;i++;continue;}elseif(tokenState==TokenStateOfParseToken.InsideTokenArea){stringtokenName=tokenNameBuffer.ToString();tokenNameBuffer.Clear();tokenState=TokenStateOfParseToken.StandardArea;prevChar=newCharacter;// The new line token IS case-sensitive in the game unlike how the bold and italic style// tokens, wanted star token, Rockstar logo token, and non-renderable token (~nrt~) are parsed.if(tokenName=="n"){substrings.Add(str.Substring(startPosOfSubstring,endPosOfSubstring-startPosOfSubstring));i++;startPosOfSubstring=i;endPosOfSubstring=i;}else{i++;endPosOfSubstring=i;}}}elseif(tokenState==TokenStateOfParseToken.InsideTokenArea){// Emulate how the game appends the token name to the token name buffer 😛if(tokenNameBuffer.Length<MaxTokenLen){tokenNameBuffer.Append(newCharacter);}prevChar=newCharacter;i++;}elseif(tokenState==TokenStateOfParseToken.StandardArea){prevChar=newCharacter;i++;endPosOfSubstring=i;}}endPosOfSubstring=i;if(endPosOfSubstring-startPosOfSubstring>0){substrings.Add(str.Substring(startPosOfSubstring,endPosOfSubstring-startPosOfSubstring));}returnsubstrings;}publicstaticList<RangeCompact>FindAllTokenRanges(stringstr){TokenStateOfParseTokentokenState=TokenStateOfParseToken.StandardArea;charprevChar='\0';inti=0;intstartPosOfToken=0;intendPosOfToken=0;List<RangeCompact>ranges=newList<RangeCompact>();while(i<str.Length){charnewCharacter=str[i];if(newCharacter==TokenEscape&&(i+1)<str.Length){// Get the next character. If valid (not null), check if it is the delimiter.// If it is the delimiter, skip this escape character. The code below// will handle treating the delimiter as a normal character.charpeek=str[i+1];if(peek==TokenDelimiter){prevChar=newCharacter;i++;// endPosOfToken will be set to i + 1 latercontinue;}}if(newCharacter==TokenDelimiter&&(i==0||prevChar!=TokenEscape)){if(tokenState==TokenStateOfParseToken.StandardArea){tokenState=TokenStateOfParseToken.InsideTokenArea;prevChar=newCharacter;startPosOfToken=i;endPosOfToken=i+1;i++;continue;}elseif(tokenState==TokenStateOfParseToken.InsideTokenArea){tokenState=TokenStateOfParseToken.StandardArea;prevChar=newCharacter;ranges.Add(newRangeCompact(startPosOfToken,endPosOfToken+1));i++;startPosOfToken=i;endPosOfToken=i;}}elseif(tokenState==TokenStateOfParseToken.InsideTokenArea){prevChar=newCharacter;i++;endPosOfToken=i;}elseif(tokenState==TokenStateOfParseToken.StandardArea){prevChar=newCharacter;i++;}}if(endPosOfToken-startPosOfToken>0){ranges.Add(newRangeCompact(startPosOfToken,endPosOfToken+1));}returnranges;}publicreadonlystructRangeCompact:IEquatable<RangeCompact>{/// <summary>Represent the inclusive start index of the Range.</summary>publicintStart{get;}/// <summary>Represent the exclusive end index of the Range.</summary>publicintEnd{get;}/// <summary>Construct a Range object using the start and end indexes.</summary>/// <param name="start">Represent the inclusive start index of the range.</param>/// <param name="end">Represent the exclusive end index of the range.</param>publicRangeCompact(intstart,intend){Start=start;End=end;}/// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary>/// <param name="value">An object to compare with this object.</param>publicoverrideboolEquals(object?value)=>valueisRangeCompactr&&r.Start.Equals(Start)&&r.End.Equals(End);/// <summary>Indicates whether the current Range object is equal to another Range object.</summary>/// <param name="other">An object to compare with this object</param>publicboolEquals(RangeCompactother)=>other.Start.Equals(Start)&&other.End.Equals(End);/// <summary>Returns the hash code for this instance.</summary>publicoverrideintGetHashCode(){returnStart.GetHashCode()*39+End.GetHashCode();}} |
7f69174 to07f22e7Compare
Uh oh!
There was an error while loading.Please reload this page.
Resolves#1301
Summary
This commit adds the ability to display output strings with multiple lines to the console, making output strings will not always get overlapped even if there is no room for displaying in single line.
Uses binary search to avoid taking too long time to calculate where to split strings.
Do note that the initial commit does not make it possible to display long strings that needs to be pushed with more than 4 substrings where the length is up to 99 excluding the null bytes. This is probably because
END_TEXT_COMMAND_DISPLAY_TEXTcannot display strings with more than 4 variable substrings (more than 397 bytes if all the characters in the string are ASCII ones). Maybe ScriptTextLinesFourSubstringsThreeNumbers in gameconfig.xml is relevant a bit? I'll try to find a way to draw long strings where more than 4 variable substrings has to be pushed in later commits.This commit does not make a window for the current input since it isn't as important as the output window IMHO, the console still displays in a silly way if the input is too long to fit in a single line. Would be fixed when we start to use ImGui for the console though.
Showcase
Type the lines below for each to test:
The last one need to set the game language to Japanese or use a font file for the font file for the language you use (the efigs one for most people who see this page?) where the Chalet 1960 London font has all the japanese glyphs of strings, before you can see the text.
I believe I could make the new code more clean, especially the pieces of it where how many spaces the console should keep for a output string and how to set text style variables...