fceux/output/luaScripts/SMB-Mouse.lua

573 lines
17 KiB
Lua

--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, "<Main Menu>") 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