Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Still seems very consistent to me after testing. Jumping twice makes the peak of jumps to be only the difference between the y-subpixel values before each jump. You probably know what you're talking about though, just that the two of us most likely have very different ways to analyse a situation like this :)
Something new to me while testing this was that when not doing a full jump, the y-position is set to the start of the pixel where you released the "a" button plus 51 subpixels.
Demonstration: http://tasvideos.org/userfiles/info/8276427459843689
Best way to test this is jumping multiple times on the same platform without touching the ceiling. Try to jump for 1 frame around boss 2 start (without changing the other inputs).
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
The reason why it desyncs is this:
In your run at frame 7346 (the frame before you jump) your y-subpixel value is 108. If you do a jump at frame 7327 before this your y-subpixel value at frame 7346 will be 88. In your run this will result in a y peak position of 167.7 while the peak position in the other case naturally is 166.243, your position thus gets pushed to 167.51 while in the other it get pushed to 166.51 meaning that you'll reach position 180 (the one required for you to land) 1 frame earlier and thus it desyncs. Still can't see where those extra subpixels show up, other than if you simply look at this in a completely different way. I'm quite a lot more old-school than you for example :)
It's not ridiculous, it's the only sane way to substract 16-bit fixed-point numbers on a 8-bit platform.
Judging by the tracelog, 0x7B0-0x7BF stores fractional part of YSpeed, while 0x7C0-0x7CF store integer part of the YSpeed.
That's because "Y sub 2" is speed, not position.
I can verify that jumping at different times (on the same position for the same length) can make a different maximum jumping height. Jumping height also changes depending on previous jumping. This is shown in my demonstration.
Also note that the lua script doesn't shows the "correct" Y pos nor the Y sub position. This is because I can't calculate it. Since the only thing I found is the subroutine I added to the post, I can't really understand how would your description should match the debugged information even if I take away the Y sub 2 from the calculation (I mean that I'm not really sure about my theory either).
edit: the memory values again:
0x07C0 - the vertical boost, jumping makes it 4...3..2...
0x07B0 - Y sub 1, changes only when you jump. Values like -120, 35, 84...
0x0660 - Y sub 2 at least what I think. Changes everytime during the in-game frames. -102, 68, -17...
0x0640 - Y position. 180, 175, 171...
0x07B0 and 0x660 starts the very same pattern similar but in different direction. 660 starts with 51, -103, 50 while 7B0 starts with -51, -102, 103.
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Well just as Ans pointed out, one Y sub is for position (0x0660) and the other is for speed (0x07B0), which explains the misunderstanding. Think about that the position should be unsigned to make it easier to read.
local lastXpos = memory.readbyte(0x720) + memory.readbyte(0x730) * 0x100 + memory.readbyte(0x670)/400
local lastYpos = memory.readbyte(0x640) + memory.readbyte(0x660)/400
function Stuff()
local Xpos = memory.readbyte(0x720) + memory.readbyte(0x730) * 0x100 + memory.readbyte(0x670)/400
local Ypos = memory.readbyte(0x640) + memory.readbyte(0x660)/400
local Yspdtemp = memory.readbyte(0x7C0) + memory.readbyte(0x7B0)/400
local Xspd = Xpos - lastXpos
local Yspd = Ypos - lastYpos
local scrnX = memory.readbyte(0x650)
gui.text(scrnX-10, Ypos+10, string.format("%.2f\n%.2f",Xspd,Yspd), "#ffff00ff")
gui.text( 0, 8, string.format(
"X: %.2f\nY: %.2f",Xpos,Ypos
))
gui.text(150,8, string.format("Y speed?: %.2f", Yspdtemp))
lastXpos = Xpos
lastYpos = Ypos
end
emu.registerafter(Stuff)
This way it shows Scrooge's Y position changes in every frame. And needless to say, depeding on when he jumps makes his jump peak vastly different. I'm not sure that this would be the solution. edit: however it seems to be right. using cheats, 07B0 does increases the Y speed and freezing a value to 0660 (Y sub) will make the same jump height.
So the game really changes the Y subposition every frame? I don't understand the logic behind doing this.
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Yup, the value increases by 51 every frame which is the minimum y-speed when falling of a platform. For more details, read the subpixel paragraph in my Chip 'n' Dale submission. Replace 85 with 51 and 171 with 205. Thus, in this game, 205 is the best y-subpixel value to jump from if you're looking for the highest jump height.
Thank you AnS and Aglar for helping with figuring this out! And feos for telling me to write in to this thread. I wonder if it's possible to abuse this everchanging Y subposition other than highest jump / fastest fall down. Now that I write this sentence, maybe this is the reason your newer wip has scrooge humping the air after beating the 1st boss in Duck Tales?
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Nah, that's a common thing which I did already in 1991 I think. It sometimes happens when you have the cane jump animation after beating a stage, don't know what causes it.
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
Got to the first vine 1 frame faster here. In this and similar cases, test what exact pixel you need to be at in order to hit an enemy (vertically in this case), then try to position yourself so that you're as close to that pixel as possible at the frame before you would hit it.
EDIT: Ok, apparently the last part of the input wasn't recorded. Here is the full one. Notice that I didn't left the wall above the plant with the best x-subpixels in order to grab the vine with even better subpixels. This might make you either lose or gain time later on so try the other subpixel pattern as well.
I did that already (actually have 896.0 because I change subposition at wall). Currently I have to delay 5 frames at level 2 boss start to get the "fastest" hit for 1st time (so boss flies up a little bit and starts to move right and down). So I'm trying the other possible routes (level 1 - level 5 - level 2 for example). However sometimes this spawns extra lag frames.
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
I thought you might have, after having better understanding in the physics. Though position 896.128 is better since you'll grab the next vine 1 frame earlier (frame 913).
Somehow it should be possible to manipulate the bosses without losing time. In the moon level I remember being able to do so to the rat boss by playing the level up til that point a little different but always getting there at the same time. I suppose you can spot what variables the boss pattern depends on in the code?
I don't know if yours would be faster, but here's my latest WIP.
Also here's my modified lua script:
Language: lua
-- HUD (all functions except stuff()) by Pasky13
-- rest made by mostly feos and MESHUGGAH
local camx
lastXpos = 0
function findbit(p)
return 2 ^ (p - 1)
end
function hasbit(x, p)
return x % (p + p) >= p
end
local function hex(val)
val = string.format("%X",val)
if string.len(val) == 1 then
val = "0" .. val
end
return val
end
local function axis(x,y,color)
gui.line(x,y-16,x,y+16,color)
gui.line(x-16,y,x+16,y,color)
end
local function camera()
camx = memory.readbyte(0xF2) + (memory.readbyte(0xF3) * 256)
end
local function player()
local x = (memory.readbyte(0x720) + memory.readbyte(0x730) * 256) - camx
local y = memory.readbyte(0x640)
local offset = memory.readbytesigned(0x7D0)
local action = memory.readbyte(0x620)
if hasbit(action,findbit(3)) == true then
axis(x,y,"#FF0000")
else
axis(x,y,"#FFB000")
end
end
local function objects()
for i = 1,15,1 do
if memory.readbytesigned(0x700 + i) < 0 then
local x = (memory.readbyte(0x720 + i) + memory.readbyte(0x730 + i) * 256) - camx
local y = memory.readbyte(0x640 + i)
local fill = "#FF0000FF"
local outl = "#FF000030"
local xrad = 0
local yrad = 0
local enemy = false
local offset = 0
local etype = memory.readbytesigned(0x7D0 + i)
if etype < 0 then
if bit.band(etype,0x70) == 0 then
fill = "#FFFF00FF"
outl = "#FFFF0030"
else
enemy = true
end
offset = bit.band(etype,0x7F)
if enemy == true then
if memory.readbyte(0x620) == 3 then
offset = offset + 7
end
end
xrad = rom.readbyte(0x1F4C8 + offset)
yrad = rom.readbyte(0x1F4A8 + offset)
if xrad < 0x50 then
gui.box(x+xrad,y+yrad,x-xrad,y-yrad,outl,fill)
end
else
offset = memory.readbyte(0x610 + i)
xrad = rom.readbyte(0x18675 + offset)
offset = bit.band(memory.readbyte(0x7D0+i),0x7F)
yrad = rom.readbyte(0x1F4A8 + offset)
if xrad < 0x50 then
gui.box(x+xrad,y+yrad,x-xrad,y-yrad,outl,fill)
end
end
--gui.text(x,y,"E" .. hex(i) .. "/" .. hex(offset) .. "/" .. hex(xrad) .. "/" .. hex(yrad))
end
end
end
function Stuff()
camera()
player()
objects()
local Xpos = memory.readbyte(0x720) + memory.readbyte(0x730) * 0x100
local Xsub = memory.readbyte(0x670)
local Ypos = memory.readbyte(0x640)
local Ysub = memory.readbyte(0x660)
local Yspd = memory.readbyte(0x7C0) + memory.readbyte(0x7B0) / 1000
local Xspd = Xpos + (Xsub / 1000) - lastXpos
local bossHP = memory.readbyte(0xB1)
local bossInv = memory.readbyte(0x6FF)
local scrnX = memory.readbyte(0x650)
gui.text(scrnX-10, Ypos+10, string.format("%.3f\n%.3f",Xspd,Yspd), "#ffff00ff")
gui.text( 0, 8, string.format("X: %4d.%3d\nY: %4d.%3d",Xpos,Xsub,Ypos,Ysub))
gui.text(70, 8, string.format("Boss HP:%d\nBoss Inv:%d",bossHP,bossInv))
lastXpos = Xpos + Xsub / 1000
end
emu.registerafter(Stuff)
I'm gonna look at boss RNG now. I know that "killing enemies differently" can change boss 1 and boss 3 behaviour. Boss 2 (didn't started to debug yet) depends on in-game timer. So if I remove a lag frame (by not holding that arrow before the lag frame) will be the same as entering the boss fight 1 frame later (literally, saves 1 frame).
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 11/9/2007
Posts: 375
Location: Varberg, Sweden
I grab the second vine 1 frame faster here by that subpixel thing.
Yeah, that's a good old strat. It might work to advance quite a few ingame frames without losing time on the falling bridge, which I guess you've thought about already.
You are right, it's really faster by 1 frame. Not changing the X subpos and jumping from the flower to the bee gives a much better Y position on the 2nd vine. I didn't saw that the game doesn't changes the Y spd (only Y pos) when grabbing vines but talespin (the guy in the helicopter). However I'm still don't understand how could you made that very good jump with just 38 rerecords.
I would be glad if we could work as a team (and I'm okay with being a coauthor and you as an author).
edit: I've created the Wiki: GameResources/NES/DuckTales page for more recent things. Updating continuously
RNG is at $C2E6 subroutine
Just a few notes to reminder me and Aglar about the movie.
- Our WIP now really uses the hardest difficulty which needs an extra frame to select it. However we get 1 lag frame fewer (with trading) so if everything goes well, it will be the same length as the normal difficulty run.
- Level 3 boss "transforming to a ball until transforming back to hittable" is 212 frames if it's the fastest (214 if it's slower). RNG resync needed.
- Level 4 goat should be very similar to the non-hard movie (so lag frame should be traded again by removing "R" before that)
And we target for a final time of 07:05.48 (=length of normal difficulty) (not sure if 1 frame improvement by Aglar on level 4 will save extra frame(s)).
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
*necrobumping*
Found this post on gamefaqs from DDCecil. http://www.gamefaqs.com/boards/587253-disneys-ducktales/72263320
I tried doing this and it causes Scrooge to wrap around from the bottom of the screen back to the top. I think this could be useful in making the climbing screens quicker. Anyone want to play with it?
Also it doesn't appear to work in the sequel.
Joined: 1/6/2012
Posts: 585
Location: Azerbaijan, Baku
To visit secret bonus level, you need the amount of money you've got in the 10'000's a 7 (so 70'000, 170'000, 270'000 thousand etc.), then find Launchpad McQuack in a level
and agree to go back to Duckburg. He will drop you off at a secret bonus stage.
Link to video