Before reading I'm going to post the script that displays the CURRENT address object pointer loads object IDs from. It starts from $0B first frame after restart. It can be resetted if it gets $FF in the address it reads from. Note that the actual value it contains is what it reads from, but the object with this ID will be created NEXT FRAME. So you need to prepare the values you can affect. Okay.
The pointer jumps over $B (length of object descriptors line) addresses for searching IDs. It gets 0 as initial value, because the checkpoint that changes the game logics is still unchecked, and after restart at the RACE it contains the value that it GIVES to object pointer. Getting 0, object pointer measures 11 steps off and sticks to $0B. Then every frame it jumps through $16, $21, $2C, $37 and normally stops rolling, because it thinks it created enough objects to start the level (see how it works for normal levels).
$0B contains scene timer, or
something like that. Objects with that low ID get removed as there are no such objects.
Interesting thing here is that $16 contains TEMPORARY P2 input value, that gets read from $4017. After the frame ENDS it contains some different value it is actually used for. But reading objects occurs at the same time it contains whole P2 input! It will spawn any object equal to P2 input value.
Then it reads from $21 and I don't know what it is.
Second direct manipulation chanse is reading from $2C! It contains InputTap_P2 value, updates every frame.
The way Zlomus worked is:
1. Nothing.
2. Spawn object when pointer sticks to $16.
3. Nothing.
4. Reset the pointer when it reads from ;2C.
5. Repeat from 1.
Download BtObjects.luaLanguage: lua
-- feos, 2012
-- Object IDs and atributes display script for NES Batletoads.
-- Developed with 256x240 screen in mind.
lastVal = 0
function info()
CamX = memory.readbyte(0x87) + 256*memory.readbyte(0x88)
CamY = memory.readbyte(0x89) + 256*memory.readbyte(0x8A)
-- LevelCheckpointVal = memory.readbyte(0x585)
ObjectPointerLo = memory.readbyte(0xB7)
ObjectPointerHi = memory.readbyte(0xB8)
newVal = ObjectPointerHi*0x100+ObjectPointerLo
PointedAddr = memory.readbyte(lastVal)
-- AAA = memory.readbyte(0x2a)
CCC = memory.readbyte(0x2c)
-- gui.box(0,0,256,40,"#000000ff")
-- gui.text(130,231, "ChP: "..string.format("%02X",LevelCheckpointVal), "#00ff00ff")
gui.text(95,17, string.format("Cur:%04X=%02X\nNew:%04X",lastVal,PointedAddr,newVal), "#00ff00ff")
gui.text(200,17, string.format("2C=%02X",CCC), "#00ff00ff")
gui.text(1, 1, "slot:\nid:")
for i = 0, 14 do
id = memory.readbyte(0x3c1+i)
x = memory.readbyte(0x3FD+i) + 256*memory.readbytesigned(0x3EE +i) - CamX
y = memory.readbyte(0x493+i) - memory.readbyte(0x475+i)
xSH = memory.readbyte(0x484+i)
ySH = memory.readbyte(0x493+i)
move = memory.readbyte(0x4A2+i)
state = memory.readbyte(0x4B1+i)
hp = memory.readbyte(0x51a+i)
linked = memory.readbyte(0x529+i)
linker = memory.readbyte(0x538+i)
dthTmr = memory.readbyte(0x574+i)
follow = memory.readbyte(0x592+i)
if id>0 then
gui.text(x, y, string.format("%2X",id))
end
if x<0>242 then x=242 end
gui.text(1+i*16+18, 1, string.format("%2d\n%2X\n\n\n%2X",i+1,id,dthTmr))
-- gui.text(xSH,ySH,"Sh")
end
lastVal = newVal
end
emu.registerafter(info);
EDIT: Updated the post contents, also:
RNG in this game rolls all the time CPU is free from other calculation. It mixes multiple values over and over, making direct manipulation impossible. BUT it gets more or less time to roll if you change INPUT, so you can only do trial & error, watching at how addresses $25-$28 change. The way to catch when it READS from these addresses to decide the enemies or game behaviour is in some future plans, but I dunno how to do it.
EDIT2: Fixed the script again.