Last active
April 13, 2023 23:46
-
-
Save benolee/314572a7be9a984f874adca32c9d7240 to your computer and use it in GitHub Desktop.
Mesen port of Scumtron's Lua Script for Ninja Gaiden II
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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