Section 7: Text-Based EUDs
I'm sure some of you have thought, "How cool would it be to have a StarCraft game where players can type commands and things would happen?" If your plan is to make a text-input game using EUDs, this next section is going to be full of anguish and disappointment for you. I really do not see any practical implementation of Display Text EUDs, and this section is primarily just an educational resource. Don't get me wrong: you can still do some very useful things using text detection. Checking to see if a particular player is in the game, for example, is not too difficult to implement. (Oh, the hilarity of writing an EUD that drops/defeats certain usernames each time they try to play!)
Section 7.1: Calculating the Value of TextThe original resource I used to first learn text calculation is no longer existent. But! Since then, there have been programs written that will do this tedious calculation for you. If you already acquired or looked at the text resources from Section 2, you may have read how text detection works. For those who did not see it and/or like to read:
Manual Calculation
First, SC reads characters in groups of four, so if your string is longer than that, you'll need multiple EUDs to detect the entirety of it. Take the string "Hello World" as an example. The first step is to split the text into groups of four:
Hell
o Wo
rld
The last group ends with only three characters, but this is fine. For this string, you'll need three EUD conditions to detect the exact phrase. When SC reads the text, it reads the group of four characters in reverse order. The next step is to reverse the order of the letters in each group:
lleH
oW o
dlr
Now, we can't actually write EUDs for strings because the trigger requires a number, so we have to represent each string as a number. To convert the text into a value, we must look up each letter in a character map (Windows computers should already have this) and use its hexadecimal value. When we've done that for all letters, we should end up with a hexadecimal number:
6C6C6548
6F57206F
646C72
For the first group, l's value is 6C, e's value is 65, and H's value is 48. When put all together, it makes a large hexadecimal number (6C 6C 65 48 -> 6C6C6548). (It should be noted that the hex value of a space is 20, but it doesn't show up on some character maps). Finally, we need to convert these values to decimal, as that is what SC reads. Just use a calculator or a hex to decimal converter:
1819043144
1867980911
6581362
Now you finally have the values you need to make an EUD condition for detecting the text "Hello World" from some address. The trigger will end up looking a little like this:
Memory at Death Table + 186879 is exactly 1819043144
Memory at Death Table + 186880 is exactly 1867980911
Memory at Death Table + 186881 is exactly 6581362
Notice that for whichever starting address value you're using, the next group of four characters is at the next address value (+1). Above, "Hell" is read at 186879, "o Wo" is read at 186880, and "rld" is read at 186881.
Also, for those of you crazy enough to dream, yoonkwun wrote a program that will generate universal triggers for player chat. You can find this here:
http://www.staredit.net/topic/11426/. I will lightly touch on this in a bit more detail in segment 7.4.
Section 7.2: The 11 AddressesAssuming you want to read from "Display Text Message" type text, you will require 11 triggers to check for one string. That's right. If you want to read when "Hello there" is on the screen, you have to write 11 triggers for it. Why? Because each of the 11 chat rows in SC has its own address, and in order to ensure that the message will not be missed, each address needs to check for the message.
From the
EUDDB entry, we can see that this madness starts at EUD 186879. The following EUD values are +55 values apart from each other. Here is a list of all EUD values for display text:
186879 ROW 1
186934 ROW 2
186988 ROW 3
187043 ROW 4
187097 ROW 5
187152 ROW 6
187206 ROW 7
187261 ROW 8
187315 ROW 9
187370 ROW 10
187424 ROW 11
An interesting thing to note is that the very first line in-game is actually ROW 11, and the second chat line is in fact ROW 1. This won't be a concern if you write triggers for all rows, though.
Now, how do these rows work? Well, say a player named "Ike" types "Hello" right when the map starts. ROW 11 gets populated with the text "Ike: Hello" immediately, and ROW 1 becomes the next chat line that will be populated. If Ike is a bit of a spammer and says "World," "How," "Goes," and "It?" afterward, then ROW 1 will have "Ike: World," ROW 2 will have "Ike: How," ROW 3 will have "Ike: Goes," ROW 4 will have "Ike: It?," and ROW 5 will be the next chat line that will be populated. The next chat line is always determined by the number of messages that have occurred,
not by where they are displayed on the screen. (For those interested, the address 0x640B58 / EUD 186877 will store the value of which row will be the next chat line.)
There's an additional thing to note here: every even row (2, 4, 6, 8, 10) is offset by two characters. This means if you were reading the string "Hello there," these rows would be reading "llo there."
So, what can we take from this? It's really annoying to make these kinds of EUDs. But hey, if your Display Text Message action just spams the same thing on all 11 rows, you'd only need to check one row for the value. So there's that.
Well, how would we write EUDs to check the text "Hello there"? We have the EUD values, and we have converters to convert the text into a number to evaluate. Let's get to it. Using a text converter, we get:
ODD ROWS:
"Hell" -> 1819043144
"o th" -> 1752440943
"ere" -> 6648421
EVEN ROWS:
"llo " -> 544173164
"ther" -> 1919248500
"e" -> 101
Now, for each group of four letters after the first, we add one to the memory value we're using. For example, if we were reading from ROW 1, we would check:
186879 for "Hell"
186880 for "o th"
186881 for "ere"
Do you want to see what the complete trigger set looks like to check "Hello there"? Do you really? ... Alright, but you've been warned:
Detecting "Hello there"
Player 1
Memory at Death Table + 186879 is exactly 1819043144
Memory at Death Table + 186880 is exactly 1752440943
Memory at Death Table + 186881 is exactly 6648421Set Switch 1 Player 1
Memory at Death Table + 186934 is exactly 544173164
Memory at Death Table + 186935 is exactly 1919248500
Memory at Death Table + 186936 is exactly 101Set Switch 1 Player 1
Memory at Death Table + 186988 is exactly 1819043144
Memory at Death Table + 186989 is exactly 1752440943
Memory at Death Table + 186990 is exactly 6648421Set Switch 1 Player 1
Memory at Death Table + 187043 is exactly 544173164
Memory at Death Table + 187044 is exactly 1919248500
Memory at Death Table + 187045 is exactly 101Set Switch 1 Player 1
Memory at Death Table + 187097 is exactly 1819043144
Memory at Death Table + 187098 is exactly 1752440943
Memory at Death Table + 187099 is exactly 6648421Set Switch 1 Player 1
Memory at Death Table + 187152 is exactly 544173164
Memory at Death Table + 187153 is exactly 1919248500
Memory at Death Table + 187154 is exactly 101Set Switch 1 Player 1
Memory at Death Table + 187206 is exactly 1819043144
Memory at Death Table + 187207 is exactly 1752440943
Memory at Death Table + 187208 is exactly 6648421Set Switch 1 Player 1
Memory at Death Table + 187261 is exactly 544173164
Memory at Death Table + 187262 is exactly 1919248500
Memory at Death Table + 187263 is exactly 101Set Switch 1 Player 1
Memory at Death Table + 187315 is exactly 1819043144
Memory at Death Table + 187316 is exactly 1752440943
Memory at Death Table + 187317 is exactly 6648421Set Switch 1 Player 1
Memory at Death Table + 187370 is exactly 544173164
Memory at Death Table + 187371 is exactly 1919248500
Memory at Death Table + 187372 is exactly 101Set Switch 1 Player 1
Memory at Death Table + 187424 is exactly 1819043144
Memory at Death Table + 187425 is exactly 1752440943
Memory at Death Table + 1874246 is exactly 6648421Set Switch 1 When Switch 1 is set, you'll know that your text appeared. Do you see why hardly anyone uses Display Text for practical applications? It's bulky, annoying, error-prone, inefficient, and messy.
More Advanced Stuff
For a more accurate reading on even rows, you can search between a range in which the last two characters in the character group would have to be the first two characters you want to detect. The calculation for this is complicated to explain and requires a deeper understanding of how text detection works (or rather, how converting strings to integer groups, arranging by endianness, and calculating the min and max value of an integer with the last two bytes determined works). This process is done in
http://www.staredit.net/topic/15793/ automatically.
In essence, the reason we take 4 character groups is because 4 chars is the same size as an integer, and SC is reading an integer value when doing comparisons. Characters are really just numerical values that represent a letter or number or other symbol. To calculate four bytes of characters into an integer value, we do the following (given "s" is the string, or array of chars):
(s[0] * 1) + (s[1] * 256) + (s[2] * 65536) + (s[3] * 16777216)
The way you probably first learned how to calculate string values was by reversing the order of the characters and putting their hexadecimal values together, and then converting that to the decimal value. The reason this convenient little trick works is because the size of a byte fits into a two-digit hexadecimal value, so by converting the characters to their hexadecimal values, you're representing them as bytes. Now, Intel processors are little-endian, meaning the least significant byte comes first, which is opposite of how our numerical system works. For example:
1000 in the numerical system is bigger than 0001 (which is just 1).
However, [1, 0, 0, 0] (which equals 1) is smaller than [0, 0, 0, 1] (which is equal to 16777216 when read as an integer, because the last byte's value becomes 1*256*256*256).
Because this calculation is backwards, we reverse the characters so that when we combine them, the end result can be converted to decimal and represent the value we want.
Another way of looking at it that may make more sense when thinking native to traditional numbers is that each time you add "00" to the end of a hexadecimal number, it multiplies it by 256. So if we have the value "5A" (90 in decimal) and change it to "5A00," the resulting value is 23040 (90 * 256). Therefore, whatever is further to the left in a hexadecimal value is multiplied by factors of 256.
Here's an example:
Detect "Mike":
M = 4D
i = 69
k = 6B
e = 65
So we have our array of [4D, 69, 6B, 65]. The operation we want to do is (4D * 1) + (69 * 256) + (6B * 65536) + (65 * 16777216). If we want to do this automatically, we can reverse the values and put them together to be 656B694D. Going back to how each value is multiplied by factors of 256 from earlier, you can see how 65 gets multiplied by 256*256*256, or 16777216.
Now, what's this have to do with even rows? Well, we can detect a range from at least [0, 0, X, Y] to at most [255, 255, X, Y] (where X and Y are the first and second characters of the string) because the first two bytes (characters) are the least significant. It's like if you have a 4-digit number that starts with "63," you'll know it's between 6300 and 6399. Using this method, we don't need to care what values actually come before XY, and we simply check between (X * 65536) + (Y * 16777216) and 65535 + (X * 65536) + (Y * 16777216).
If you understood the above explanation, I applaud you for working through my attempt at describing the wonderful convenience of counting in hexadecimal. If you didn't understand it, just assume it's magic and you'll be fine (maybe even better off).
Section 7.3: The Null CharacterIf you're ready for some more punishment, I have good news. StarCraft does not reset the text at addresses for Display Text, but rather just overwrites what it needs to. Say, for example, someone typed something like this:
Roy: Seriously, text detection is the worst thing ever.
Now say, 11 chat lines later, someone named Marth says "Ugh." in chat. Here's what StarCraft will do:
Marth: Ugh.�ly, text detection is the worst thing ever.
See what happened? StarCraft wrote over the beginning of the row, but left the rest of it there! You may notice a strange symbol after Marth's message before Roy's old message appears. This represents the null character; all chat lines in SC terminate with a null character, which has a value of 0. If SC completely wiped the row with null characters, Roy's old text wouldn't exist, and we wouldn't have any concern about it. However, this is not the case, and issues immediately arise. (P.S.: text detection
is "Ughly"
)
Now you have a problem. If you were checking a one or two letter phrase, you have to check a range of values, because some leftover text from another chat line may change the exact value. Or, as an easier alternative, you can just create padding at the end of each Display Text (i.e. putting a few spaces at the end of it), and then just calculate the end of your phrase with spare spaces to fit the last group into an even size of 4 characters. For example, if you were checking for the phrase "Hello," it splits into two groups: "Hell" and "o"; adding three spaces will make the last group turn into "o " to bring it to an even size of 4 characters. Now, there is no possible way this will have a variance in value, regardless of leftover text from older chat.
Section 7.4: Detecting Player ChatIf you still haven't lost hope on making a player chat trigger system, you are either very brave or very stupid; regardless, this segment should be the final blow.
Say you want to be picky, for simplicity's sake: you want a map that
only checks for a command given by you. For example, I want an extra life when I type "1up" into chat. Well, the first thing I have to do is detect my name (Roy), followed by the characters in between my name and my text, and finally my text (1up). But what are the characters in between? It looks like a colon and a space, which it is, but there is another key element hidden in there: a color character.
So, what is the color character? It depends. For the person who typed it, it is green. For the other players in the game, it is blue. You know what this means? You actually need 22 triggers for each phrase you're trying to detect! I shit you not. You need 11 triggers for you, the player typing the message, because your text shows up as green to you. You also need 11 triggers for all the other players, because your text shows up as blue for them. So, for the person typing the text, there needs to be a trigger reading:
Roy: 1up
And for the other players, there needs to be a trigger reading:
Roy: 1up
(Don't let your eyes fool you; these are not the same phrase)
The first trigger phrase has the character , which in SCMDraft, equates to <07>, or green.
The second trigger phrase has the character , which in SCMDraft, equates to <02>, or cyan.
(Note: EUD Text To Value doesn't support entering these characters; you can use
Farty's Calculator for these calculations, however.)
This is the procedure you would use for making an EUD for a specific username (don't forget about padding!). Now what if you just want to check if
anyone types "1up"? Well, this brings us down another level of Dante's Inferno. Please refer back to
yoonkwun's program for general info on this feat. As this is intended to be a beginner's guide, I am not going to go into this advanced subject.
Section 7 QuizTrue or False: In order to perfectly detect text from a one-line Display Text Message, 11 triggers are required.
Answer
True. You need to check for each of the 11 addresses (1 address per in-game line).
I have the string "Hello World!" and I want to detect it on Row 5 (which starts at EUD 186927). I have the values 1819043144 for "Hell," 1867980911 for "o Wo," and 560229490 for "rld!." What will my conditions look like?
Answer
You start with EUD value 186927 and add one to it for every following group of characters:
Memory at Death Table + 186927 is exactly 1819043144
Memory at Death Table + 186928 is exactly 1867980911
Memory at Death Table + 186929 is exactly 560229490
True or False: Padding is when you add spaces to the end of the display text to prevent the EUD from not working when it should.
Answer
True. Adding spaces will resolve the issue of leftover text causing your exact values to be incorrect.
Post has been edited 15 time(s), last time on Apr 26 2014, 5:32 pm by Roy.