Post subject: Battle Kid: Fortress of Peril
Skilled player (1826)
Joined: 4/20/2005
Posts: 2161
Location: Norrköping, Sweden
Battle Kid: Fortress of Peril is actually a new NES platformer game in the making by Sivak games. As far as I understand, it will be finished sometime this year, and actual NES cartridges will be sold (for about 30$ I've heard). So all of you who still have a functioning NES console may soon enjoy a whole new game! Here is a youtube trailer for the game, and here is the (completely legal) public demo ROM for the game. I have even made a TAS of the demo here (no sound though). Here is the .fcm. This game looks promising, doesn't it?
Former player
Joined: 4/16/2004
Posts: 1276
Location: Uppsala, Sweden
Hah! Coolest thing I saw in a long time! Reminded me about Mega Man mostly, but also somewhat Metroid, Kid Icarus and Legacy of the Wizard Definately very cool! Thanks for the info!
/Walker Boh
Joined: 7/2/2007
Posts: 3960
...it looks like someone decided to actually port I Wanna Be The Guy to the NES. Yikes.
Pyrel - an open-source rewrite of the Angband roguelike game in Python.
Post subject: Re: Battle Kid: Fortress of Peril
Expert player (3630)
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Randil wrote:
I have even made a TAS of the demo here (no sound though). Here is the .fcm.
This seemed so fun that I had to give it a try myself:). Only fcm since I make terrible encodes.
feos wrote:
Only Aglar can improve this now.
Active player (434)
Joined: 9/27/2004
Posts: 650
Location: Canada
It's coming out in less than 12 days I think. There was a problem with the manual printing that's delayed the release. I've played a few beta builds and the demo and everything. A TAS would be lovely :)
Post subject: Re: Battle Kid: Fortress of Peril
Skilled player (1826)
Joined: 4/20/2005
Posts: 2161
Location: Norrköping, Sweden
Aglar wrote:
Randil wrote:
I have even made a TAS of the demo here (no sound though). Here is the .fcm.
This seemed so fun that I had to give it a try myself:). Only fcm since I make terrible encodes.
Very nice job! I knew my run could be improved, but not by this much. Perhaps I'll try and improve your attempt, but I couldn't spot any potential improvement.
Post subject: Battle Kid - Fortress of Peril
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
An awesome game that only was recently dumped, and needs a TAS. Because this game is for sale, I can't tell you where to find a rom for it, but I can tell you some info about the rom, in order to verify it. If you wish to get it legitimately and dump it yourself, you can buy it from here, other then that, google is your friend.
Name: Battle Kid - Fortress of Peril.nes
File size: 256KB
CRC: 74D23949
I worked out the games password system, and made a generator for it. http://caitsith2.com/nes/BK1PassGen.zip
Skilled player (1826)
Joined: 4/20/2005
Posts: 2161
Location: Norrköping, Sweden
Yeah, this game is great! There's an older thread here, and both me and Aglar made a WIP of the first level.
Active player (434)
Joined: 9/27/2004
Posts: 650
Location: Canada
Well, I guess I have two questions. Should the game be run on easy mode for the damage amp at the start, and should the invincibility glitch be used?
Editor, Skilled player (1438)
Joined: 3/31/2010
Posts: 2106
There is a damage glitch? Please elaborate
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
Invincibility glitch is only possible in version 1.0, and the rom that was dumped, was version 1.1. Really, the game should actually be run on unfair, and the gauntlet of evil needs to be run as well. Also, what needs running, are 3 specific passwords, PETUNIAX, SGCLEVEL, and CHAMPION, each of these being their own stages. Boss randomization is based on controller input, and therefore can be manipulated quite easily.
Active player (434)
Joined: 9/27/2004
Posts: 650
Location: Canada
scrimpeh wrote:
There is a damage glitch? Please elaborate
You pause the game at the same time you pick up an item, if memory serves, and you can't take damage afterwards. I think you may need to undo it before you fight the colormancer? I forget. But caitsith is correct, it was fixed for version 1.1 anyway.
Active player (434)
Joined: 9/27/2004
Posts: 650
Location: Canada
Hey, sorry, just thinking out loud about routes. The gauntlet of evil is optional, and key 4 is optional. Should they be skipped? I'm almost positive damage amp wouldn't save time since you get it so late and have to go so far out of the way. You need key 4 for the 'good' ending but does anyone care which ending is gotten? I know for sure you can skip the infinite oxygen item the second time (spoilers you lose it). If key 4 and damage amp is gotten, should it be skipped? I guess basically what I'm getting at is would people prefer a 100% or an any% run. Personally I was thinking any% easy mode would be best, but 100% unfair mode would be fine too.
Joined: 7/29/2004
Posts: 136
Location: Temple City, CA
I'd think that a run of the game, 100% on the hardest difficulty, would provide the most impressive and entertaining movie. I also agree that running some of the password accessed things, like PETUNIAX, would be very interesting.
"How can you prove you exist? Maybe we don't exist..." -Vivi Ornitier (Final Fantasy IX)
Active player (434)
Joined: 9/27/2004
Posts: 650
Location: Canada
A cursory glance at the maps says a 100% run should activate portals 2 and 4 to avoid backtracking. I think you start with portal 1, have to ckeck that. I'll draw up a for reals proposed route later but does anyone care if all portals are activated?
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
Portal 1 is already given to you automatically, whether or not it is visited from the start, so it can be skipped.
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
Working on a lua script, for displaying some useful info, like where hit boxes are. Green means the hitbox is okay to hit. Yellow means you should proceed through that hitbox with caution. Red means you end up dead by hitting it. The top number, if displayed, means HPs for that Hit box. The bottom number, if displayed, is a timer related to what that sprite does. Most of the bosses have specific handlers, to only show relevant info. For Cleo, The attack color being used is shown. (not everyone can tell red/green apart.) For Seahorse Turret, What turret will fire next, is shown by a yellow instead of a read hitbox, along with a counter counting number of frames left before it fires. (If you watch Sivak's latest video showing the challenge runs, in the commentary, he tells exactly how the next one is determined.)
-- Battle Kid: Fortress of Peril 
-- Written by CaitSith2
-- 03 April, 2011
-- Displays Hitboxes, Enemy HP, and various stats on screen

local function box(x1,y1,x2,y2,color)
	-- gui.text(50,50,x1..","..y1.." "..x2..","..y2);
	-- if (x1 > 0 and x1 < 255 and x2 > 0 and x2 < 255 and y1 > 0 and y1 < 241 and y2 > 0 and y2 < 241) then
		-- gui.drawbox(x1,y1,x2,y2,color);
		gui.drawline(x1,y1,x1,y2,color);
		gui.drawline(x1,y1,x2,y1,color);
		gui.drawline(x2,y1,x2,y2,color);
		gui.drawline(x1,y2,x2,y2,color);
	-- end;
end;
local function text(x,y,str)
	if (x > 0 and x < 255 and y > 0 and y < 240) then
		gui.text(x,y,str);
	end;
end;
local function pixel(x,y,color)
	if (x > 0 and x < 255 and y > 0 and y < 240) then
		gui.drawpixel(x,y,color);
	end;
end;

while (true) do
	local stuff = 0x0300; -- start of tile data, 4 bytes each, y, ?, ?, x. every tile appears to be 10x10px
	local timmy = 0x0031;		--V1.1
	local timmyshots = 0x0290;
	--local timmy = 0x0030;		--Demo and V1.0
	if(memory.readbyte(0x22) == 1) then
		timmy = 0x30;
	else
		timmy = 0x31;
	end;


	--spritetype is structured bitwise as follows, ffffbbdd
	--ffff = flags. 0001 = box backs off one color on sprites 3-15.
	--		    0010 = 
	--bb = box color. 00 = red, 01 = yellow, 10 = green, 11 = no box.
	--dd = display info. 00 = All info, 01 = no info, 10 = HP, 11 = Phase
      local spritetype = { 	0x00,0x00,0x00,0x00,0x00, 0x00,0x08,0x00,0x02,0x00, 
					0x09,0x00,0x04,0x00,0x01, 0x00,0x0A,0x00,0x08,0x00, 
					0x00,0x00,0x00,0x00,0x02, 0x00,0x08,0x00,0x00,0x00, 
					0x41,0x00,0x05,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x04,0x00, 

					0x14,0x00,0x08,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x02,0x00,0x00, 0x00,0x0D,0x00,0x02,0x00, 
					0x41,0x00,0x01,0x00,0x00, 0x00,0x20,0x00,0x03,0x00, 
					0x40,0x00,0x42,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x08,0x00,0x41,0x00,0x00, 0x00,0x41,0x00,0x08,0x00, 

					0x01,0x00,0x09,0x00,0x00, 0x00,0x41,0x00,0x00,0x00, 
					0x00,0x00,0x08,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x08,0x00,0x01,0x00,0x01, 0x00,0x05,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 

					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,
 
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,
					0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,

					0x00,0x00,0x00,0x00,0x00, 0x00,
	};
	-- print boxes for all the tiles
	-- invalid tiles are automatically hidden because their x/y coords are out of range, i guess
	x1 = memory.readbyte(timmy);
	x2 = memory.readbyte(timmy+1);
	y1 = memory.readbyte(timmy+2) + 24;
	y2 = memory.readbyte(timmy+3) + 24;
	hp = memory.readbyte(0x20);
	if(hp == 0) then
		hp = 1;
	else
		hp = 0;
	end;
	text(x1+3,y1+3,hp);
	box(x1,y1,x2,y2,"green");
	for i=0,2 do
		timmyshotexists = memory.readbyte(timmyshots+7+(i*8))
		x1 = memory.readbyte(timmyshots+1+(i*8));
		x2 = memory.readbyte(timmyshots+2+(i*8));
		y1 = memory.readbyte(timmyshots+3+(i*8)) + 24;
		y2 = memory.readbyte(timmyshots+4+(i*8)) + 24;
		if(timmyshotexists ~= 0xF8) then
			box(x1,y1,x2,y2,"green");
		end;
	end;
	for i=0,15 do
		stype = memory.readbyte(stuff+(i*16)) + 1;
		spritedisplay = bit.band(spritetype[stype],3);
		spritebox = bit.band(spritetype[stype],12)
		spriteflags = bit.band(spritetype[stype],240);
		spriteexists = memory.readbyte(stuff+8+(i*16));
		if (bit.band(spriteflags,0x10) == 0x10) then
			if (i >= 3) then
				spritebox = spritebox - 4;
				if (spritebox < 0) then
					spritebox = 12;
				end;
			end;
		end;
		if (bit.band(spriteflags,0x20) == 0x20) then
			if (i >= 3) then
				spritedisplay = 1;
			end;
		end;
		if (stype == (70+1)) then	--Handle OwlBot
			if (i == 4) then
				spritedisplay = 0;
			end;
		end;
		if (stype == (116 + 1)) then	--Handle Cleo
			cleo = memory.readbyte(stuff + 15);
			if(cleo == 0) then
				text(128,8,"Red")
			end;
			if(cleo == 1) then
				text(128,8,"Blue")
			end;
			if(cleo == 2) then
				text(128,8,"Purple")
			end;
			if(cleo == 3) then
				text(128,8,"Pink")
			end;
			if(cleo == 4) then
				text(128,8,"Yellow")
			end;
			if(cleo == 5) then
				text(128,8,"Green")
			end;
		end;
		if (stype == (96 + 1)) then	--Handle Amethyst Angel
			if(i == 0) then
				spritedisplay = 0
			end;
			if(i == 1) then
				spritebox = 12;
			end;
		end;
		if (stype == (100 + 1)) then	--Handle Zedd
			if(i == 0) then
				spritedisplay = 0
			end;
		end;
		if (stype == (106 + 1)) then	--Handle TY-524A4ET
			if(i == 0) then
				spritedisplay = 0
			end;
		end;
		if (stype == (80+1)) then	--Handle nagaconda displaying specifically.
			nagaconda = memory.readbyte(stuff+10+(i*16))
			if (i == 1) then
				spritebox = 12;
			end;
			if((i >= 2)) then
				spritedisplay = 1;
			end;
			if (i == 5) then
				spritedisplay = 3;
			end;
			if (i == 12) then
				if (nagaconda == 3) then
					spritedisplay = 2;
				end;
			end;
		end;
		if (stype == (92+1)) then	--Handle Seahorse Turret.
			seahorsenext = {2,3,1,0,0}
			seahorsefire = memory.readbyte(stuff+13)
			seahorsetimer = memory.readbyte(stuff+12)
			seahorsebullets = memory.readbyte(stuff+14)
			j = 0;
			if(seahorsebullets == 0) then
				seahorsefire = 3;
			end;
			while((j < 4) and (seahorsefire < 4)) do
				hp = memory.readbyte(stuff+5+((seahorsenext[seahorsefire+1]+1)*16))
				--text(128,24+(8*seahorsefire),hp);
				if (memory.readbyte(stuff+5+((seahorsenext[seahorsefire+1]+1)*16))==0) then
					seahorsefire = seahorsefire - 1;
					if(seahorsefire < 0) then
						seahorsefire = 3;
					end;
				else
					break;
				end;
				j = j + 1;
			end;
			if(seahorsefire < 4) then	--Check that there is at least one seahorse turret still alive.
				text(128,8,seahorsebullets/4);
				--text(128,8,seahorsefire);
				--text(128,16,seahorsenext[seahorsefire+1]);
				if ((i >= 1) and (i <= 5)) then
					spritedisplay = 2;
				end;
				if ((i-1) == (seahorsenext[seahorsefire+1])) then
					spritebox = 4;
					x1 = memory.readbyte(stuff+2+(i*16));
					y1 = memory.readbyte(stuff+4+(i*16)) + 24;
					text(x1,y1+10,seahorsetimer);
				else
					spritebox = 0;
				end;
			end;
		end;		


		if((spriteexists < 248) or (i < 3)) then
			if ((spriteexists == 248) and (i < 3)) then
				spriteexists = memory.readbyte(stuff+7+(i*16));
			end;
			if((spriteexists < 248)) then
				x1 = memory.readbyte(stuff+2+(i*16));
				x2 = memory.readbyte(stuff+3+(i*16));
				y1 = memory.readbyte(stuff+4+(i*16)) + 24;
				y2 = memory.readbyte(stuff+5+(i*16)) + 24;
				-- if((stype ~= (30+1)) and (stype ~= (80+1)) and (stype ~= (82+1))) then
				if(bit.band(spriteflags,0x40) ~= 0x40) then
					if (x2 <= x1) then
						x2 = x1 + 15;
					end;
					if (y2 <= x1) then
						y2 = y1 + 15;
					end;
				end;
				
				--text(1,8+(i*8),stype-1);	--Debug info on sprite identifier.
				--text(26, 8+(i*8),spritedisplay)
				--text(34, 8+(i*8),spritebox)
				
				if (stype == (22+1)) then --Blinking spike room, display the phase counters in a visible spot.
					phase = memory.readbyte(stuff+12+(i*16));
					text(128,8,phase);
					phase = memory.readbyte(stuff+13+(i*16));
					text(128,16,phase);
				end;
				
				
				if ((spritedisplay == 0) or (spritedisplay == 2)) then
					hp = memory.readbyte(stuff+6+(i*16));
					if (hp > 0) then
						text(x1+1,y1+1,hp);
					end;
				end;
				if ((spritedisplay == 0) or (spritedisplay == 3)) then
					if(stype == (74+1)) then	--Colormancers use 14/12 for their attack counters.
						phase = memory.readbyte(stuff+14+(i*16))
					else 
						phase = memory.readbyte(stuff+12+(i*16));
					end;
					if(phase > 0) then
						text(x1,y1+8,phase);
					end;
					if(phase == 0) then
						if(stype == (74+1)) then
							phase = memory.readbyte(stuff+12+(i*16))
						else 
							phase = memory.readbyte(stuff+13+(i*16));
						end;
						if(phase > 0) then
							text(x1,y1+8,phase);
						end;
					end;
				end;
				if(spritebox == 0) then
					box(x1,y1,x2,y2,"red");
				end;
				if(spritebox == 4) then
					box(x1,y1,x2,y2,"yellow");
				end;
				if(spritebox == 8) then
					box(x1,y1,x2,y2,"green");
				end;
			end;
		end;
	end;
	
	FCEU.frameadvance();
end;
Edit: Barring any bugs, the script should be complete. Edit: Now shows hit boxes of timmy's shots. Also detects Demo from Version 1.1. Should work on Version 1.0, but can't confirm that, since there isn't a dump of 1.0 out there.
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
Did a test run of SGCLEVEL. Also edited the above post, hopefully for the last time. I think I picked the wrong value to check, for determining the version of the game. (Which only really matters for the first 0x200 bytes in ram, where Timmy's Hit box is. Timmy's Shots, and all other hitbox data is in the same location between versions.) Edit: Also did a test run of PETUNIAX. Edit: Encoded PETUNIAX and SGCLEVEL Edit: Improved on both of my runs, PETUNIAX fm2, encode, and SGCLEVEL fm2,encode
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
There, did a full unfair difficulty 100% run, meaning nothing was skipped. fm2. The segment from the base/first save room, all the way to pwning the Lotus Guardian boss, was inserted from Agler's Demo TAS. This is improvable, for example, I should of waited to get Portal 2. An any% run can skip collecting Portal 3, Key 4, Damage Amp, and Inf O2 in the final Area. On the other hand, Having ALL 5 portals in a 100% run is good, because every single one of them is used in one way or another, to save time.
Active player (434)
Joined: 9/27/2004
Posts: 650
Location: Canada
I can't download the fm2 for some reason. I'm getting a 406 error.
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
Didn't realize the % sign caused problems with my host. updated the filename server side, and updated the fm2 link accordingly. Edit: Encoded
Joined: 7/29/2004
Posts: 136
Location: Temple City, CA
Okay. Those were all absolutely great to watch. I knew there were some speedy ways to get through some of the rooms, but several times my jaw was just on the floor. Can't wait to see your actual run if your tests are that good.
"How can you prove you exist? Maybe we don't exist..." -Vivi Ornitier (Final Fantasy IX)
caitsith2
He/Him
Player (47)
Joined: 3/26/2004
Posts: 194
Here is one more TAS for you, this time, the CHAMPION Boss Rush mode. fm2, encode The Boss fights with OwlBot, Cleo, Seahorse Turret, Amethyst Angel, Zedd, and Supermech TY-52414ET, all have random elements. The good news, is that this randomness is fairly easy to manipulate with controller input. For OwlBot, when he opens hatches to let birds fly, you want him to open the bottom one, not the top one. Fpr Cleo, A good color to Manipulate, is Blue or Purple. (She never repeats attacks, and since she starts out Green, she will never open with Green.) For Seahorse Turret, I just deal with it. The rounds involve 4 slow waves, and 1 fast one, repeated. For the First wave, Top Left Turret always fires first, and they always fire in a counter clockwise direction. After All 4 turrets have fired, the next one to fire, is randomly chosen. Of course, I pwn each of them so fast, that there isn't much of a need to deal with the randomness there. For Amethyst Angel, You want her to always be on the Ground, never in the air, and you always want here to drop her crystals In ALL at once fashion, rather than single file fashion. (There are 3 groups of Crystals.). If for some reason, you can't get her to stay on the Ground, then for the in air attack, you want the Single file flying crystals. She never stays in the same spot, but she can repeat attack type, if she stays on the same level as previous attack. For Zedd, You want his teleport attack, and Zedd is able to repeat attacks. For Supermech, he has 5 attacks. He never repeats last attack, and he will never open with the Mass Laser attack. The two attacks you do want, are is bullets, and his electric balls, exclusively. And on a side note, there is a programming error at OwlBot that Sivak has known about in V1.0, but never bothered to fix for V1.1. This error is the overhead laser being off center, when it approaches from the left.
Editor, Skilled player (1438)
Joined: 3/31/2010
Posts: 2106
Let me say, I really appreciate the effort you put into these runs, by all means, keep going! I'd love to see a submission
Expert player (3630)
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Well, because of the "trivial" controls of this game and no lag (?) I thought it wouldn't hurt spending some time with this again. Having never really played this before I just took the same route as caitsith2. I started from scratch and after the first boss I was 100 frames ahead of my own gameplay in caitsith2's test run, 46 frames faster boss fight was nice. I stopped after the first appearing/disappearing-block area and don't know if I'll continue since I was kind of disappointed that I couldn't cut away more of the waiting periods there. http://dehacked.2y.net/microstorage.php/info/338032627/Battle%20Kid%20-%20Fortress%20of%20Peril_TESTWIP.fm2
feos wrote:
Only Aglar can improve this now.