+  RHDN Forum Archive
|-+  Romhacking
| |-+  ROM Hacking Discussion
| | |-+  LUA
Pages: [1] 2
Author Topic: LUA  (Read 2 times)
Narf
Guest
LUA
« on: June 24, 2009, 07:22:23 am »

Has anyone gotten into LUA scripting with emulators?

There's some rather impressive videos getting around on youtube:

Super Mario Bros. 1 Drag & Drop (FCEUX 2.1 + Lua)
Lua experiments - Super Mario Bros. 3 Rainbow Riding
Lua experiments - Mega Man 2 LASER EYES

It seems that TASVideos.org (Tool Assisted Speedruns) are the main one pushing forward in this area.
If you take a look around their forums - in the emulators section - you can find lots of modified emulators that have recently had LUA Scripting implemented.

I've been playing around with a modified Snes9X Version (snes9x143-rerecording) and pretty quickly got a script running with Lufia 2 that displays various statistics while in the Ancient Cave (Screenshots below).

The API specific to the emulator is located here: Snes9X Lua API
Theres also some sample scripts here: Snes9x Lua Sample Scripts
It's also rather easier to learn (read copy) as the scripts are saved in plain text.

I've highlighted the changes with a red rectangle.

About to enter the Ancient Cave


Exploring the Ancient Cave


Battle!


I'll attach the script if anyone wants to try it out, but I wouldn't advise using it as learning tool as I was basically stitching pieces of code together.  Tongue
Jigglysaint
Guest
« Reply #1 on: June 24, 2009, 01:00:20 pm »

Wow, with scripting like this, you could easily change the way old games are played.  I just checked out the MM2 script, but then I just changed it from firing when the Select button is pressed, to the B button.  With the right additions, it could totally replace the actual weapons.  Imagine inserting your own custom graphics to be used as weapons.  Imagine adding weapon charging to Megaman 3 or 4 with no reguard for space.  Yes, it's not for the console enthusists, but it would still be awesome.
Aprocalypse
Guest
« Reply #2 on: June 25, 2009, 07:53:27 am »

Interestingly timed, actually, I found out about this recently, and wrote a script that prints the hp of monsters in a battle in Chrono Trigger. All it's missing is finding a memory address that lets me know if you're in battle or not so it will only come up in battle. Sad It's very iiinteresting though!

I also love both the idea of that script, and the fact it's for Lufia 2. Awesome. Cheesy

(also, is it actually attached, or am I just blind?)
« Last Edit: June 25, 2009, 08:05:13 am by Aprocalypse »
Lindblum
Guest
« Reply #3 on: June 25, 2009, 08:42:36 am »

LUA is one of those things I'd go geeky over if I ever had time for it.  I'd probably write an AI script that plays the game all by itself, which would be FLIPPIN' SWEET!  It would be like the time I wrote a Java app that controls the mouse so that it could play minesweeper automatically (which I want to do for online games where you can win money).  First I'd try making an auto-playing Tetris script, then a Dragon Warrior one (to manage battles and gain levels (even though I could just hack them)).  Bomberman might be cool too.  Platformers would be the ultimate challenge. 
Narf
Guest
« Reply #4 on: June 25, 2009, 09:23:56 am »

Lindblum: I've found a bit of discussion regarding things like that, but the ones I've seen (One for Super Punch-out and one for a gambling game - I think Vegas Stakes) utilise save states to continually reload if the desired outcome is not achieved for an action (i.e the memory value holding money reduces).

I can't seem to attach files, so copy and paste the following into a text file and save it with a .lua extension.
This script is for the (U) version. Haven't tested it with the (E) version.

Code:
local location = """";
local enemycount = 0;
local enemy = {}
--general functions - Have to include these due to the lack of native bit handling in lua (and my lack of skills...)

local hex2bin = {
["0"] = "0000",
["1"] = "0001",
["2"] = "0010",
["3"] = "0011",
["4"] = "0100",
["5"] = "0101",
["6"] = "0110",
["7"] = "0111",
["8"] = "1000",
["9"] = "1001",
["a"] = "1010",
        ["b"] = "1011",
        ["c"] = "1100",
        ["d"] = "1101",
        ["e"] = "1110",
        ["f"] = "1111"
}

function Hex2Bin(s)
-- s -> hexadecimal string
local ret = ""
local i = 0
for i in string.gfind(s, ".") do
i = string.lower(i)
ret = ret..hex2bin[i]
end
return ret
end

function Dec2Bin(s, num)
-- s -> Base10 string
-- num  -> string length to extend to
local n
if (num == nil) then
n = 0
else
n = num
end
s = string.format("%x", s)
s = Hex2Bin(s)

while string.len(s) < n do
s = "0"..s
end
return s
end


while true do

--Extract current location from RAM
location = string.char(unpack(memory.readbyterange(0x7eff00,15)))
enemycount = 0

--Should have a check for when changing floors
--That way can only check and set the level name once

--Make sure in Ancient Cave
if  location ~= "Ancient Dungeon" then
--return false
gui.text(200,10,"Not In AC")
else
--Lets check if we are in battle - This value seems to only hold 01 while in battle
if memory.readbyte(0x7e02e0) == 01 then
    --We are in battle...lets display the enemy info on screen
    --Not quite sure on the max number of enemies...6 will do for now!
    -- enemy = {}
    for id = 0, 5 do
local i = id + 1
enemy[i] = {
--name = memory.readbyterange(0x7e161b + 190*i,13),
name = string.char(unpack(memory.readbyterange(0x7e155d + 190*i,13))),
hp = memory.readword(0x7e156e + 190*i),
}
enemy[i].id = id
enemy[i].active = (enemy[i].hp ~= 0)
    end

    --Display enemy info on screen
    local ypos = 2
    for i = 1, 6 do
if enemy[i].active then
    --gui.text(165,ypos,"Enemy("..enemy[i].id .."):")
    gui.text(175,ypos,"Enemy:")
gui.text(200,ypos,enemy[i].name)
gui.text(175,ypos+10,"HP:")
gui.text(187,ypos+10,enemy[i].hp)
ypos = ypos + 20
end
    end
else
    --really shouldnt be setting this twice! need to grab the whole string earlier
    -- and then compare first 15...
    location= string.char(unpack(memory.readbyterange(0x7eff00,19)))
    gui.text(138,2,"Location:")
    gui.text(175,2,location)

    gui.text(175,12,"Active Enemies:")

    --enemycount = 0
    --Check for enemies -- Not really sure how many to cap it at...19 is the most I've seen...
    for id = 0,20 do
local i = id + 1
--Getting enemy count by checking this section of memory. When an enemy is moving it has these values here
--I've found a number of enemies variable at 7FE731, but it doesnt get decremented as you kill them....
if memory.readbyte(0x7e062A + id) == 0 or memory.readbyte(0x7e062A + id) == 80 or memory.readbyte(0x7e062A + id) == 40 then
    enemycount = enemycount + 1
end
    end
    gui.text(239,12,enemycount)

    --Get the total number of chests for the level
    local totalchests = memory.readbyte(0x7FE734)

    --Seems to set a bit for each chest you open on 0x7FE733
    -- eg. Theres 5 chests on level. You open 1 chest - 7FE7ee may now hold 1 or 2,4,8,16 etc. (eg you open chest 2 - this value now equals 2 (00010) in binary    

    --convert byte to a binary array
    local dec = Dec2Bin(memory.readbyte(0x7FE733),totalchests)
    local chestsopened = 0
    for index = 1, string.len(dec) do
s = string.sub(dec,index,index)
if s == "1" then
    chestsopened = chestsopened + 1
end
    end

    local chestsremaining = totalchests - chestsopened
    gui.text(175,22,"Unopened Chests:")
            gui.text(239,22,chestsremaining)

end
end

snes9x.frameadvance()

end
Isao Kronos
Guest
« Reply #5 on: June 25, 2009, 01:16:00 pm »

Is it sad that every time I hear something about LUA, I immediately think about Monkey Island?
Kitsune Sniper
Guest
« Reply #6 on: June 25, 2009, 01:31:30 pm »

Quote from: Aprocalypse on June 25, 2009, 07:53:27 am
Interestingly timed, actually, I found out about this recently, and wrote a script that prints the hp of monsters in a battle in Chrono Trigger. All it's missing is finding a memory address that lets me know if you're in battle or not so it will only come up in battle. Sad It's very iiinteresting though!
Why not tie this to the music loaded in memory? If a battle or boss theme is playing then show the HP. If not, then nothing shows.
snesmaster40
Guest
« Reply #7 on: June 25, 2009, 06:29:55 pm »

Amazing stuff. I was really entertained with the rainbow riding video even though I hate Kirby Rainbow Canvas thingy
Aprocalypse
Guest
« Reply #8 on: June 25, 2009, 07:38:26 pm »

Quote from: Kitsune Sniper on June 25, 2009, 01:31:30 pm
Quote from: Aprocalypse on June 25, 2009, 07:53:27 am
Interestingly timed, actually, I found out about this recently, and wrote a script that prints the hp of monsters in a battle in Chrono Trigger. All it's missing is finding a memory address that lets me know if you're in battle or not so it will only come up in battle. Sad It's very iiinteresting though!
Why not tie this to the music loaded in memory? If a battle or boss theme is playing then show the HP. If not, then nothing shows.

I managed to find a variable that changes from 0 to 1 in memory when in a fight, which worked for this anyways. Apparently there's about 20 different memory locations that seem to do that, which was odd, but it appears to work in any case. Cheesy

Thanks for the concept help on that though, that would be a really good way of doing it.
Jigglysaint
Guest
« Reply #9 on: June 25, 2009, 10:39:07 pm »

Quote from: Kitsune Sniper on June 25, 2009, 01:31:30 pm
Quote from: Aprocalypse on June 25, 2009, 07:53:27 am
Interestingly timed, actually, I found out about this recently, and wrote a script that prints the hp of monsters in a battle in Chrono Trigger. All it's missing is finding a memory address that lets me know if you're in battle or not so it will only come up in battle. Sad It's very iiinteresting though!
Why not tie this to the music loaded in memory? If a battle or boss theme is playing then show the HP. If not, then nothing shows.

There are parts in the game, like the Undersea Palace, where music doesn't change to the battle music.
There should be a spot in ram that determines if you are in battle or not.  There has to be, or else you could move while engaging in battle.
KingMike
Guest
« Reply #10 on: June 26, 2009, 03:47:58 pm »

I don't remember any undersea fights besides a single boss fight.
Tomato
Guest
« Reply #11 on: June 26, 2009, 04:21:06 pm »

This earthbound one is among the sample scripts, but it's not commented, so I can't tell what it does exactly. Any ideas?

http://dehacked.2y.net/snes9x-lua/earthbound.lua
GenoBlast
Guest
« Reply #12 on: June 26, 2009, 05:15:42 pm »

Quote from: Tomato on June 26, 2009, 04:21:06 pm
This earthbound one is among the sample scripts, but it's not commented, so I can't tell what it does exactly. Any ideas?
I haven't got a clue what it does, but here's a breakdown:

Once every frame:
  Set the variables alloc and free to 0.
  Read every fifth memory value from $7E467E to $7E4A02:
     If the value is 255, increment free, otherwise increment alloc.
  Display the values of alloc and free.

If you know what the game stores at those memory addresses, maybe you can figure it out.
Lleu
Guest
« Reply #13 on: June 30, 2009, 02:43:56 am »

...

I might be going out on a limb, here, but ... maybe it's checking a range of memory for free space?
GenoBlast
Guest
« Reply #14 on: June 30, 2009, 02:52:36 am »

Quote from: Lleu on June 30, 2009, 02:43:56 am
I might be going out on a limb, here, but ... maybe it's checking a range of memory for free space?
You're probably right. I still don't know what the point is, though. Unless there's something specific that it's monitoring the existence of. >__>
Pages: [1] 2  


Powered by SMF 1.1.4 | SMF © 2006-2007, Simple Machines LLC