Skip to content

Instantly share code, notes, and snippets.

@benolee
Last active April 13, 2023 23:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benolee/314572a7be9a984f874adca32c9d7240 to your computer and use it in GitHub Desktop.
Save benolee/314572a7be9a984f874adca32c9d7240 to your computer and use it in GitHub Desktop.
Mesen port of Scumtron's Lua Script for Ninja Gaiden II
-- Q, W, I, A keys while unpaused or frame-advancing toggles stuff
-- for Mesen 0.9.9
local bit = require "bitop_funcs".bit
local function BIT(x)
return bit.bor(0, 2^x)
end
function tobitstring(n,m)
if n > 0 then
local t = { }
while n > 0 do
insert(t,1,n % 2 > 0 and 1 or 0)
n = floor(n/2)
end
local nn = 8 - #t % 8
if nn > 0 and nn < 8 then
for i=1,nn do
insert(t,1,0)
end
end
if m then
m = m * 8 - #t
if m > 0 then
insert(t,1,rep("0",m))
end
end
return concat(t)
elseif m then
rep("00000000",m)
else
return "00000000"
end
end
local function gui_box(x1, y1, x2, y2, fillcolor, outlinecolor)
local fill = fillcolor or false
local fillwidth = x2 - x1 - 2
local fillheight = y2 - y1 - 2
local fillcolor = fillcolor or 0xffffffff
local outlinewidth = x2 - x1
local outlineheight = y2 - y1
local outlinecolor = outlinecolor or 0xffffffff
if fill then
emu.drawRectangle(x1+1, y1+1, fillwidth, fillheight, fillcolor, true)
end
emu.drawRectangle(x1, y1, outlinewidth, outlineheight, outlinecolor, false)
end
local f, g = 0x007f7f7f, 0x8f000000
local lv, move, pk, kp = 0x30, "", {}, {}
local qtog, atog, wtog, itog = false, false, true, true
local substage = "abc abc abcde abcabc abcabc abc abcdeabcde "
-- local xPlot, yPlot = {}, {}
local fstr1 = "%02d\n%s\n\n%02X\n%s\n%02X\n\n%02X\n%02X\n%s"
local fstr2 = " %02X%02X %02X%02X % .2f %s\n %02X%02X %02X%02X % .2f\n%05X"
local qfstr = "%02X:%02X|\n%02X.%02X|\n%02X.%02X|\n%02X.%02X|\n%02X.%02X|"
local function Spawns()
local function PrintSpawns(blks,pntr,lvl)
local count = emu.read(0xC9, emu.memType.cpuDebug)
local blocks, spawns = "",""
for i = 0, count do
local eOff = emu.read(pntr+i, emu.memType.cpuDebug)
blocks = blocks..string.format(" %02X", emu.read(blks+i, emu.memType.cpuDebug))
spawns = spawns..string.format(" %02X", emu.read(0xE56D+eOff, emu.memType.cpuDebug))
end
local stagenum = string.format("%02X\t",lvl)..emu.read(0x2356, emu.memType.ppuDebug).."-"..
emu.read(0x2358, emu.memType.ppuDebug)..substage:sub(lvl+1,lvl+1)
emu.log(stagenum.."\r\nB:"..blocks.."\r\nE:"..spawns.."\r\n\r\n")
end
local function PrintOrbs(lvl)
local count = emu.read(0xB9, emu.memType.cpuDebug)-1
local bPntr = emu.readWord(0xBA, emu.memType.cpuDebug)
local oPntr = emu.readWord(0xBC, emu.memType.cpuDebug)
local right = emu.read(0x92, emu.memType.cpuDebug)==0
local blocks, spawns = "",""
for i = 0, count do
local blk = emu.read(bPntr+i, emu.memType.cpuDebug)
if right then blk = blk-0x10 end
blocks = blocks..string.format(" %02X", blk)
spawns = spawns..string.format(" %02X", bit.band(emu.read(oPntr+i, emu.memType.cpuDebug),0xF))
end
emu.log(string.format("%02X\r\nB: ",lvl)..blocks.."\r\nO: "..spawns.."\r\n")
end
local spawnBlks = emu.readWord(0xC3, emu.memType.cpuDebug)
local spawnPntr = emu.readWord(0xC7, emu.memType.cpuDebug)
local offset = emu.read(0xCA, emu.memType.cpuDebug)
local spawn = emu.read(spawnBlks+offset, emu.memType.cpuDebug)
local spwnBlk = emu.read(0x40, emu.memType.cpuDebug)
local nextSpawn = string.format("%02X",emu.read(0xE56D+emu.read(spawnPntr+offset, emu.memType.cpuDebug), emu.memType.cpuDebug))
local R, L = "R ", "L "
if (spwnBlk == spawn) then R = R..nextSpawn end
if (spwnBlk+1 == spawn) then L = L..nextSpawn end
emu.drawString(0,66,R.."\n"..L,f,g)
if f == 0x00ffffff then
local level = emu.read(0x7E, emu.memType.cpuDebug)
if (lv ~= level) then PrintSpawns(spawnBlks,spawnPntr,level) end
-- if (lv ~= level) then PrintOrbs(level) end
lv = level
end
end
local function ViewBGh()
local x, y = 37, 67
local function pr(a) if a==0 then return end emu.drawString(x,y,bit.tohex(a,-1),f,g) end
for c = 0, 0xE1, 0xF do
for r = 0, 0xE do
local tile = emu.read(0x300+c+r, emu.memType.cpuDebug)
pr(bit.rshift(tile,4))
x = x+6
pr(bit.band(tile,0xF))
x, y = x-6, y+8
end
x, y = x+12, 67
end
end
local function NG2RAMview()
if not itog then return end
local xPos_f = emu.read(0x538, emu.memType.cpuDebug)
local xPos = emu.read(0x550, emu.memType.cpuDebug)
local xSpd_f = emu.read(0x598, emu.memType.cpuDebug)
local xSpd = emu.read(0x5B0, emu.memType.cpuDebug, true)
local yPos_f = emu.read(0x568, emu.memType.cpuDebug)
local yPos = emu.read(0x580, emu.memType.cpuDebug)
local ySpd_f = emu.read(0x5C8, emu.memType.cpuDebug)
local ySpd = emu.read(0x5E0, emu.memType.cpuDebug, true)
local action = emu.read(0x4A8, emu.memType.cpuDebug)
local bgColl = emu.read(0x4C0, emu.memType.cpuDebug)
local screen_f = emu.read(0x39, emu.memType.cpuDebug)
local screen_p = emu.read(0x3A, emu.memType.cpuDebug)
local screen = emu.read(0x3B, emu.memType.cpuDebug)*0x10000
local inv = emu.read(0x68, emu.memType.cpuDebug)
local bossHP = emu.read(0x4D, emu.memType.cpuDebug) > 0 and string.format("%02d",emu.read(0x60F)) or ""
local windT = emu.read(0x42, emu.memType.cpuDebug)
local windC = emu.read(0x66, emu.memType.cpuDebug)
local atks = bit.band(emu.read(0x49, emu.memType.cpuDebug),7)
atks = atks > 0 and tobitstring(atks):sub(7,9) or ""
Spawns()
if atog then ViewBGh() end
local face = bit.band(emu.read(0x4F0, emu.memType.cpuDebug),0x40) > 0 and "L" or "R"
emu.drawString(242,25,fstr1:format(
inv,bossHP,bgColl,move,action,windT,windC,face),f,g)
local ecks = screen + screen_p*0x100 + screen_f
local xv, yv = xSpd+(xSpd_f/256), ySpd+(ySpd_f/256)
emu.drawString(0,41,fstr2:format(
yPos,yPos_f,bit.band(ySpd,0xFF),ySpd_f,yv,atks,
xPos,xPos_f,bit.band(xSpd,0xFF),xSpd_f,xv,
ecks),f,g)
move, f = "!!", 0x00ff7f00
-- xPlot[#xPlot+1],yPlot[#yPlot+1]=xPos,yPos
-- for i=1,#xPlot do emu.drawPixel(xPlot[i],yPlot[i],0x00ff0000) end
end
local function Objects()
if not wtog then return end
local tX, tY, back, etxt = 210, 0, 0x3f000000, 0x0000ff00
local oddcount, crouch, slash = emu.read(0xC1, emu.memType.cpuDebug)%2==1, bit.band(emu.read(0x520, emu.memType.cpuDebug),2) > 0, emu.read(0x4A8, emu.memType.cpuDebug)
slash = (slash >= 0x1D and slash <= 0x1F)
local eBox = (oddcount or slash) and 0xc0ffffff or 0x8fff0000
local sx, sy, sd = {}, {}, {}
for s=0,2 do
sx[s] = emu.read(0x550+s, emu.memType.cpuDebug)
sy[s] = emu.read(0x580+s, emu.memType.cpuDebug)
sd[s] = bit.band(emu.read(0x4F0+s, emu.memType.cpuDebug),0x40) > 0
end
local active = emu.readWord(0x49, emu.memType.cpuDebug)*0x100 + emu.read(0x48, emu.memType.cpuDebug)
local function Swords(xpos,ypos,xrad,yrad,color,orb)
-- Attempts to show when a slash would be in range of something
-- (B-press on framecount-2 for jump slash, on framecount-4 for standing slash)
-- may show some false positives for bottom of item orbs
for s=2,0,-1 do
local x, y = sx[s], orb and sy[s] or sy[s]-3
local sxrad, inrange = 0x20
if sd[s] then
local xleft = orb and sxrad or 0x22
sxrad = -sxrad
inrange = (bit.band(x-(xpos+xrad), 0xFF) < xleft)
else
inrange = (bit.band(xpos-xrad - x, 0xFF) < sxrad)
end
if inrange then
if (y < ypos) then
inrange = (ypos-yrad < y)
else
inrange = (ypos+yrad >=y)
end
end
if inrange then emu.drawLine(x,y,x+sxrad,y,color) end
end
end
-- Enemies
for i = 0x17, 0x0B, -1 do
local eID = emu.read(0x4D8+i, emu.memType.cpuDebug)
local eXpos = emu.read(0x550+i, emu.memType.cpuDebug)
local eYpos = emu.read(0x580+i, emu.memType.cpuDebug)
local a = bit.band(active, BIT(i)) > 0
if (a and (eID ~= 0xAF and eID ~= 0x05)) then
local eBGc = emu.read(0x4C0+i, emu.memType.cpuDebug)
local eTimer = emu.read(0x508+i, emu.memType.cpuDebug)
local eXrad = emu.read(0x610+i, emu.memType.cpuDebug)
local eYrad = emu.read(0x628+i, emu.memType.cpuDebug)
local eMisc = emu.read(0x640+i, emu.memType.cpuDebug)
local notProj = bit.band(emu.read(0x520+i, emu.memType.cpuDebug),0x10)==0
Swords(eXpos,eYpos,eXrad,eYrad,0x00ff00ff,false)
gui_box(eXpos-eXrad,eYpos-eYrad,eXpos+eXrad,eYpos+eYrad,eBox,eBox)
emu.drawString(eXpos,eYpos-8,string.format("%02X:%02X",i,eMisc),etxt,0)
if eTimer > 0 then
emu.drawString(eXpos,eYpos+1,string.format("%02X",eTimer),etxt,0) end
if (eBGc > 0 and notProj) then
emu.drawString(eXpos+15,eYpos+1,string.format("%02X",eBGc),etxt,0) end
-- if (eID=='45')then xPlot[#xPlot+1],yPlot[#yPlot+1]=eXpos,eYpos end
end
if qtog then
local front = a and 0x00aaeeaa or 0x00ff9f8f
local einfo = qfstr:format(i, eID,
eXpos, emu.read(0x538+i, emu.memType.cpuDebug),emu.read(0x5B0+i, emu.memType.cpuDebug), emu.read(0x598+i, emu.memType.cpuDebug),
eYpos, emu.read(0x568+i, emu.memType.cpuDebug), emu.read(0x5E0+i, emu.memType.cpuDebug), emu.read(0x5C8+i, emu.memType.cpuDebug))
emu.drawString(tX, tY, einfo, front, back)
tX=tX-30
if (i == 0x10) then tX, tY, back = 210, 200, 0xbf000000 end
end
end
-- Weapons
local size, wBox = emu.read(0xC0, emu.memType.cpuDebug), (not oddcount or slash) and 0xa0ffffff or 0xa000ff00
for i = 0xA, 0x8, -1 do
local a = bit.band(active, BIT(i)) > 0
if a then
local x = emu.read(0x550+i, emu.memType.cpuDebug)
local y = emu.read(0x580+i, emu.memType.cpuDebug)
gui_box(x-size,y-size,x+size,y+size,wBox,wBox)
end
end
-- Orbs
local xrad, yrad, oBox = 0, 0xc, 0xa0ffffff
for i = 0x7, 0x4, -1 do
local a = bit.band(active, BIT(i)) > 0
local orbf = bit.band(emu.read(0x4F0+i, emu.memType.cpuDebug),4)==0
if (a and orbf) then
local orbS = emu.read(0x520+i, emu.memType.cpuDebug)
local x = emu.read(0x550+i, emu.memType.cpuDebug)
local y = emu.read(0x580+i, emu.memType.cpuDebug)
if (orbS <= 0xF) then Swords(x,y,xrad,yrad,oBox,true) end
emu.drawLine(x,y-yrad,x,y+yrad,oBox)
emu.drawString(x-2,y-2,bit.tohex(orbS,-1),0x00000000,oBox)
end
end
-- Clone boxes (keeps something visible when frame-advancing or if sprites are disabled)
xrad, yrad = 3, 10
for i = 1, 2 do gui_box(sx[i]-xrad,sy[i]-yrad,
sx[i]+xrad,sy[i]+yrad,0xc0ff0000,0xc0ff0000) end
-- Ryu
local x, y = sx[0], sy[0]
xrad, yrad = 0x08, crouch and 0x0C or 0x10
gui_box(x-xrad,y-yrad,x+xrad,y+yrad,0xbf0000ff,0xbf0000ff)
end
local props = {[0] =
-- U,D,L,R,color
{0,0,0,0,0xff000000}, -- 0, air
{1,0,0,0,0x3000ff00}, -- 1, platform
{1,1,1,1,0x000000ff}, -- 2, barrier
{0,0,1,0,0x0000ff00}, -- 3, right wall
{0,0,0,1,0x0000ff00}, -- 4, left wall
{1,1,1,1,0x1000ff00}, -- 5, corner
{1,1,1,1,0x00ffffff}, -- 6, exit, next
{1,1,1,1,0x00ffffff}, -- 7, exit, previous
{1,1,1,1,0x00cf002f}, -- 8, spikes/flames
{1,1,1,1,0x00ffff00}, -- 9, exit, cutscene
{1,1,1,1,0x00ff00ff}, -- A, unused
{1,0,0,0,0x3000ff00}, -- B, ice platform
{1,1,1,0,0x3000ff00}, -- C, ice corner, left
{1,1,0,1,0x3000ff00}, -- D, ice corner, right
{1,1,1,1,0x0000cf7f}, -- E, right water
{1,1,1,1,0x00007fcf}} -- F, left water
local function Background()
if not wtog then return end
local x_off = emu.read(0xC2, emu.memType.cpuDebug)
local c_off = emu.read(0x8A, emu.memType.cpuDebug)
local b, base = 15, 0x300
local function DrawBG(a,x,y)
local c2 = props[a][5]
if props[a][1] == 1 then emu.drawLine(x, y, x+b, y, c2) end
if props[a][2] == 1 then emu.drawLine(x, y+b, x+b, y+b, c2-0x30000000) end
if props[a][3] == 1 then emu.drawLine(x, y, x, y+b, c2) end
if props[a][4] == 1 then emu.drawLine(x+b, y, x+b, y+b, c2) end
end
for c = 0, 8 do
local col = (c*0xF+c_off)%0xF0 + base
local x, y = c*32-x_off, 48
for row = 0, 11 do
local tile = emu.read(col+row, emu.memType.cpuDebug)
local hi = bit.rshift(tile,4)
local lo = bit.band(tile,0xF)
if hi > 0 then DrawBG(hi,x,y) end
if lo > 0 then DrawBG(lo,x+16,y) end
y = y+16
end
end
local xPos, yPos = emu.read(0x550, emu.memType.cpuDebug), emu.read(0x580, emu.memType.cpuDebug)
local yPlat, yWall, c1 = emu.read(0x054, emu.memType.cpuDebug)+yPos, emu.read(0x055, emu.memType.cpuDebug)+yPos, 0x00ff00ff
local bgX = emu.read(0x67, emu.memType.cpuDebug)*0x100 + emu.read(0x09, emu.memType.cpuDebug) -- $67 should always be 0 for Ryu
if (emu.read(0x5E0, emu.memType.cpuDebug) <= 0x7F) then
emu.drawPixel(xPos+3,yPlat,c1) -- platform, right
emu.drawPixel(xPos-5,yPlat,c1) -- platform, left
end
emu.drawPixel(xPos+5,yPos,0x000000ff) -- barrier, right
emu.drawPixel(xPos+5,yWall,c1) -- wall, right
emu.drawPixel(xPos-9,yPos,0x000000ff) -- barrier, left
emu.drawPixel(xPos-9,yWall,c1) -- wall, left
emu.drawString(140,41,string.format("%02X %03X",yPos,bgX),0x007f7f7f,g)
end
emu.addEventCallback(NG2RAMview, emu.eventType.endFrame)
emu.addMemoryCallback(function() move="+" end, emu.memCallbackType.cpuExec, 0x9BEF) -- shadow clone movement
emu.addMemoryCallback(function() f=0x00ffffff end, emu.memCallbackType.cpuExec, 0xE2DA) -- indicates gameplay frames
emu.addMemoryCallback(Background, emu.memCallbackType.cpuExec, 0xC2B9)
emu.addMemoryCallback(Objects, emu.memCallbackType.cpuExec, 0xE285)
-- uncomment next line to see all points checked for background collision (Ryu, bad guys, falling items)
-- emu.addMemoryCallback(function() emu.drawPixel(emu.getState().cpu.x-emu.read(0xC2, emu.memType.cpuDebug),emu.getState().cpu.x,0x00ffff00);end, emu.memCallbackType.cpuExec, 0x977C)
-- local function Pr(b) return kp[b] and not pk[b] end
-- local function getInput()
-- pk = kp
-- kp = {}
-- kp['Q'] = emu.isKeyPressed('Q')
-- kp['A'] = emu.isKeyPressed('A')
-- kp['W'] = emu.isKeyPressed('W')
-- kp['I'] = emu.isKeyPressed('I')
--
-- if Pr('Q') then qtog = not qtog end
-- if Pr('A') then atog = not atog end
-- if Pr('W') then wtog = not wtog end
-- if Pr('I') then itog = not itog end
-- end
-- emu.addEventCallback(getInput, emu.eventType.inputPolled)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment