GML [SOLVED]: Parsing Strings ignoring substring

C

CedSharp

Guest
So I don't want to create a book out of my problem, here it is:

I have a string, say 'Hello [c:yello]friend[c:default]! How are you?'.
That string has tags (starts with [ and ends with ]) inside of it for colors.

Now, I'm trying to make a draw_text_ext that ignores said tags, but manually.
Basically I want my script to return a ds_list of 'lines'.

So I want to wrap the text 'Hello friend! How are you' but after it's wrapped, put back the tags where they belong.
I've tried different ways but always end up not being able to track where the tags go or calculating the wrong number of characters (so it's not ignoring the tags like I want)

Anyone could point me in different ways of tackling this? I'm just very frustrated by this small issue.

---

EDIT: I ended up implementing a complex structure of data to do this. The simple 'wrap ignoring substring' logic just didn't make sense I suppose. Thanks for everyone for trying to help me.
 
Last edited:
Basically, iterate through the string one character at a time. Add each character to the current line, and in a second variable, add the width of the character. If the character is an opening tag, start a second loop that keeps adding the characters to the string, but does not add the width, until the closing tag is found.
 

TheouAegis

Member
I'd have a couple ds_lists, one holding each string separated whenever a tag is encountered, one for colors per string, and another for anything else you may have. If no tag, set the first color to default and copy the string to the strings list until you encounter a tag.
 

Gamebot

Member
I have a string, say 'Hello [c:yello]friend[c:default]! How are you?'.
I don't think string_width_ext is necessary. If your planning on having a bunch of different strings I would just use the same set up with each string along with a parse token. Then use a parse script/string_split script to extract what you need. You can hold data within a script or the create event with an array. To go to next line use"#". Something like:

Create event:
Code:
//Empty array
mytxt = "";
scr_text:
Code:
var num = argument0;

// The data! This is arranged by how it draws.
txt[0] = "c_yellow | Hello#how are you? | c:default"
txt[1] = "c_red | Oh, thank you! | c:default"
.
.

var myarray = scr_parse(txt[num], "|")
return myarray
Then on call:
Code:
//This will call the entire array by number.
mytxt = scr_txt(0)
Then within your array you would see:
mytxt[0] = c_yellow
mytxt[1] = "Hello#how are you?"
mytxt[2] = c:default

Draw:
Code:
draw_set_color(mytxt[0]);  //Always mytxt[0]
draw_text(x, y, mytxt[1]);  // Always mytxt{1]
draw_set_color(mytxt[2]) // Always mytxt[2]
You can find string_split and parse scripts all over the internet. Including but not limited to: gmlscripts.com
 
Last edited:
C

CedSharp

Guest
Basically, iterate through the string one character at a time. Add each character to the current line, and in a second variable, add the width of the character. If the character is an opening tag, start a second loop that keeps adding the characters to the string, but does not add the width, until the closing tag is found.
Sadly I cannot just work with 2 variables as you suggest because I won't want only the width of the text-until-now but more precisely the width of each and every line. I also have to parse the line to find spaces and/or special characters like ( or - to allow breaking the line.

I'd have a couple ds_lists, one holding each string separated whenever a tag is encountered, one for colors per string, and another for anything else you may have. If no tag, set the first color to default and copy the string to the strings list until you encounter a tag.
and
Then use a parse script/string_split script to extract what you need. You can hold data within a script or the create event with an array. To go to next line use"#".
I already have a ds_list of all the tags and all the text, separated. I have no issue with that. My issue is how to remove the tags from the text, do the wrapping, and then add those tags back at the correct location.

If removing/adding them is not a viable solution, how can I effectively ignore them during the wrapping of the text. That my big issue right now.

------

Ok I think I didn't explain my problem properly, let me reformulate:

I have a string with tags, that you all understood very well.
This said string uses a monospace font, so I don't care about string_width, I'm using string_length only.

My code does 2 things:
1. It wraps the text in a ds_list
2. For each lines, it pads the end with spaces and adds '|' to the start and the end.

My goal is to create a script that creates a box using some special characters. My script works fine as long as I have no tags, but the goal is to modify it in order to support tags. The wrapping must occure like if the tags don't exists, but once the wrapping is done, the tags must be re-inserted at the correct location. This is the part that I'm having issues with.

I'm able to get a ds_list that looks like this:

Code:
DS_LIST [
    Array [ 0, 1, "Hello " ],
    Array [ 1, 7, "c", "yellow" ],
    Array [ 0, 16, "friend" ],
    Array [ 1, 23, "c", "default" ],
    Array [ 0, 34, "! How are you?" ]
]
Where the first number is 0=text and 1=tag, the second is the index in the string where this 'part' belongs, and finally the rest in data related to either text or tag.
That's not a problem. Now my issue is how to wrap the text like if the tags don't exists, but keep the tags in the wrapped text at the end.

Right now, my script includes the tag text in the wrapping algorithm, which obviously breaks it.
I calculate the wrapping before I do the rendering logic, which means I can't get rid of the tags before I do the wrapping logic.
I end up with 'tokens' which tell my renderer how to render them:

Code:
// A token
Array [ 'Hello', 16, 8, c_black, f_mono ]
//    [    Text,  x, y,   color,   font ]

Here is an illustration of what I'm trying to achieve:

Code:
// this bit of text
Hello [c:yellow]friend[c:default]! How are you?

// Should wrap as follow (note the box of character around it!)
+----------------+
| Hello friend!  |
| How are you ?  |
+----------------+

// Now because of the tags, the length gets
// completely wrong, and I get this
+----------------+
| Hello      |
| friend!           |
| How are you ? |
+----------------+

// Because there is no tags around the last bit it wraps well.
So I'm supposed to end up with this (note the trailing spaces, they are important to add the closing wall of the box):
Code:
DS_LIST [
    "Hello [c:yellow]friend[c:default]!  ",
    "How are you ?  "
]
In this specific case, the tags are all on the first line, but obviously they can be sparse anywhere.
I hope this clears up what my issue is.

1. I cannot remove those tags before I apply the wrapping logic
2. The tags should not count as a character for the wrapping, that's what I'm trying to do
3. Once the wrapping is completed, the tags should be at the correct position in each lines.

Thanks for anyone taking the time to answer. I'll probably end up doing something more complicated as in 'not a single script' instead, which lots of data structures.
I wanted to avoid that route using a 'quick but effective script' but I'm starting to think there is no such thing as that for this specific issue.


-------

EDIT: An external source pointed out that I should focus more on how I treat indexes rather than trying to think about removing/adding text. Said source mentionned that, for example, if I try to get character at position 9 (which is inside a tag) I should instead 'convert' this index to the new index 19, since "[c:yellow]" has a length of 10.

But that means that I have to change how I call string_char_at(), amd string_copy() since I now have to convert all of those value... constantly.
 
Last edited:

TheouAegis

Member
What I'm saying is your lists would be like

1. Hello 1. Default
2. friend 2. c_yellow
3. ! How are you? 3. Default

Then when you write the text, you set the color first then write the text. You'd have an iterator telling you which color goes with which text.
 
C

CedSharp

Guest
What I'm saying is your lists would be like

1. Hello 1. Default
2. friend 2. c_yellow
3. ! How are you? 3. Default

Then when you write the text, you set the color first then write the text. You'd have an iterator telling you which color goes with which text.
I cannot separate the tags from the text when I do the wrapping because of how my system works.

Alright. Let's put it differently:
Forget about GameMaker using 'draw_set_color' for a second and imagine that some special escape sequence exists to change the color of the text.
Let's say `\c2` and `\c3` are special escape codes. `\c` counts as 1 character, while the number following it (1 or 2 digits) are normal characters.

I gotta remove them for the length to work correctly during the wrapping phase, but I gotta put them back, because imaginary script `draw_colored_text`
requires those to be inside the string when I draw.

How can I wrap the text but keep the `\c26` inside the string, without affecting the length for the wrapping of the text, since the length is important to decide how many space I append in order to reach the end wall of the box ?
 
C

CedSharp

Guest
I have abandonned the idea of doing this as a 'simple wrap while ignoring substring'.
I instead implemented a whole structure of parsing, ds_lists and multiple pass loops in order to achieve what I wanted.
I was able to create a simple script 'scr_wrap_text()' but this one uses many other more complex scripts behind the scenes.

I'm sorry for not being able to explain my problem better.
Thanks for everyone who tried to help me, I appreciate a lot.
 
Top