Back to Page
Revision 10 (current)
Edited by feos on 8/4/2022 1:54 PM
This page describes how to display text, as well as display memory addresses in the form of text.
!! Displaying something
Use the command {{gui.text(int x, int y, string msg[[, type color]])}}, which displays {{msg}} at (x,y) with color {{color}}. Color, which is optional, is given as string "0xrrggbbaa" (hex) or as a string name (e.g. "red").
Example, a very simple program:
%%SRC_EMBED lua
local str="test"
while true do
gui.text(0,0, str)
emu.frameadvance()
end
%%END_EMBED
{{"test"}} can also be written directly in {{gui.text}} as {{gui.text(0,0, "test")}}.
There are other drawing functions such as {{gui.pixel(int x, int y[[, type color]])}} and {{gui.line(int x1, int y1, int x2, int y2[[, type color]])}} but {{gui.text}} is the most informative.
Since {{gui}} functions only affect the next frame, these commands have to be called every frame to keep showing them.
Depending on the emulator, there may be other commands such as {{emu.message(string msg)}} which display text. The difference is that {{gui.text}} is a drawing function whereas emulator message functions display messages that are not considered to be drawn on the game display.
!! Memory Observation and Calculation.
Most emulators have a RAM Watch or similar tool to view any desired memory address while making the TAS. However, it is sometimes desirable to draw information on the screen. Drawing on the screen does not affect the playback of the movie.
First, [memory search] is required to find the addresses.
Once you have the addresses, it is a simple matter to display what it contains. For example, let's suppose the HP (hit points) of a boss is contained in the address 0x100 (0x100 means hexadecimal 100). The following script displays the address:
%%SRC_EMBED lua
local HPAddr=0x100
local HP
while true do
HP=memory.readword(HPAddr)
gui.text(0,0,"HP: " .. HP)
emu.frameadvance()
end
%%END_EMBED
The function {{readbyte}} is for one-byte values, {{readword}} for two bytes, and {{readdword}} for four bytes.
You can even do calculations before outputting. For example, in Super Mario Bros., 0x0086 is Mario's X pixel position, and 0x400 is Mario's X subpixel position. Thus, we can calculate position as (addr(0x0086) + addr(0x400)/256).
%%SRC_EMBED lua
local Pixel = 0x0086
local Subpixel = 0x0400
local Position%%%
while true do
gui.text(20,20,"X Pixel" .. memory.readbyte(Pixel) )
gui.text(22,30,"X Subpixel" .. memory.readbyte(Subpixel) )
Position = memory.readbyte(Pixel) + (memory.readbyte(Subpixel)/256)
gui.text(24,40,"X Position" .. Position)
emu.frameadvance()
end
%%END_EMBED
Text is not the only option. It is possible to mark down positions of enemies on the screen. For example, suppose there is an invisible enemy that you can't ignore. After testing, you find that the enemy's X position is at 0x1000, and its Y position is at 0x1004. You also find the screen's X position at 0x2008 and Y position at 0x200C. The enemy's position on screen is given by ( addr(0x1000)-addr(0x2008) , addr(0x1004)-addr(0x200C) ), which indicates the top-left corner of the enemy's hitbox. Suppose the enemy is 15 pixels high and 7 pixels wide. Then the following draws an approximate hitbox:
%%SRC_EMBED lua
local eX, eY, sX, sY
local hbX, hbY
while true do
eX=memory.readdword(0x1000)
eY=memory.readdword(0x1004)
sX=memory.readdword(0x2008)
sY=memory.readdword(0x200C)
hbX=eX-sX
hbY=eY-sY
gui.drawbox(hbX-2, hbY-2, hbX+9, hbY+17)
emu.frameadvance()
end
%%END_EMBED
Here is yet another example, which functions as a memory viewer for a range of values:
%%SRC_EMBED lua
local s = memory.readbyterange(0xff00, 20)
local a, b
while true do
for k,v in ipairs(s) do
a= math.floor((k-1)/10)
b=(k-1)%10
gui.text(15*b,10*a,v)
end
emu.frameadvance()
end
%%END_EMBED
!!List of drawing functions
In general, these functions don't modify the game in any way. They simply paint over the display with whatever you wanted.
! gui.text(x, y, string, color1, color2)
Paints text onto the screen. Typically fixed-width, most emulators use a width of 4 pixels, while DeSmuME uses a width of 6.
color1 applies to the main text color, and it is white if you omit color1. color2 applies to the bordering pixels around the main part of the text, and is black (or dark blue in FCEUX) by default if you omit color2.
FCEUX uses a variable-width text, so the function is modified to return the X position where it stops painting the text. Only important if you need to know the size of the block of text you just threw up there, otherwise just ignore the returned value.
Useful for displaying calculated values that RAM Watch alone can't help you with, or providing various bits of other information in text form.
! gui.pixel(x, y, color)
Paints a single pixel. Usually, a single pixel tends to go unnoticed, but there are a few uses for painting individual pixels rather than full lines or boxes.
Good for some sort of minimap script or any form of display requiring fine detail.
! gui.line(x1, y1, x2, y2, color)
Paints a straight line starting from one coordinate and ending on the other coordinate. The line is always one pixel wide.
Generally good for showing the angle where things are moving (if you can calculate them in lua!), as well as turning a number into a visible bar for an intuitive feel rather than a concrete feel.
! gui.box(x1, y1, x2, y2, FillColor, BorderColor)
Paints a filled-in box, using one color for the border and another color to fill. Omitting BorderColor means the FillColor is used for the border, and a more translucent version of FillColor is used for the fill.
Useful for when you've found the hitbox data in RAM, and want to actually paint them. Also helpful in making other bits of drawing more visible when you first fill a section of the screen with a translucent black, for example.
! x y coordinates
You do not need to use static, unchanging values for your x and y. However, {{gui.text(5,12,"hello world")}} is certainly very easy to use. But there are ways to move the pieces of display around as needed.
You can fetch some values from RAM (say, the enemy's x,y position), and then use them as the x,y coordinate in your drawing function so that, for example, the text related to the enemy follows them where they go.
! Colors
There are several representations of colors to use:
* Number - 0xrrggbbaa format%%%Most emulators have the __80Red__ glitch that makes drawing using a red value of 80 or higher completely invisible.
* String - "#rrggbb" or "#rrggbbaa"%%%There are also a number of simple colors as well, using strings like "red" or "green".
* Table - {r = 0xrr, g = 0xgg, b = 0xbb, a = 0xaa} or {0xrr,0xgg,0xbb,0xaa}%%%If you prefer, you can use straight decimal numbers, which range from 0 to 255 instead of the hex values that range from 0x00 to 0xFF. The other formats don't easily allow use of decimal numbers like tables.
__80Red__ glitch: When using a number as your color, any value above 0x7FFFFFFF simply fails. No drawing is done, as though the function didn't do anything. However, this only affects the number form -- String and Table are unaffected. You can use negative numbers to reach the brighter reds, but you really need to know what you're doing to get the color you want.
__Note:__ This is how snes9x-based lua implementation works with colors. Newer emulators like lsnes and Bizhawk handle the colors differently, see their docs.
----
''[TODO]: Teach us to draw things without one-frame delay.''%%%
(([user:FatRatKnight]: You realize... That depends on emulator implementation. Snes9x is impossible as it stands as far as memory reading goes (can paint other things immediately, but {{memory}} functions work late regardless of register), but other than that, using {{gui.register}} instead of {{emu.frameadvance}} usually gets rid of the one-frame delay. Shouldn't be too hard to explain, but I'm not in the mood to write that up now.))