--Super Mario Bros. - Drag and Drop --Written by XKeeper --Allows you to use the mouse to pick up enemies and movie them around! debugmodes = { enabled = false; -- CHANGE THIS AT YOUR PERIL showenemydata = false; drawmouse = false; locktimer = false; invincible = false; ver = "03/29 15:00:00"; }; control = {}; -- must come first. -- basically, will contain all the "interface" things, -- buttons, menus, etc. -- Easier to organize, I guess. require "x_functions"; require "x_interface"; if debugmodes['enabled'] and false then require "x_smb1enemylist"; -- used for summoning and other things... not finished end; if not x_requires then -- Sanity check. If they require a newer version, let them know. timer = 1; while (true) do timer = timer + 1; for i = 0, 32 do gui.drawbox( 6, 28 + i, 250, 92 - i, "#000000"); end; gui.text( 10, 32, string.format("This Lua script requires the x_functions library.")); gui.text( 53, 42, string.format("It appears you do not have it.")); gui.text( 39, 58, "Please get the x_functions library at"); gui.text( 14, 69, "http://xkeeper.shacknet.nu:5/"); gui.text(114, 78, "emu/nes/lua/x_functions.lua"); warningboxcolor = string.format("%02X", math.floor(math.abs(30 - math.fmod(timer, 60)) / 30 * 0xFF)); gui.drawbox(7, 29, 249, 91, "#ff" .. warningboxcolor .. warningboxcolor); FCEU.frameadvance(); end; else x_requires(6); end; -- **************************************************************************** -- * drawmouse(xpos, ypos, click) -- * Draws a crude mouse pointer at the location; mostly good for screenshots and debugging. -- **************************************************************************** function drawmouse(x, y, click) if click then fill = "#cccccc"; else fill = "#ffffff"; end; y = y + 1; for i = 0, 6 do if i ~= 6 then line(x + i, y + i, x + i, y + 8 - math.floor(i / 2), fill); pixel(x + i, y + 8 - math.floor(i / 2), "#000000"); end; pixel(x + i, y + i - 1, "#000000"); end; pixel(x + 1, y + 0, "#000000"); line(x , y , x , y + 9, "#000000"); -- line(x + 1, y + 1, x + 6 , y + 6, "#000000"); -- line(x , y + 11, x + 7 , y + 7, "#000000"); end; -- **************************************************************************** -- * smbpx2ram( screen-x, screen-y ) -- * Returns the offset that represents the tile under screenx/screeny. -- **************************************************************************** function smbpx2ram(px, py) local py = math.floor(py) - 0x20; local px = math.floor(px); if px < 0 or px > 400 or py < 0x00 or py > (240 - 0x20) then return false; end; local oy = math.floor(py / 0x10); local ox = math.fmod(math.floor((px + smbdata['screenpos']) / 0x10), 0x20); offset = 0x500 + math.fmod(oy * 0x10 + math.floor(ox / 0x10) * 0xC0 + math.fmod(ox, 0xD0), 0x1A0); return offset; end; -- **************************************************************************** -- * smbram2px( memory offset ) -- * Gives the current top-left pixel of the tile the offset represents. -- **************************************************************************** function smbram2px(offset) offset = offset - 0x500; if offset < 0 or offset >= 0x1A0 then return false; end; local px = (math.fmod(offset, 0x10) + math.floor(offset / 0xD0) * 0x10) * 0x10; px = px - math.fmod(smbdata['screenpos'], 0x200); if px < 0 then px = px + 0x200; end; local py = math.floor(math.fmod(offset, 0xD0) / 0x10); returnval = {x = px, y = py}; return returnval; end; -- **************************************************************************** -- * smbmoveenemy( Enemy number, xpos, ypos, x accell, y accell ) -- * moves enemies to given point. auto-sets facing direction, as well -- **************************************************************************** function smbmoveenemy(n, x, y, ax, ay) local x1 = math.fmod(x, 0x100); local x2 = math.floor(x / 0x100); local y1 = math.fmod(y, 0x100); local y2 = math.floor(y / 0x100); local ax = math.max(-128, math.min(ax, 0x7F)); local ay = math.max(-128, math.min(ay, 0x7F)); memory.writebyte(0x006D + n, x2); memory.writebyte(0x0086 + n, x1); memory.writebyte(0x00B5 + n, y2); memory.writebyte(0x00CE + n, y1); memory.writebyte(0x0057 + n, ax); memory.writebyte(0x009F + n, ay); if ax > 0 then memory.writebyte(0x0045 + n, 1); elseif ax < 0 then memory.writebyte(0x0045 + n, 2); end; end; -- **************************************************************************** -- * inputaverage() -- * Mouse movement averages (something unique to this...). -- **************************************************************************** function inputaverage() local tempx = 0; local tempy = 0; for temp = 1, 2 do tempx = tempx + avgmove[temp]['xmouse']; tempy = tempy + avgmove[temp]['ymouse']; avgmove[temp] = avgmove[temp + 1]; end; avgmove[3]['xmouse'] = inpt['xmouse'] - last['xmouse']; avgmove[3]['ymouse'] = inpt['ymouse'] - last['ymouse']; avgmove['calc']['xmouse'] = (tempx + avgmove[3]['xmouse']) / 3; avgmove['calc']['ymouse'] = (tempy + avgmove[3]['ymouse']) / 3; end; -- **************************************************************************** -- * tileview() -- * This does all the "what tile is here" shit for you (me) -- **************************************************************************** function tileview () local ramval = smbpx2ram(inpt['xmouse'], inpt['ymouse']); if ramval then local ret = smbram2px(ramval); local c = "#ffffff"; if math.fmod(timer, 4) < 2 then c = "#cccccc"; end; if ret then local tx1 = math.max(0, ret['x'] - 1); local tx2 = math.min(0xFF, ret['x'] + 0x10); local ty1 = math.max(ret['y'] * 0x10 + 0x1F, 0); local ty2 = math.min(244, ret['y'] * 0x10 + 0x30); box(tx1, ty1, tx2, ty2, c); end; local textx = inpt['xmouse'] + 10; local texty = inpt['ymouse'] - 4; if textx > 229 then textx = textx - 42; end; texty = math.min(214, texty); text(textx, texty, string.format("%04X", ramval)); text(textx, texty + 8, string.format(" %02X ", memory.readbyte(ramval))); end; end; -- **************************************************************************** -- * Generic test function, really. Does nothing of use. -- * Incidentally, most of the times this shows up, it doesn't -- **************************************************************************** function test(arg) text(50, 100, "IT WORKS"); end; -- **************************************************************************** -- * spawnsetup(arg) -- * Prepares spawning of an enemy. -- * Perhaps should create a dialog box in the middle of the screen?.. -- **************************************************************************** function spawnsetup(args) if mode ~= 2 then spawndata['lmode'] = mode; mode = 2; spawndata['enum'] = 0x00; spawndata['ename'] = "Green Koopa"; spawndata['etype'] = 0; spawndata['exs'] = 16; spawndata['eys'] = 24; -- etype: --- 0: normal enemy (one slot) --- 1: big enemy (two slots, takes latter but fills both) --- 2: powerup (takes slot 6) end; end; -- **************************************************************************** -- * spawnsetup(arg) -- * Prepares spawning of an enemy. -- * Perhaps should create a dialog box in the middle of the screen?.. -- **************************************************************************** function spawnenemy(args) local c = "#ffffff"; if math.fmod(timer, 4) < 2 then c = "#888888"; end; local freespace = 0; if debugmodes['showenemydata'] then for i = 1, 6 do text(8, 8 + 8 * i, string.format("%d %02X", i, memory.readbyte(0x000E+i))); end; end; for i = 1, 6 do if ((spawndata['etype'] <= 1 and i <= 5) or (spawndata['etype'] == 2 and i == 6)) and memory.readbyte(0x000E+i) == 0 then if debugmodes['showenemydata'] then text(8, 8 + 8 * i, string.format("%d %02X *", i, memory.readbyte(0x000E+i))); end; if (spawndata['etype'] == 1 and memory.readbyte(0x000E + i - 1) == 0) or spawndata['etype'] ~= 1 then freespace = i; break; end; end; end; if freespace > 0 then box(inpt['xmouse'] - (spawndata['exs'] / 2), inpt['ymouse'] - (spawndata['eys'] / 2), inpt['xmouse'] + (spawndata['exs'] / 2), inpt['ymouse'] + (spawndata['eys'] / 2), c); text(70, 31, string.format("Summon [%s]", spawndata['ename'])); if debugmodes['showenemydata'] then text(70, 39, string.format("Enemy slot [%X]", freespace)); end; local mx = smbdata['screenpos'] + inpt['xmouse']; local my = 0x100 + inpt['ymouse']; if inpt['leftclick'] and not last['leftclick'] then memory.writebyte(0x000E + freespace, 1); memory.writebyte(0x0015 + freespace, spawndata['enum']); memory.writebyte(0x0499 + freespace, 3); smbmoveenemy(freespace, mx - (spawndata['exs'] / 2), my - (spawndata['eys'] / 2), -1, 0); mode = spawndata['lmode']; end; else text(70, 31, string.format("Can't summon (too many enemies)!")); end; end; -- **************************************************************************** -- * modechange(arg) -- * changes current mode (used in menu). -- * also changes menu text to reflect new mode. -- **************************************************************************** function modechange(args) mode = args[1]; if args[1] == 0 then mainmenu['menu']['m001_mode']['menu']['m001_tiles']['marked'] = 1; mainmenu['menu']['m001_mode']['menu']['m002_enemy']['marked'] = 0; else mainmenu['menu']['m001_mode']['menu']['m001_tiles']['marked'] = 0; mainmenu['menu']['m001_mode']['menu']['m002_enemy']['marked'] = 1; end; end; -- **************************************************************************** -- * debugmode(arg) -- * changes debugmode flags -- * useful for on-the-fly checking, I guess -- **************************************************************************** function debugmode(args) if debugmodes[args[2]] == false then debugmodes[args[2]] = true; mainmenu['menu']['m999_debug']['menu'][args[1]]['marked'] = 1; else debugmodes[args[2]] = false; mainmenu['menu']['m999_debug']['menu'][args[1]]['marked'] = 0; end; end; mainmenu = { test = 1; life = 0; width = 54; menu = { m001_mode = { label = "Mode", life = 0; width = 50; menu = { m001_tiles = { label = " Objects", action = modechange, args = {0}, marked = 0; }, m002_enemy = { label = " Sprites", action = modechange, args = {1}, marked = 1; }, }, }, m002_summon = { label = "Summon", action = spawnsetup, args = {1}, }, }, }; if debugmodes['enabled'] then mainmenu['menu']['m999_debug'] = { label = "Debug", life = 0; width = 90; menu = { m000_showmouse = { label = " Draw mouse", action = debugmode, marked = debugmodes['drawmouse'] and 1 or 0; args = {"m000_showmouse", "drawmouse"}, }, m001_enemydata = { label = " Show enemy data", action = debugmode, marked = debugmodes['showenemydata'] and 1 or 0; args = {"m001_enemydata", "showenemydata"}, }, m002_locktimer = { label = " Lock timer", action = debugmode, marked = debugmodes['locktimer'] and 1 or 0; args = {"m002_locktimer", "locktimer"}, }, m003_invincible = { label = " Invincibility", action = debugmode, marked = debugmodes['invincible'] and 1 or 0; args = {"m003_invincible", "invincible"}, }, }, }; end; smbdata = {screenpos = 0}; mode = 1; enemyhold = {}; avgmove = { { xmouse = 0, ymouse = 0 }, { xmouse = 0, ymouse = 0 }, { xmouse = 0, ymouse = 0 }, calc = {} }; spawndata = {}; while (true) do input.update(); -- updates mouse position inputaverage(); -- average movement (for throwing) smbdata['screenposold'] = smbdata['screenpos']; smbdata['screenpos'] = memory.readbyte(0x071a) * 0x100 + memory.readbyte(0x071c); smbdata['screenposchg'] = smbdata['screenpos'] - smbdata['screenposold']; smbdata['rendercol'] = memory.readbyte(0x06A0); if smbdata['screenposchg'] < 0 then smbdata['screenposchg'] = 0; end; timer = timer + 1; if debugmodes['enabled'] then if control.button( 234, 15, 19, 2, "SET\n999") then memory.writebyte(0x07F8, 0x09); memory.writebyte(0x07F9, 0x09); memory.writebyte(0x07FA, 0x09); end; if debugmodes['locktimer'] then memory.writebyte(0x0787, 0x1F); end; if debugmodes['invincible'] then memory.writebyte(0x079E, 0x02); end; end; if mode == 0 then tileview(); elseif mode == 2 then spawnenemy(); else if debugmodes['showenemydata'] then text(0, 25 + 0, string.format("E# XPOS YPOS XREL YREL XA YA TY HB")); end; for i=1,6 do if (memory.readbyte(0x000E+i) ~= 0) then --and memory.readbyte(0x04AC+(i*4)) ~= 0xFF) and (memory.readbyte(0x0015 + i) ~= 0x30 and memory.readbyte(0x0015 + i) ~= 0x31) then if not enemyhold[i] or not inpt['leftclick'] then enemyhold[i] = nil; -- text(8, 50 + i * 8, "-"); elseif enemyhold[i] then -- text(8, 50 + i * 8, string.format("HOLD %04X %04X", smbdata['screenpos'] + inpt['xmouse'] - enemyhold[i]['xmouse'], inpt['ymouse'] + 0x100 - enemyhold[i]['ymouse'])); smbmoveenemy(i, smbdata['screenpos'] + inpt['xmouse'] - enemyhold[i]['x'], inpt['ymouse'] + 0x100 - enemyhold[i]['y'], (avgmove['calc']['xmouse']) * 8, avgmove['calc']['ymouse'] / 2.5); end; e2x1 = memory.readbyte(0x04AC+(i*4)); e2y1 = memory.readbyte(0x04AC+(i*4)+1); e2x2 = memory.readbyte(0x04AC+(i*4)+2); e2y2 = memory.readbyte(0x04AC+(i*4)+3); -- text(e2x1 - 5, e2y1 - 13, string.format("%02X", memory.readbyte(0x001E + i))); enemyxpos = memory.readbytesigned(0x006D + i) * 0x0100 + memory.readbyte(0x0086 + i); enemyypos = memory.readbytesigned(0x00B5 + i) * 0x100 + memory.readbyte(0x00CE + i); enemyxacc = memory.readbytesigned(0x0057 + i); enemyyacc = memory.readbytesigned(0x009f + i); enemyxposa = enemyxpos - smbdata['screenpos']; enemyyposa = enemyypos - 0x100; enemyyposa2 = math.fmod(enemyyposa + 0x10000, 0x100); enemytype = memory.readbyte(0x0015 + i); enemyhitbox = memory.readbyte(0x0499 + i); dead = ""; if enemyyposa <= -72 + 32 and enemyyacc < -3 then dead = "UP"; end; if math.abs(enemyxacc) >= 0x60 then dead = dead .. "SIDE"; end; if dead ~= "" then dead = " ".. dead; end; if debugmodes['showenemydata'] then line(enemyxposa, enemyyposa2, enemyxposa + 16, enemyyposa2, "#ffffff"); text(enemyxposa, enemyyposa2, memory.readbyte(0x0045 + i)); text(1, 25 + 8 * i, string.format("E%X %04X %04X %04X %04X %02X %02X %02X %02X %s", i, AND(enemyxpos, 0xFFFF), AND(enemyypos, 0xFFFF), AND(enemyxposa, 0xFFFF), AND(enemyyposa, 0xFFFF), AND(enemyxacc, 0xFF), AND(enemyyacc, 0xFF), AND(enemytype, 0xFF), AND(enemyhitbox, 0xFF), "")); -- dead)); end; if hitbox(inpt['xmouse'], inpt['ymouse'], inpt['xmouse'], inpt['ymouse'], e2x1, e2y1, e2x2, e2y2, "#ffffff", "#0000ff") then -- if hitbox(inpt['xmouse'], inpt['ymouse'], inpt['xmouse'], inpt['ymouse'], enemyxposa, enemyyposa, enemyxposa + 0xF, enemyyposa + 0x17) then -- text(e2x1 - 5, e2y1 - 13, string.format("#%d %02X", i, memory.readbyte(0x0015 + i))); if inpt['leftclick'] then if not enemyhold[i] then enemyhold[i] = { x = inpt['xmouse'] - enemyxposa, y = inpt['ymouse'] - enemyyposa }; end; -- memory.writebyte(0x001F + i, 0xFF); -- memory.writebyte(0x04AC + i, 0xFF); -- memory.writebyte(0x000E + i, 0x00); end; end; else enemyhold[i] = nil; end; end; end; --[[ zap = zapper.read(); box(zap['xmouse'] - 5, zap['ymouse'] - 5, zap['xmouse'] + 5, zap['ymouse'] + 5, "#ffffff"); if zap['click'] == 1 then box(zap['xmouse'] - 7, zap['ymouse'] - 7, zap['xmouse'] + 7, zap['ymouse'] + 7, "#ff0000"); end; line(zap['xmouse'] - 0, zap['ymouse'] - 9, zap['xmouse'] + 0, zap['ymouse'] + 9, "#ffffff"); line(zap['xmouse'] - 9, zap['ymouse'] - 0, zap['xmouse'] + 9, zap['ymouse'] + 0, "#ffffff"); ]] -- almost always if control.button(78, 14, 54, 1, "
") then mainmenu['life'] = 70; end; control.showmenu(78, 25, mainmenu); text( 20, 222, " 2009 Xkeeper - http://jul.rustedlogic.net/ "); line( 21, 231, 232, 231, "#000000"); if debugmodes['enabled'] then text( 41, 214, " Debug version - ".. debugmodes['ver'] .." "); end; -- always on top if debugmodes['drawmouse'] then drawmouse(inpt['xmouse'], inpt['ymouse'], inpt['leftclick']); end; FCEU.frameadvance(); end