You are not logged in.

Read the FAQ and Knowledge Base before posting.
We won't make a 3DS/2DS emulator.



#1 2021-03-30 04:32:33

windwakr
Member
Registered: 2010-06-10
Posts: 20

Metroid Prime Hunters LUA script

I've been sitting on this for a while(years) now and decided to clean it up a bit and post it. This script allows you to play Metroid Prime hunters with a controller with smooth camera movement.
Some small snippets(like subweapon switching) were borrowed from https://forums.desmume.org/viewtopic.php?id=10957 .
This may only work on the Windows version of DeSmuME, I'm not sure.

This requires the US ROM with a SHA1 hash of 90164D1AC127EE5F9815EA4AE7DE798C7B5FC629

Clear any controller input you may have set up in DeSmuME before using.
By default it's set up for an Xbox style controller but the controls are easily customizable.

left stick - move
right stick - aim
left or right trigger - shoot/scan
bumpers - cycle subweapon forward/back
start - start
d-pad up(hold) - scan visor
d-pad down - morphball
d-pad left - presses back arrow on messages
d-pad right - presses forward arrow on messages
B - presses OK on messages
A - jump
X - morphball boost
Y - cycle weapon(beam/missile/subweapon)
select(hold) - virtual stylus

while virtual stylus is active
  right stick - move the virtual stylus
  right bumper - tap the screen
  left bumper - toggle manual touchscreen input

The way aiming works is the center of the screen is continually pressed and the script injects stylus movement into the game's memory. If you need to manually use the touchscreen then use select+left bumper to toggle aiming on or off. That shouldn't be necessary with the existence of the virtual stylus, though.

Use with the U.S. version of the game.

joyid = 0 --change this if your preferred controller isn't being picked up
deadzone = 0.15 --Adjust analog stick deadzones

cooldown=0
weapon=0
subweapon=0
togglestylus=false
toggleaim=false
vcurx=128
vcury=96
key = {}
joy = {}

--Set the controls here
morphkey = "joy.down" --morph ball switch
visorkey = "joy.up" --scan visor switch(hold)
okkey = "joy.2" --ok button in messages
leftkey = "joy.left" --left arrow in messages
rightkey = "joy.right" --right arrow in messages
weaponkey = "joy.4" --weapon cycle
subweaponkey1 = "joy.5" --subweapon cycle back
subweaponkey2 = "joy.6" --subweapon cycle forward
leftstickx = "joy.x" --must be analog stick axis
leftsticky = "joy.y" --must be analog stick axis
rightstickx = "joy.u" --must be analog stick axis
rightsticky = "joy.r" --must be analog stick axis
shootkey = "joy.z" --may be either analog axis or button
boostkey = "joy.3" --morph ball boost
vstyluskey = "joy.7" --virtual stylus(hold)
jumpkey = "joy.1" --jump
startkey = "joy.8" --start


--https://www.lua.org/pil/14.1.html
function getfield (f)
  local v = _G    -- start with the table of globals
  for w in string.gfind(f, "[%w_]+") do
    v = v[w]
  end
  return v
end

while true do
    joysend = {}
    key=input.get()
    joy = controller.get(joyid)
    
    if getfield(vstyluskey) then
        toggleaim = true --this exists to just delay the pressing of the screen when you release the virtual stylus key
        if vcury>0 then
            gui.drawbox(vcurx-1, vcury-1, vcurx+1, vcury+1, "#000000", "#FFFFFF")
        else
            gui.drawbox(vcurx-1, vcury, vcurx+1, vcury+1, "#FFFFFF") --workaround for vcury=0, as it would draw to the top screen
            gui.pixel(vcurx, vcury, "#000000")
        end
        
        if getfield(subweaponkey2) then
            stylus.set{x=vcurx, y=vcury, touch=true}
        end
        if getfield(subweaponkey1) and cooldown==0 then
            togglestylus = not togglestylus
            print('Touchscreen input allowed = ' .. tostring(togglestylus))
            cooldown = 20
        end
        tmp = getfield(rightstickx)
        if tmp and math.abs(tmp)>deadzone then
            vcurx = vcurx + (tmp * 5)
        end
        tmp = getfield(rightsticky)
        if tmp and math.abs(tmp)>deadzone then
            vcury = vcury + (tmp * 5)
        end
        
        if vcurx<0 then vcurx=0 end
        if vcurx>255 then vcurx=255 end
        if vcury<0 then vcury=0 end
        if vcury>191 then vcury=191 end
    else
        if getfield(morphkey) then --Morph
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=231, y=167, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(visorkey) then --Visor
            if cooldown == 0 then
                stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            end
            cooldown = 10
            stylus.set{x=128, y=173, touch=true}
        end
        if getfield(okkey) then --OK (in scans and messages)
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=128, y=142, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(leftkey) then --Left arrow (in scans and messages)
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=71, y=141, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(rightkey) then --Right arrow (in scans and messages)
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=185, y=141, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(weaponkey) and cooldown==0 then --Switch weapon (beam->missile->subweapon->beam)
            cooldown=20
            weapon=(weapon+1)%3
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=85+40*weapon, y=32, touch=true} emu.frameadvance()
            stylus.set{x=85+40*weapon, y=32, touch=true}
        end
        if (getfield(subweaponkey1) or getfield(subweaponkey2)) and cooldown==0 then --Switch subweapon (previous and next)
            cooldown=20
            weapon=2
            if getfield(subweaponkey1) then subweapon=(subweapon-1)%6 end --previous
            if getfield(subweaponkey2) then subweapon=(subweapon+1)%6 end --next
            subX=93+25*subweapon subY=48+25*subweapon
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=232, y=34, touch=true} emu.frameadvance()
            stylus.set{x=232, y=34, touch=true} emu.frameadvance()
            stylus.set{x=subX, y=subY, touch=true} emu.frameadvance()
            stylus.set{x=subX, y=subY, touch=true}
        end
        tmp = getfield(leftstickx)
        if tmp and tmp < -deadzone then
            joysend.left = true
        elseif tmp and tmp > deadzone then
            joysend.right = true
        end
        tmp = getfield(leftsticky)
        if tmp and tmp < -deadzone then
            joysend.up = true
        elseif tmp and tmp > deadzone then
            joysend.down = true
        end
        tmp = getfield(rightstickx)
        if tmp and math.abs(tmp)>deadzone and togglestylus==false then
            memory.writedword(0x020DE526, (tmp * 4))
            toggleaim=false
        end
        tmp = getfield(rightsticky)
        if tmp and math.abs(tmp)>deadzone and togglestylus==false then
            memory.writedword(0x020DE52E, (tmp * 6))
            toggleaim=false
        end
        if getfield(boostkey) then
            joysend.R = true
        end
        
        tmp = getfield(shootkey)
        if tmp then
            if type(tmp) == "number" then
                if math.abs(tmp) > deadzone then
                    joysend.L = true
                end
            elseif type(tmp) == "boolean" then
                joysend.L = true
            end
        end
        
        if getfield(jumpkey) then
            joysend.B = true
        end
        if getfield(startkey) then
            joysend.start = true
        end
    end
    
    if cooldown>0 then
        cooldown=cooldown-1
    else
        ball = memory.readbyte(0x020DA818) == 0x02 --Is this a good way of detecting morph ball status??
        if ball==false and togglestylus==false and toggleaim==false then
            stylus.set{x=128, y=96, touch=true} --Required for aiming
        end
    end
    
    joypad.set(1,joysend)
    emu.frameadvance()
end

video recorded while using this script: https://files.catbox.moe/o6r3r5.webm

Last edited by windwakr (2021-10-14 00:45:27)

Offline

#2 2021-04-02 01:45:01

windwakr
Member
Registered: 2010-06-10
Posts: 20

Re: Metroid Prime Hunters LUA script

Updated the script to include a "virtual stylus" for navigating stuff like your ship and the main menu. When holding select(on an xbox style controller) a virtual stylus will be displayed. It can be moved with the right stick and touch the screen with the right bumper. You can still toggle manual touchscreen input if needed with select + left bumper.

Last edited by windwakr (2021-04-02 01:47:21)

Offline

#3 2021-04-02 23:37:09

Legacy
Member
Registered: 2021-04-02
Posts: 1

Re: Metroid Prime Hunters LUA script

I'm actually new to adjusting scripts in DesMume and I'm a visual learner, so I was wondering if you could post of video of how to do this? (I'm sure you would get a lot of views on youtube as well considering no one has done this.)
I always wanted to play Metroid prime hunters but trying to use my right hand for my laptop's mouse and my left hand for the controller is a horrible gaming experience.

Thank you in advance for your time!

Offline

#4 2021-04-05 19:49:14

windwakr
Member
Registered: 2010-06-10
Posts: 20

Re: Metroid Prime Hunters LUA script

Legacy wrote:

I'm actually new to adjusting scripts in DesMume and I'm a visual learner, so I was wondering if you could post of video of how to do this? (I'm sure you would get a lot of views on youtube as well considering no one has done this.)
I always wanted to play Metroid prime hunters but trying to use my right hand for my laptop's mouse and my left hand for the controller is a horrible gaming experience.

Thank you in advance for your time!

What are you having trouble with?

To run Lua scripts you will need the correct dlls in the folder with your DeSmuME exe. A Lua setup guide copied from here:

Download the Lua DLL that matches your Desmume: - https://sourceforge.net/projects/luabin … s/Dynamic/

lua-5.1.5_Win32_dll14_lib.zip for x86 Desmume
lua-5.1.5_Win64_dll14_lib.zip for x86-64 Desmume

Extract lua5.1.dll from the .zip file to the same folder where your DeSmuME_0.9.11_x86.exe or DeSmuME_0.9.11_x64.exe is

Rename lua5.1.dll to lua51.dll

Copy the script from my post into a text file and rename the extension to ".lua". In DeSmuME once the game is running go to "Tools -> Lua Scripting -> New Lua Script Window...". In the new window that pops up hit browse and select the script you saved and it will start running.


If you want to adjust the controls then use this lua script to identify your controller and its buttons

dpad = {"up", "down", "left", "right"}
sticks = {"x", "y", "z", "r", "u", "v"}

function showControllers()
    gui.box(0, 0, 256, 192, "#808080")
    gui.box(0, -192, 256, 0, "#808080")
    for i=0,15 do
        cont = controller.get(i)
        if type(next(cont)) ~= "nil" then
            gui.text(0, (18*i)-143, i)
            xoffs = 14
            for ii=0,32 do
                col = "#606060"
                tmp = tostring(ii)
                if cont[tmp] ~= nil then
                    if cont[tmp] == true then col = "#FF0000" end
                    gui.text(xoffs, (18*i)-143, tmp, col) --goes off screen if controller has more than ~20 buttons.
                    xoffs = xoffs + string.len(tmp)*6 + 2
                end
            end
            xoffs = 14
            for idx, name in ipairs(dpad) do
                col = "#606060"
                if cont[name] ~= nil then
                    if cont[name] == true then col = "#FF0000" end
                    gui.text(xoffs, (18*i)-143+9, name, col)
                    xoffs = xoffs + string.len(name)*6 + 4
                end
            end
            for idx, name in ipairs(sticks) do
                col = "#606060"
                if cont[name] ~= nil then
                    if math.abs(cont[name]) > 0.25 then col = "#FF0000" end
                    gui.text(xoffs, (18*i)-143+9, name, col)
                    xoffs = xoffs + string.len(name)*6 + 2
                end
            end
        end
    end
end
gui.register(showControllers)

CzzpAOV.png
You should see output similar to this, but different depending on your controller and how many of them you have hooked up.
The white number on the left is the controller id. When you press a button it will turn red to help you identify which button corresponds to which number.
In the main script you can then adjust joyid and the controls with the values you find here.

Last edited by windwakr (2021-04-05 19:55:15)

Offline

#5 2021-04-06 02:26:32

windwakr
Member
Registered: 2010-06-10
Posts: 20

Re: Metroid Prime Hunters LUA script

Also, it looks like input in Lua was broken for the last month in DeSmuME builds, but is now fixed. If you get an error when trying to run one of the scripts then make sure you have an up-to-date build of the emulator.

Last edited by windwakr (2021-04-06 06:06:50)

Offline

#6 2021-10-12 23:29:54

WookV3M0
Member
Registered: 2021-10-12
Posts: 3

Re: Metroid Prime Hunters LUA script

I apologize heavily for resurrecting this post but I'm at my wits end. So far I've gotten everything to work by right analog stick aiming.

I use a PS5 controller and have managed to remap everything properly and even with the virtual stylus the right analog stick works perfectly. It's just when it comes to aiming in the game and looking around it just will not work. I even tried using DS4Windows and Steam to help treat it as an xbox controller but still no luck. I don't have any other country to test this with sadly.

I'm not really smart when it comes to scripting so I don't know what I'm supposed to do to fix this or if there is a fix. Again, everything is working fine with the buttons except that one thing, aiming/looking around. If anyone has an answer that would be appreciated.

Also I apologize if I violated a rule on necroing old posts. I didn't see the rule anywhere but I may have missed it.

Offline

#7 2021-10-14 00:22:54

windwakr
Member
Registered: 2010-06-10
Posts: 20

Re: Metroid Prime Hunters LUA script

What region game are you using? This was only tested on the US version. It has hardcoded memory addresses that it injects values at that probably won't work with other regions.


EDIT: Specifically the first release, not Rev 1. The working ROM has the SHA1 hash 90164D1AC127EE5F9815EA4AE7DE798C7B5FC629

Last edited by windwakr (2021-10-14 00:44:30)

Offline

#8 2021-10-14 15:03:31

WookV3M0
Member
Registered: 2021-10-12
Posts: 3

Re: Metroid Prime Hunters LUA script

Thank you for the response! Okay, so it is the US region but after struggling to figure it out it's apparently Rev 1. I assume that's the reason.

Since I know nothing about scripting or LUA there's not much I can do in this regard to figuring it out so I'll likely just have to try out the mouse and keyboard script.

Thank you again for your reply. I appreciate it.

Offline

#9 2021-10-14 16:58:35

windwakr
Member
Registered: 2010-06-10
Posts: 20

Re: Metroid Prime Hunters LUA script

This version of the script should work with US Rev 1. I have not extensively tested it, though.

joyid = 0 --change this if your preferred controller isn't being picked up
deadzone = 0.15 --Adjust analog stick deadzones

cooldown=0
weapon=0
subweapon=0
togglestylus=false
toggleaim=false
vcurx=128
vcury=96
key = {}
joy = {}

--Set the controls here
morphkey = "joy.down" --morph ball switch
visorkey = "joy.up" --scan visor switch(hold)
okkey = "joy.2" --ok button in messages
leftkey = "joy.left" --left arrow in messages
rightkey = "joy.right" --right arrow in messages
weaponkey = "joy.4" --weapon cycle
subweaponkey1 = "joy.5" --subweapon cycle back
subweaponkey2 = "joy.6" --subweapon cycle forward
leftstickx = "joy.x" --must be analog stick axis
leftsticky = "joy.y" --must be analog stick axis
rightstickx = "joy.u" --must be analog stick axis
rightsticky = "joy.r" --must be analog stick axis
shootkey = "joy.z" --may be either analog axis or button
boostkey = "joy.3" --morph ball boost
vstyluskey = "joy.7" --virtual stylus(hold)
jumpkey = "joy.1" --jump
startkey = "joy.8" --start


--https://www.lua.org/pil/14.1.html
function getfield (f)
  local v = _G    -- start with the table of globals
  for w in string.gfind(f, "[%w_]+") do
    v = v[w]
  end
  return v
end

while true do
    joysend = {}
    key=input.get()
    joy = controller.get(joyid)
    
    if getfield(vstyluskey) then
        toggleaim = true --this exists to just delay the pressing of the screen when you release the virtual stylus key
        if vcury>0 then
            gui.drawbox(vcurx-1, vcury-1, vcurx+1, vcury+1, "#000000", "#FFFFFF")
        else
            gui.drawbox(vcurx-1, vcury, vcurx+1, vcury+1, "#FFFFFF") --workaround for vcury=0, as it would draw to the top screen
            gui.pixel(vcurx, vcury, "#000000")
        end
        
        if getfield(subweaponkey2) then
            stylus.set{x=vcurx, y=vcury, touch=true}
        end
        if getfield(subweaponkey1) and cooldown==0 then
            togglestylus = not togglestylus
            print('Touchscreen input allowed = ' .. tostring(togglestylus))
            cooldown = 20
        end
        tmp = getfield(rightstickx)
        if tmp and math.abs(tmp)>deadzone then
            vcurx = vcurx + (tmp * 5)
        end
        tmp = getfield(rightsticky)
        if tmp and math.abs(tmp)>deadzone then
            vcury = vcury + (tmp * 5)
        end
        
        if vcurx<0 then vcurx=0 end
        if vcurx>255 then vcurx=255 end
        if vcury<0 then vcury=0 end
        if vcury>191 then vcury=191 end
    else
        if getfield(morphkey) then --Morph
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=231, y=167, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(visorkey) then --Visor
            if cooldown == 0 then
                stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            end
            cooldown = 10
            stylus.set{x=128, y=173, touch=true}
        end
        if getfield(okkey) then --OK (in scans and messages)
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=128, y=142, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(leftkey) then --Left arrow (in scans and messages)
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=71, y=141, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(rightkey) then --Right arrow (in scans and messages)
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=185, y=141, touch=true} emu.frameadvance() emu.frameadvance()
        end
        if getfield(weaponkey) and cooldown==0 then --Switch weapon (beam->missile->subweapon->beam)
            cooldown=20
            weapon=(weapon+1)%3
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=85+40*weapon, y=32, touch=true} emu.frameadvance()
            stylus.set{x=85+40*weapon, y=32, touch=true}
        end
        if (getfield(subweaponkey1) or getfield(subweaponkey2)) and cooldown==0 then --Switch subweapon (previous and next)
            cooldown=20
            weapon=2
            if getfield(subweaponkey1) then subweapon=(subweapon-1)%6 end --previous
            if getfield(subweaponkey2) then subweapon=(subweapon+1)%6 end --next
            subX=93+25*subweapon subY=48+25*subweapon
            stylus.set{touch=false} emu.frameadvance() emu.frameadvance()
            stylus.set{x=232, y=34, touch=true} emu.frameadvance()
            stylus.set{x=232, y=34, touch=true} emu.frameadvance()
            stylus.set{x=subX, y=subY, touch=true} emu.frameadvance()
            stylus.set{x=subX, y=subY, touch=true}
        end
        tmp = getfield(leftstickx)
        if tmp and tmp < -deadzone then
            joysend.left = true
        elseif tmp and tmp > deadzone then
            joysend.right = true
        end
        tmp = getfield(leftsticky)
        if tmp and tmp < -deadzone then
            joysend.up = true
        elseif tmp and tmp > deadzone then
            joysend.down = true
        end
        tmp = getfield(rightstickx)
        if tmp and math.abs(tmp)>deadzone and togglestylus==false then
            memory.writedword(0x020DEDA6, (tmp * 4))
            toggleaim=false
        end
        tmp = getfield(rightsticky)
        if tmp and math.abs(tmp)>deadzone and togglestylus==false then
            memory.writedword(0x020DEDAE, (tmp * 6))
            toggleaim=false
        end
        if getfield(boostkey) then
            joysend.R = true
        end
        
        tmp = getfield(shootkey)
        if tmp then
            if type(tmp) == "number" then
                if math.abs(tmp) > deadzone then
                    joysend.L = true
                end
            elseif type(tmp) == "boolean" then
                joysend.L = true
            end
        end
        
        if getfield(jumpkey) then
            joysend.B = true
        end
        if getfield(startkey) then
            joysend.start = true
        end
    end
    
    if cooldown>0 then
        cooldown=cooldown-1
    else
        ball = memory.readbyte(0x020DB098) == 0x02 --Is this a good way of detecting morph ball status??
        if ball==false and togglestylus==false and toggleaim==false then
            stylus.set{x=128, y=96, touch=true} --Required for aiming
        end
    end
    
    joypad.set(1,joysend)
    emu.frameadvance()
end

Offline

#10 2021-10-14 19:51:02

WookV3M0
Member
Registered: 2021-10-12
Posts: 3

Re: Metroid Prime Hunters LUA script

That seems to work. Thank you so much. You really didn't need to go through this extra effort but I sincerely appreciate that you did.

Offline

#11 2021-10-14 22:46:22

zeromus
Radical Ninja
Registered: 2009-01-05
Posts: 5,704

Re: Metroid Prime Hunters LUA script

we should fix whatever problems make it hard to use lua in desmume (dont you have to scrounge up the dll or something) and then include some good scripts in the repository as part of a perks pack for next time we make a release (whenever that may be...)

Offline

#12 2021-11-16 18:17:40

TallyFeli
Member
Registered: 2021-11-16
Posts: 1

Re: Metroid Prime Hunters LUA script

Hi....The hardest parts are tracking down the API for the emulator, and observing a bundle to peruse regulator input. When you have those two things, utilizing them is really clear, and I (or another person on these sheets) ought to have the option to help you through it.

DeSmuME's API should be distributed some place... if not it'd be totally unusable. Like I said... if you ask on that emu's discussions, somebody ought to have the option to guide you to something you can utilize.

With respect to the bundle, that may be trickier... as I'm not even certain if one exists. What you need is an overall Lua bundle for getting regulator input - it doesn't need to be explicitly for DeSmuME. What's more, truth be told, there most likely isn't something explicitly for DeSmuME.

Offline

Board footer

Powered by FluxBB