Post subject: Parodius: Non-Sense Fantasy
MarbleousDave
He/Him
Player (12)
Joined: 9/12/2009
Posts: 1555
This game is described as Gradius on acid. A TAS on Level 8 will be amazing. Especially the second loop. Also, the Lollipop stage is essentially a score attack. We could go for a max score possible. Like get 46 extra lives through bells, and 51 through score. And get a score of 5,030,000 before applying the bonuses to get 9,999,999.
Joined: 5/15/2014
Posts: 4
Location: San Diego, United States
I started working on a playaround run, and I'm just past the first must-kill boss. I need to optimize that fight and the menu, but I wanted to get feedback on the entertainment approach. I'm not so interested in optimizing it until it's complete, because it'd be a good exercise in editing with TAStudio. Link to video Anyway, since this sort of thing has been done (and started with Gradius), I want to try something different with Parodius as well: writing an AI script that achieves the goals of my manual attempt without any human guidance. The only problem is finding the RAM addresses that the script needs to understand state. I could really use help on this, because it's not nearly as simple as one might expect. This is all I have so far:
Input Bitfield: 000026
System clock: 00003A
Stage Clock: 000078
I can't even find my character's position on screen. Any tips or links to a discussion on SNES analysis would be greatly appreciated.
[img url=i.imgur.com/8vCpFuH.gif][/img]
Patashu
He/Him
Joined: 10/2/2005
Posts: 4017
Just FYI, in the Parodius games the rank goes up faster if you're more upgraded, so it's worth considering getting all the upgrades you can ASAP even if you're doing a pacifist style TAS.
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Joined: 5/15/2014
Posts: 4
Location: San Diego, United States
Thanks! Anyway, I just found out that the fruity pebble heads only drop a candy when you kill all of them. Good to know. Also, I think I've narrowed horizontal position to 3 addresses: 0001E0 0001E4 0001E8 It's also possible that position on-screen is stored in two 16-bit ints, but probably within those words? Will investigate later.
[img url=i.imgur.com/8vCpFuH.gif][/img]
Editor, Player (53)
Joined: 12/25/2004
Posts: 634
Location: Aguascalientes, Mexico
The coordinates of your ship seems to be located at these addresses:
7E0B88  X coord
7E0B8C  Y coord

7E0BD8  X coord
7E0BDC  Y coord
Both have the same value as far as I could tell while testing. So either one should work. Note that for Bizhawk instead of starting with 7E, you start with 00. You can try drawing a line in LUA using those values. Then maybe add a box to simulate collision box... Edit: Ok I was playing with a LUA script and there seem to be some correspondence with the values having a separation of 0x50. Still not sure why both of those I mentioned before do the same, but if you keep adding 0x50, you'll get the position for your Options (those little helpers), the enemy position and even some other graphical positions... (I believe there's an address that classify which type they are, but haven't looked for that yet)
I'm the best in the Universe! Remember that!
Joined: 5/15/2014
Posts: 4
Location: San Diego, United States
Oh wow this is excellent. That means we have a consistent object table. I was afraid the heap would be played fast and loose with enemy spawning, but of course with that hardware you have to be meticulous to fit all your stuff! Miscellaneous notes: 0x0001F0 - 0x0003DE are stagnant with an alternating pattern before enemies spawn, which suggests they involve enemies as well. Could be a buffer for the graphics renderer, I dunno. 0x000BA4 looks like an animation counter or something. I'll try to formalize the object table tomorrow.
[img url=i.imgur.com/8vCpFuH.gif][/img]
Editor, Player (53)
Joined: 12/25/2004
Posts: 634
Location: Aguascalientes, Mexico
Here are some addresses that might help you:
7E008A XX           Stage
7E008C XX           Scene
7E0098 XX           Current Lives
7E1C64 XX           Speed Up    (Up to 6)
7E1C66 XX           Missile (2nd Ability)
7E1C68 XX           Options     (Up to 4)
7E1C6A XX           Shield  (last Ability)
7E1C6C XX           Power Up lv (Up to 7)
7E1C6E XX           Invincibility (Huge ship)
7E1C70 XX           Double/Laser power
7E1F48 XXXXXXXX     Score (In hex w/ dec numbers)
Found a few more, but I don't think they'll help you out (like menu settings, ship, etc.)
I'm the best in the Universe! Remember that!
Joined: 5/15/2014
Posts: 4
Location: San Diego, United States
I have some code here that I've been trying to make work, but I'm not sure what's going wrong. I watched an x-y pair that I'm almost certain is picked up by readbyte() in the for loop, just to make sure something has canvas coordinates. Yet drawRectangle isn't giving me anything with the inputs I give it. I'd love to hear any suggestions, since coming from Python, I see Lua as some kind of special needs child that I have to work with. gui.drawRectangle(100,100,5,5), if put inside highlightSprites(), gives a very apparent white square.
Language: lua

--not used function map(f, arr) for index,value in pairs(arr) do result[index] = f(index,value) end return result end meX = 0 meY = 0 --canShoot = false function boxSprite(name,position) gui.drawRectangle(position[0],position[1],5,5) end --dudewat no colons this is spooky where's muh python function highlightSprites() --probably not bytes, will determine the correct read when I get home and have the game in front of me meX = memory.readbyte(0x000B88) meY = memory.readbyte(0x000B8C) --seriously this language is like made of marshmallows --this assignment is index-explicit for a reason --because {meX,meY}[0] == nil --like wow positionTable = {} positionTable[0] = {} positionTable[0][0] = meX positionTable[0][1] = meY top = 0x000BD8+25*0x50 --not sure how big the object table is but on frame 3737 for i = 0x000BD8, top, 0x50 do positionTable[i] = {[0] = 0,[1] = 0} positionTable[i][0] = memory.readbyte(i) positionTable[i][1] = memory.readbyte(i+2) end --map(boxSprite,positionTable) i = 0 while(i ~= nil) do gui.drawRectangle(positionTable[i][0],positionTable[i][1],5,5) i = next(positionTable,i) end end --emu.registerbefore(highlightSprites) while true do emu.frameadvance() highlightSprites() end
[img url=i.imgur.com/8vCpFuH.gif][/img]
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Language: lua

function map(f, arr) for index,value in pairs(arr) do result[index] = f(index,value) end return result end
The variable result is not defined anywhere in your code. Lua looks for a global result and can only find nil, and indexing nil should instead throw an error, generally putting your code to a screeching halt. Check the output on the lua console. This would happen even if the function given to map returns nothing. Adding "local result = {}" somewhere in there, preferably inside the function before the for loop, might fix things. This would make result into a table which can be indexed, and the lua will use that local result you put in the function. Outside of that, I can't spot anything else that would break it. This would be my approach when trying to paint boxes, at least to start:
Language: lua

local function ShowSprites() gui.drawRectangle(memory.readbyte(0x000B88),memory.readbyte(0x000B8C),5,5) for i= 0, 25 do local addr= 0x000BD8 + i*0x50 gui.drawRectangle( memory.readbyte(addr ), --X memory.readbyte(addr+ 2), --Y 5, 5 ) end end
... I'm just making this analysis with total blindness to the game itself. I'm only looking at the code you produced and making my own interpretations on what it means. Hope it helps.
Editor, Player (53)
Joined: 12/25/2004
Posts: 634
Location: Aguascalientes, Mexico
scantics, LUA indexes tables normally 1 based (Starts on index 1), if you want to use it as 0 bases then you have to explicit declare it (as you did, but didn't understood why). Unless there's an specific need to have them start at 0, I would avoid it all together. I was able to run your script and it didn't thrown any error. However the coordinates for the enemies were off... Anyway, when I was testing the position addresses I made a LUA script. Though it was for Snes9x, I adapted it now for Bizhawk and here it is. It has the same style as the one I have for Final Fantasy V, where I save first the addresses in a table and then call them, sure you could reduce some code but I think this is very legible:
Language: LUA

-- -------------------------------------- -- -- Parodius; Non-Sense Fantasy LUA script -- -- by: samurai goroh -- -- mail: samuraigoroh@gmail.com -- -- -------------------------------------- -- -- ------- -- -- GLOBALS -- -- ------- -- wwm = 0 -- windows width min wwM = 255 -- windows width MAX whm = 0 -- windows height min whM = 224 -- windows height MAX -------------------------------------------------------------------------------- function drawAxis(x, y, color) -- Draws an Axis around ships -------------------------------------------------------------------------------- gui.drawLine ( x, whm, x, whM, color) gui.drawLine (wwm, y, wwM, y, color) end -------------------------------------------------------------------------------- function drawCollision(x, y, color) -- Draws a box around ships -------------------------------------------------------------------------------- gui.drawBox (x-6, y-6, x+6, y+6, color) end -------------------------------------------------------------------------------- function EnemyInfo(Enemy) -- ENEMY addresses goes here -------------------------------------------------------------------------------- local enemy = Enemy - 1 local enemyTable = {} enemyTable = { sprite = memory.readbyte(0x000D61 + enemy*(0x50)), x = memory.readbyte(0x000D68 + enemy*(0x50)), y = memory.readbyte(0x000D6C + enemy*(0x50)), hp = memory.readbyte(0x000D76 + enemy*(0x50)), } return enemyTable end -------------------------------------------------------------------------------- local function enemy_data() -- ENEMY code goes here -------------------------------------------------------------------------------- local enemy = {} -- Holds the enemy's information local x,y = 0,0 -- Coord X,Y local hp = 0 -- HP?! local sprite = 0 -- Sprite?! local EnemiesMax = 64 -- Max number of enemies in screen (not sure what's the limit but 64 sounds about right...) for i=1, EnemiesMax do enemy[i] = EnemyInfo(i) -- Populate the enemy's information x = enemy[i].x y = enemy[i].y hp = enemy[i].hp sprite = enemy[i].sprite if (hp ~= 0) and (sprite ~= 0) then --drawAxis(x, y, 0xFFFF4000) drawCollision( x, y, 0xFFFF4000) end end end -------------------------------------------------------------------------------- function AllyInfo(Enemy) -- ALLY addresses goes here -------------------------------------------------------------------------------- local enemy = Enemy - 1 local enemyTable = {} enemyTable = { x = memory.readbyte(0x000BD8 + enemy*(0x50)), y = memory.readbyte(0x000BDC + enemy*(0x50)), hp = memory.readbyte(0x000BE6 + enemy*(0x50)), } return enemyTable end -------------------------------------------------------------------------------- local function character_data() -- ALLY code goes here -------------------------------------------------------------------------------- local ally = {} -- Holds the character's information local x,y = 0,0 -- Coord X,Y local hp = 0 -- HP?! local AllyMax = 5 -- Max number of allies in screen (Ship + 4 Options) for i=1, AllyMax do ally[i] = AllyInfo(i) -- Populate the ally's information x = ally[i].x y = ally[i].y hp = ally[i].hp --if x > 0 then drawAxis( x, y, 0xFF0040FF) drawCollision( x, y, 0xFF0080FF) --end end end -------------------------------------------------------------------------------- function dumping() -- Dumps some addresses, to analyze manually. Easy to paste on Excel -------------------------------------------------------------------------------- io.output("Parodius.txt") address = 0x000B80 size = 80 iter = 64 for i=0, size do row = "" for j=0, iter do x = memory.readbyte( address + i + (j*size) ) row = row .. string.format( "%2X ", x ) -- Uses a tab to split text end io.write( row .. "\n" ) end io.close() print ("dumped file") end -------------------------------------------------------------------------------- function main() -- Contains code that will be continiously running -------------------------------------------------------------------------------- character_data() enemy_data() end -------------------------------------------------------------------------------- --****************************************************************************-- --dumping() while true do main() emu.frameadvance() end --****************************************************************************--
I'm the best in the Universe! Remember that!