MM Graphics Library - LeonardoTheMutant/SRB2-Murder-Mystery GitHub Wiki
This library includes custom functions for string rendering. They allow to draw strings with characters ranged from 0x80 to 0xFF (128 - 255 in decimal)(also known as Extended ASCII characters) on HUD. In vanilla SRB2 this range of characters is used by text coloring and transparency symbols.
To explain the "Extended ASCII" thing we have to get deep into the long and boring details...
Let's take this LUA-formatted string for an example:
"\xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD1\x96\xD1\x82 \xD1\x81\xD0\xB2\xD1\x96\xD1\x82\xD0\xB5!"
Standard HUD rendering functions in BLUA (this is what Lua 5.1 modification in SRB2 is officially called) will display this string as a mess because all of these characters marked by hexadecimal values are setting the color and the transparency of the text. If you take the Windows1251 encoding as a base and try to manually decode this string you will get something like this:
"Привіт світе!"
which means "Hello world!" in Ukrainian.
Let's take a look at this string now:
"Witaj \x9Cwiecie!"
This is supposed to be a Polish text saying "Hello world!". Windows1251 will not help here because it's designed for Cyrillic. Polish is a Latin-based language and like European languages, it has its own native symbols. Those symbols can be found in the Windows1250 code page. If you decode this string with that code page you will now get
Witaj świecie!
which looks readable now.
Now lets get into the "under the hood" details and how you can print such strings in SRB2 yourself with this library
Functions like V_DrawStrASCII()
, V_DrawStrASCII_Center()
and V_DrawStrASCII_Right()
are designed to print strings with Extended ASCII symbols. Still, this is not enough to actually print strings like these because you're missing the font for the characters (more specifically, font graphics/patches). SRB2.pk3
contains graphics for the printable Standard ASCII characters only (0x19-0x7E range, 25-126 in decimal). You need the graphics for every single extended character you are going to use.
Let's imagine that we are making a Greek translation of Murder Mystery. We use Windows1253 as our base. We make the font graphics and name them like "1253C###"
(###
is a decimal number that corresponds to a character in that code page). Our prefix for Greek code page is "1253C"
because we named files like that. Now we need to tell our Rendering Functions to use these files as graphics for Greek letters. The 4th argument in the V_DrawStrASCII()
function specifies the character set prefix to use. In case of the Language Files for the LTM's Murder Mystery we simply need to include the "CHARSET"
key to the LUA table and set its value to the Character Set prefix we got. The final result should look something like this:
MM.AddLang("GR", {
["VERSION"] = "1.0", --don't forget about the compatibility!
["AUTHOR"] = "Sonic",
["NONASCII"] = true, --little outdated name but we set this to show that this language is not Latin-based
["CHARSET"] = "1253C", --this is the font graphic file prefix that we needed!
["MM"] = "Murder Mystery in Greek",
...
})
And now when we kindly ask the V_DrawStrASCII()
function (or the game type) to print the Delta character (Δ, 0xC4
in hex, 192 in decimal) the function will try to load the file by the name of "1253C192"
and draw this graphic on the screen. Wow, it works now! Amazing isn't it?
In theory, you can create your character encoding standard that no one will understand. The possibilities are endless.
Things get a little bit complicated when it comes to the text coloring since characters like 0x80
or 0x85
are now reserved for your character encoding. V_DrawStrASCII()
, like v.draw()
, supports up to 16 text colors, but as color codes it uses characters ranged from 0x10
to 0x1F
. Note that translucency is not supported in MM Strings (there are no free character ranges left to implement this). This is one of the cons of the MM String Renderers.
Let's say you want to use this cool string format with colors but you don't want to rewrite your 50+ Kilobytes of text to convert every color code manually. V_ConvertStringColor()
exists just for this purpose - it converts SRB2 color codes in your string to MM format and as a return value it returns your modified string. You can do vise-versa with the V_ConvertStringColor2()
function which converts MM color codes to vanilla SRB2 ones. Just remember to use MM strings with V_DrawStrASCII()
and regular SRB2 strings with v.drawString()
because they're not compatible with each other.
List of Text Color codes for V_DrawStrASCII()
function. Very similar to the vanilla SRB2 color codes
Decimal | Hexadecimal | Color | Example |
---|---|---|---|
\16 | \x10 | White/Reset | |
\17 | \x11 | Magenta[1] | |
\18 | \x12 | Yellow | |
\19 | \x13 | Green | |
\20 | \x14 | Blue | |
\21 | \x15 | Red | |
\22 | \x16 | Gray | |
\23 | \x17 | Orange | |
\24 | \x18 | Cyan | |
\25 | \x19 | Purple | |
\26 | \x1A | Aqua | |
\27 | \x1B | Peridot | |
\28 | \x1C | Azure | |
\29 | \x1D | Brown | |
\30 | \x1E | Pink | |
\31 | \x1F | Black/Inverted |
[1]: The text renderer in the MMHELP
in-game command does not interpret this code correctly and does not color the text
List of Extended ASCII tables and the available characters which are included in MM. You can also see the characters directly in the game by using the MMCHARSET [charset_prefix]
console command (available only in developer builds of the add-on)
Based on Windows1250 code page. Central and Eastern European Latin-based languages such as Polish, Czech, Slovak, Hungarian, German
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
8 | Š | Ś | Ť | Ž | Ź | |||||||||||
9 | š | ś | ť | ž | ź | |||||||||||
A | Ł | Ą | Ş | Ż | ||||||||||||
B | ł | ą | ş | Ľ | ľ | ż | ||||||||||
C | Ŕ | Ä | Ć | Ç | Č | É | Ę | Ë | Ě | Í | Î | Ď | ||||
D | Ń | Ň | Ó | Ô | Ő | Ö | Ř | Ú | Ű | Ü | Ý | ß | ||||
E | ŕ | á | â | ă | ä | ć | ç | č | é | ę | ë | ě | í | î | ď | |
F | ń | ň | ó | ô | ő | ö | ř | ú | ű | ü | ý |
Based on Windows1251 codepage. Cyrillic languages such as Russian, Ukrainian, Belarussian, Bulgarian, Macedonian
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
8 | ||||||||||||||||
9 | ||||||||||||||||
A | Ґ | Ё | Є | Ї | ||||||||||||
B | І | і | ґ | ё | є | ї | ||||||||||
C | А | Б | В | Г | Д | Е | Ж | З | И | Й | К | Л | М | Н | О | П |
D | Р | С | Т | У | Ф | Х | Ц | Ч | Ш | Щ | Ъ | Ы | Ь | Э | Ю | Я |
E | а | б | в | г | д | е | ж | з | и | й | к | л | м | н | о | п |
F | р | с | т | у | ф | х | ц | ч | ш | щ | ъ | ы | ь | э | ю | я |
Based on Windows1254 codepage. Similar to Windows1252 but also supports Turkish letters
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
8 | Š | |||||||||||||||
9 | š | Ÿ | ||||||||||||||
A | ||||||||||||||||
B | ||||||||||||||||
C | À | Á | Â | Ã | Ä | Ç | È | É | Ê | Ë | Ì | Í | Î | Ï | ||
D | Ğ | Ń | Ň | Ó | Ô | Ő | Ö | Ù | Ú | Û | Ü | İ | Ş | ß | ||
E | à | á | â | ã | ä | ç | è | é | ê | ë | ì | í | î | ï | ||
F | ğ | ñ | ò | ó | ô | ő | ö | ù | ú | û | ü | ı | ş | ÿ |
As an alternative to the regular Patch files, MM HUD Library provides a way to draw bitmap graphics provided by the Text. This is developed primarily for the Language Files as these will most likely not be packed in a .pk3
archive (to additionally include all required graphics).
You must be very familliar how the TIME
HUD label in vanilla SRB2 looks (if not you can open the game right now and see it)(this label can also be found in srb2.pk3
as STTTIME
lump). All you need to know about the Text Patch is that it is a bitmap representation of an image where every charater is a pointer to SRB2 palette's color. This is what STTTIME
patch looks like as Text Patch:
local BMP_TIME_INFO = { --offset values
xoff = 0, --X
yoff = 0 --Y
}
local BMP_TIME = { --actuall Patch data
"IIIIII\x1FFFII\x1FFF\xFFII\x1FFF\xFFFF\xFFII\x1FFFIIIIII\x1F",
"IIIIII\x1FFFII\x1FFF\xFFII\x1FFF\xFFFF\xFFII\x1FFFIIIIII\x1F",
"\xFFFFII\x1F1F\x1FFFII\x1FFF\xFFIII\xFFFF\xFFIII\x1FFFII\x1F1F\x1F1F\x1F",
"\xFFFFII\x1FFF\xFFFFII\x1FFF\xFFIIII\xFFIIII\x1FFFII\x1F",
"\xFFFFII\x1FFF\xFFFFII\x1FFF\xFFIIIIIIIII\x1FFFIIIII\x1F",
"\xFFII\x1FFF\xFFFFII\x1FFF\xFFII\x1FIII\x1FII\x1FFFIIIII\x1F",
"\xFFII\x1FFF\xFFFFII\x1FFF\xFFII\x1F1FI\x1FFFII\x1FFFII\x1F1F\x1F1F",
"\xFFII\x1FFF\xFFFFII\x1FFF\xFFII\x1FFF\x1FFF\xFFII\x1FFFII\x1F",
"\xFFII\x1FFF\xFFFFII\x1FFF\xFFII\x1FFF\xFFFF\xFFII\x1FFFIIIIII\x1F",
"\xFFII\x1FFF\xFFFFII\x1FFF\xFFII\x1FFF\xFFFF\xFFII\x1FFFIIIIII\x1F",
"\xFF1F\x1F1F\xFFFF\xFF1F\x1F1F\xFFFF\x1F1F\x1FFF\xFFFF\xFF1F\x1F1F\xFF1F\x1F1F\x1F1F\x1F1F"
}
If you have eyes like Neo from the Matrix you can already see the image. But don't worry if you don't because this is actually a simplified/optimized version of the Text Patch where some of the Hexadecimal values are converted into ASCII Characters (the \x49
value here got converted into I
) and Hexadecimal values are 16-bit (\x1F
is 8-bit value, \x1F1F
is 16-bit but is read as two characters). \xFF
is a translucent pixel, \x49
(or I
) points to color Yellow in SRB2's palette, \x1F
is color black.
Getting the height of such Patch is as simple as getting the length of the table where it is stored. To get the width you can use V_TextPatchWidth(string[] textpatch)
function
print(#BMP_TIME) --height, prints 11
print(V_TextPatchWidth(BMP_TIME)) --width, prints 31
To make a color swap you will need to use the V_TextPatch_SwapColor(string[] textpatch, int color1, int color2)
function. Last two arguments are specifying the source and the target colors (color1
will be swapped with color2
). The function returns a Text Patch with swapped color.
Name | Return value | Description |
---|---|---|
V_LoadPatch(drawer v, string patchName) |
nil | Load the Patch into the memory. The cached patch can be accesed with MM.graphics[patchName] . |
V_UnloadPatch(string patchName) |
nil | Unload the Patch from the memory. This function simpy changes the value of MM.graphics[patchName] to nil . |
V_LoadCharset(drawer v, string name) |
nil | Load the Character Set into the part of memory dedicated to storing Character Set patch graphics (MM.graphics.charset ). name is the 5-sign (no more, no less) patch prefix. This function will try to load all character patches named as XXXXX### where XXXXX is name and ### is a 3-digit number from 0 to 255. |
V_DrawStrASCII(drawer v, int x, int y, string charset, string str, [int videoflags, [bool small?]]) |
nil | An alternative to v.drawString() . This function supports text rendering in different character encodings. charset specifies the character set to use (the character set has to be preloaded into the memory with V_loadCharset() ). str is the string to render. If small? is set to true , it will draw the string in half of the normal sizeNote: Each character from the Extended ASCII range (0x80-0xFF) should be typed as an escape code of the character ( \x## format for Hexadecimal) in str string. |
V_DrawStrASCII_Center(drawer v, int x, int y, string charset, string str, [int videoflags, [bool small?]]) |
nil | Alternative to v.drawString() with the "center" alignment flag. Uses V_DrawStrASCII() to render the string relatively centered. The x coordinate is the center. |
V_DrawStrASCII_Right(drawer v, int x, int y, string charset, string str, [int videoflags, [bool small?]]) |
nil | Alternative to v.drawString() with the "right" alignment flag. Uses V_DrawStrASCII() to render the string right-aligned. The x coordinate is the right edge of the string. |
V_StrWidthASCII(string str, [int videoflags, [bool small?]]) |
int | Alternative to the v.stringWidth() but for V_DrawStrASCII() drawing function. Get the width of the string in pixels, arguments like videoflags or scale are also accounted in the final size. |
V_ConvertStringColor(string str) |
string | Converts the SRB2 text coloring symbols in str to MM format. Used for V_DrawStrASCII() function as it uses a different text coloring format. |
V_ConvertStringColor2(string str) |
string | Converts the MM text coloring symbols in str to SRB2 format. Used for v.drawString() to render the MM-formatted strings. |
V_DrawTextPatch(drawer v, int x, int y, int xoff, int yoff, string[]* data, int flags) |
nil | Draws a patch using the data in a text form at (x, y) coordinates (with the offset set by xoff and yoff ). data is a table of strings, each table element represents a row and each symbol in data[row] corresponds to the color value from the SRB2's palette. |
V_TextPatchWidth(string[] data) |
int | Returns the width of the Text Patch in pixels. |
V_TextPatch_SwapColor(string[] data, int sourceColor, int targetColor) |
string[] | Swaps each sourceColor pixel to targetColor in a Patch. This function returns the Patch with the swapped colors. |
V_GetTextPatchOffsetX(string language, string patchname) |
int | Get the Patch's x offset coordinate from MM.text[language][patchname.."_INFO"] field (if exists). |
V_GetTextPatchOffsetY(string language, string patchname) |
int | Get the Patch's y offset coordinate from MM.text[language][patchname.."_INFO"] field (if exists). |
V_ScrollTextPatch_Vertical(string[] patch, int offset, boolean side) |
string[] | Scroll the Text Patch vertically by offset pixels. By default, the patch is scrolled from right to left, but if side is set to true , the patch is scrolled from left to right istead. |