--Super Mario Bros. - Jetpack
--Written by XKeeper
--A fun script that gives mario a "Jetback"

require("x_functions");

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/");
		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(4);
end;



function doballs()

	count	= 0;
	for k, v in pairs(balls) do
		
		v['x']		= v['x'] + v['xs'];
		v['y']		= v['y'] + v['ys'];
		v['ys']		= v['ys'] - 0.1;
		v['life']	= v['life'] - 1;

		if v['x'] < 0 or v['x'] > 254 or v['y'] < 0 or v['y'] > 243 or v['life'] < 0 then
			balls[k]	= nil;
		else
			balls[k]	= v;
--			pixel(v['x'], v['y'], "#FFFFFF");
			colkey		= math.ceil(255 * (5 - math.max(math.min(5, (v['life'] / 15)), 0)) / 5);

			if v['c'] >= 0 then
				color	= string.format("#%02X%02X%02X", v['c'], v['c'], v['c']);
--				color	= string.format("#%02X%02X%02X", v['c'] * .8, v['c'] * .5, v['c'] * 0);
			else
				color	= string.format("#%02X0000", v['c'] * -1 , 0, 0);
			end;

			if v['life'] > 45 then 
				box(v['x'], v['y'], v['x'] + 1, v['y'] + 1, color);
			else
				pixel(v['x'], v['y'], color);
			end;
			count		= count + 1;
		end;
	end;

--	lifebar( 2, 140, 249, 10, spower, 400, "#ffffff", "#000044", "#bbbbff");
	return count;

end;



function nojumping()
	memory.writebyte(0x000a, AND(memory.readbyte(0x000a), 0x7F));

	return true;
end;

function nomoving()
	joyput	= joypad.read(1);
	if (joyput['left'] or joyput['right']) then
		memory.writebyte(0x000c, 0);
	end;

	return true;
end;


function areascrambler() 
	memory.writebyte(0x00e7, math.random(0, 0xFF));
	memory.writebyte(0x00e8, math.random(0, 0xFF));
end;

function enemyscrambler() 
	memory.writebyte(0x00e9, math.random(0, 0xFF));
	memory.writebyte(0x00ea, math.random(0, 0xFF));
end;


--[[
memory.register(0x00e8, areascrambler);
memory.register(0x00ea, enemyscrambler);
--]]
	
-- gui.register(gameloop);
memory.register(0x000a, nojumping);
memory.register(0x000c, nomoving);

testcount	= 0;
balls		= {};
z			= 0;
timer		= 0;
jmax		= 500;
jlife		= 500; -- jmax;
refillrate	= 0.020;
msgdisp		= 300;
rechargerate	= 0;
while (true) do 
	timer	= timer + 1;


	if timer < msgdisp then
		yo	= (((math.max(0, (timer + 60) - msgdisp))) ^ 2) / 50;
		text(20, 50 - yo, "2009 Xkeeper - http://jul.rustedlogic.net/");
		text(49, 64 - yo, "A: Jetpack  Left/Right: Move");
		text(53, 72 - yo, "  B button: Turbo boost!  ");
	end;

	invincible			= false;
	if memory.readbyte(0x079F) >= 1 then
		cyclepos		= math.abs(15 - math.fmod(timer, 30)) / 15;
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0xFF));
		barcolor		= "#" .. warningboxcolor .. warningboxcolor .."ff";
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0x80) + 0x7F);
		barcolor2		= "#0000" .. warningboxcolor;
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0x40));
		barcolor3		= "#0000" .. warningboxcolor;
--		barcolor3		= "#000000";

		jlife			= math.min(jmax, jlife + (jmax - jlife) / 25 + 0.25);
		rechargerate	= -0.025;
		invincible		= true;
	elseif jlife <= jmax * 0.25 then
		cyclepos		= math.abs(15 - math.fmod(timer, 30)) / 15;
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0xFF));
		barcolor		= "#ff" .. warningboxcolor .. warningboxcolor;
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0x80) + 0x7F);
		barcolor2		= "#" .. warningboxcolor .. "0000";
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0x40));
		barcolor3		= "#" .. warningboxcolor .. "0000";
--		barcolor3		= "#000000";
	else
		barcolor	= "#ffffff";
		barcolor2	= "#ff4444";
		barcolor3	= "#000000";
	end;
	lifebar(5, 8, 240, 2, jlife, jmax, barcolor, barcolor3, "#000000", barcolor2);
	textx	= math.max(4, math.min(math.floor(jlife/ jmax * 240) - 4, 229));
	if jlife == jmax then
		textx	= 223;
	end;
	text(textx, 13, string.format("%2.1d%%", math.min(jlife/ jmax * 100)));
	
--[[
	bxp		= math.sin(timer / 120) * 64 + 127;
	byp		= math.cos(timer / 120) * 64 + 127;
	for i = 0, 0 do
		balls[z]	= {x = bxp + math.random(-100, 200) / 100, y = byp + math.random(-4, 4), xs = math.random(-100, 100) / 50, ys = math.random(-100, 100) / 100, life = math.random(60, 120), c = math.random(128, 255)};
		z			= z + 1;
	end;
]]

	
	doballs();

	
	marioxspeed			= memory.readbytesigned(0x0057);
	marioyspeed			= memory.readbytesigned(0x009F);
	marioxpos			= memory.readbyte(0x4AC);
	marioypos			= memory.readbyte(0x4AD);

--	lifebar(5, 2, 240, 2, marioxspeed + 0x40, 0x80, "#8888ff", "#000000");

	joyput	= joypad.read(1);
	if joyput['up'] then
		memory.writebyte(0x07F8, 3);
	end;
	if joyput['A'] and jlife >= 3 then

		rechargerate	= 0;
		memory.writebyte(0x009F, math.max(-3, marioyspeed - 1));
--		memory.writebyte(0x009F, -3);
		if not invincible then jlife	= math.max(0, jlife - 3); end;
		for i = 0, 10 do
			balls[z]	= {x = marioxpos + 5, y = marioypos + 7, xs = math.random(-70, 70) / 100, ys =  math.random(100, 300) / 100, life = math.random(30, 60), c = math.random(128, 255)};
			z			= z + 1;
		end;
	end;
	if (joyput['left'] or joyput['right']) then
		
		speedchange		= 1;
		speedmax		= 0x28;
		if joyput['B'] and jlife > 0 then
			rechargerate	= 0;
			if not invincible then jlife	= math.max(0, jlife - 1); end;
			speedchange		= 5;
			speedmax		= 0x40;
		end;

		if joyput['left'] then
			memory.writebyte(0x0033, 2);
			memory.writebyte(0x0045, 2);
			if marioxspeed > (speedmax * -1) then
				memory.writebyte(0x0057, math.max(-0x40, marioxspeed - speedchange));
			end;
			for i = 0, 10 do
				balls[z]	= {x = marioxpos + 7, y = marioypos + 7, xs = math.random(300, 400) / 100, ys =  math.random(-10, 20) / 100, life = math.random(5, 5 + speedchange * 10), c = math.random(128, 255)};
				z			= z + 1;
			end;
		else

			memory.writebyte(0x0033, 1);
			memory.writebyte(0x0045, 1);
			if marioxspeed < speedmax then
				memory.writebyte(0x0057, math.min(0x40, marioxspeed + speedchange));
			end;
			for i = 0, 10 do
				balls[z]	= {x = marioxpos + 7, y = marioypos + 7, xs = math.random(-400, -300) / 100, ys =  math.random(-10, 20) / 100, life = math.random(5, 5 + speedchange * 10), c = math.random(128, 255)};
				z			= z + 1;
			end;
		end;
	end;
	if not ((joyput['B'] and (joyput['left'] or joyput['right'])) or joyput['A']) then

		rechargerate	= rechargerate + refillrate;
		jlife			= math.min(jmax, jlife + rechargerate);

	end;





	screenpage		= memory.readbyte(0x071a);
	screenxpos		= memory.readbyte(0x071c);

	arealow			= memory.readbyte(0x00e7);
	areahigh		= memory.readbyte(0x00e8);

	enemylow		= memory.readbyte(0x00e9);
	enemyhigh		= memory.readbyte(0x00ea);


--	text( 10,  24, string.format("Screen position: %02X.%02X", screenpage, screenxpos));
--	text(169,  24, string.format("Area: %02X %02X", areahigh, arealow));
--	text(163,  32, string.format("Enemy: %02X %02X", enemyhigh, enemylow));


--[[
	text( 4,  217, string.format("Memory writes: %04d", testcount));
	if testcount > 1500 or testcount < 200 then
		cyclepos		= math.abs(15 - math.fmod(timer, 30)) / 15;
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0xFF));
		barcolor		= "#ff" .. warningboxcolor .. warningboxcolor;
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0x80) + 0x7F);
		barcolor2		= "#" .. warningboxcolor .. "0000";
		warningboxcolor	= string.format("%02X", math.floor(cyclepos * 0x40));
		barcolor3		= "#" .. warningboxcolor .. "0000";
--		barcolor3		= "#000000";
	else
		barcolor	= "#ffffff";
		barcolor2	= "#ff4444";
		barcolor3	= "#000000";
	end;
	lifebar(5, 226, 240, 2, testcount, 2500, barcolor, barcolor3, "#000000", barcolor2);

--]]

--	memory.writebyte(0x00e7, math.random(0, 0xFF));
--	memory.writebyte(0x00e8, math.random(0, 0xFF));
--	memory.writebyte(0x00e9, math.random(0, 0xFF));
--	memory.writebyte(0x00ea, math.random(0, 0xFF));
	
	testcount		= 0;

--[[
	marioxspeed2		= math.abs(marioxspeed);

	box(2, 217, 0x40 * 3 + 5, 226, "#000000");
	box(2, 218, 0x40 * 3 + 5, 225, "#000000");
	box(2, 219, 0x40 * 3 + 5, 224, "#000000");
	box(2, 220, 0x40 * 3 + 5, 223, "#000000");
	box(2, 221, 0x40 * 3 + 5, 222, "#000000");

	if marioxspeed2 > 0 then 
		for bl = 0, marioxspeed2 do
		
			if bl < 0x20 then
				segcolor	= string.format("#%02XFF00", math.floor(bl / 0x20 * 0xFF));
			else
				segcolor	= string.format("#FF%02X00", math.max(0, math.floor((0x40 - bl) / 0x20 * 0xFF)));
			end;

			box(bl * 3 + 3, 218, bl * 3 + 4, 225, segcolor);
--			line(bl * 3 + 4, 218, bl * 3 + 4, 225, segcolor);
		end;
	end;

	--]]

	maxspeed			= 0x40;
	marioxspeed			= memory.readbytesigned(0x0057);
	marioxspeed2		= math.abs(marioxspeed);


	text(maxspeed * 3 + 2, 221, string.format(" %2d", marioxspeed2));

	box(5, 221, maxspeed * 3 + 5, 230, "#000000");
	box(5, 222, maxspeed * 3 + 5, 229, "#000000");
	box(5, 223, maxspeed * 3 + 5, 228, "#000000");
	box(5, 224, maxspeed * 3 + 5, 227, "#000000");
	box(5, 225, maxspeed * 3 + 5, 226, "#000000");

	if marioxspeed2 > 0 then 
		for bl = 1, marioxspeed2 do
		
			pct		= bl / maxspeed;
			if pct < 0.50 then
				val		= math.floor(pct * 2 * 0xFF);
				segcolor	= string.format("#%02XFF00", val);

			elseif pct < 0.90 then
				val		= math.floor(0xFF - (pct - 0.5) * 100/40 * 0xFF);
				segcolor	= string.format("#FF%02X00", val);

			elseif bl < maxspeed then
				val	= math.floor((pct - 0.90) * 10 * 0xFF);
				segcolor	= string.format("#FF%02X%02X", val, val);

			else
				segcolor	= "#ffffff";
			end;

			yb			= math.max(math.min(3, (bl - 0x28)), 0);
			box(bl * 3 + 3, 225 - yb, bl * 3 + 4, 229, segcolor);
--			box(bl * 3 + 3, 218, bl * 3 + 4, 225, segcolor);
--			line(bl * 3 + 4, 218, bl * 3 + 4, 225, segcolor);
		end;
	end;

	FCEU.frameadvance();
end;

