This is the second draft of my Mega Man TAS. Its time is 1:48.226 or 7586 frames, 77 frames faster than the
first draft. I was planning to submit this version, but at the last minute I discovered a technique that can let you start moving 1 frame earlier at the beginning of some stages. I'm planning to rework the TAS to include the improvement; in the meantime consider this a WIP.
This is the slowed-down "soundhack" encode. For the full-speed version, input files, scripts, etc., see
https://archive.org/details/megamanpc-tas-1_48_226.
Link to video
My main motivation for doing a second draft was to machine-optimize certain tricky or tedious movements, such as jumps around corners, using a
Lua script (more on this below). In addition, I found a few other minor time saves:
- Kill the dog head-on in the intro stage. This requires you to pause for 1 frame in order to avoid colliding with the dog, but that's faster than having to turn around. The dog is unlike most (all?) other enemies in that it can only take damage once per frame, which means you have to space out your shots so that they land on different frames, which leaves less time to shoot all 8 necessary shots before the dog hits you.
- Avoid vertical camera recentering delays caused by jumping from a lower to a higher platform. When Mega Man lands on a higher platform, he becomes frozen in place for a few frames while the camera scrolls vertically to recenter on him. (There are a few special cases in the underwater section of Sonic Man's stage where you can avoid the vertical recentering, but only if you touch the higher platform for just 1 frame.) The recentering delay doesn't occur when jumping from high to low or to an equal height. This means that if a sequence of platforms goes high–low–high, it's better to jump over the middle platform, rather than down and back up, even if you never stop running. You can see the benefit by comparing the lava section of Dyna Man's stage in this draft and the first draft.
- Avoid horizontal camera recentering delays when exiting boss arenas. After each of the boss refights in the Wily stage, there is a delay while the camera recenters on Mega Man. The camera moves at 8 pixels per frame, which is equal to Mega Man's running speed. Therefore, contrary to intuition, there's no advantage to being closer to the exit door when the boss dies (as long as you are on the same half of the screen)—you have a shorter distance to run but the camera has a longer distance to scroll, and they cancel out. The exception to this is the camera's first frame of movement: if Mega Man's x position is not a multiple of 8, the camera's first movement will be less than 8, in order to align itself with Mega Man's position—costing 1 additional frame. You can avoid the extra frame by shuffling your feet to get the proper alignment before the camera starts moving. The best position to be in, after killing the boss, is aligned to a multiple of 8 pixels, moving towards the exit door at maximum speed—the specific x coordinate doesn't matter, as long as you are aligned and on the correct half of the screen. This technique only applies to the refights; the camera doesn't move after beating the bosses the first time.
- In the room with wall blasters in Sonic Man's stage, shoot the wall blaster and allow its explosion to damage you. This is slightly faster than waiting for the wall blaster to shoot a bullet at you.
- Against Dyna Man, use the Mega Buster until close enough to use the Force Field. The Buster does 2 HP of damage and the Force Field does 6, but you can't use the Force Field until you are close. The boss has 32 HP, so shooting him 4 times with the Buster saves 2 uses of the Force Field (2 frames). Using the Buster only matters in the first Dyna Man fight, because the only goal is to kill the boss as quickly as possible. In the refight, the goal is to kill the boss and cross the screen, so the Buster doesn't give an advantage because you are limited by Mega Man's running speed anyway.
- Use the Nuclear Detonator in the Sonic Man refight. The Nuclear Detonator is a slow, awkward weapon: it doesn't shoot very far and there is a delay of 16 frames before you can detonate it. It is Sonic Man's weakness, doing 16 HP of damage (out of 32), but I had discounted the possibility of using it because shooting the Nuclear Detonator twice is slower than just spamming the Buster. That is, until until I saw this video by Psychedelic Eyeball, which showed me that you can hit Sonic Man twice with the same shot.
- Take advantage of RNG manipulation. I reverse-engineered the AI of certain enemies including bosses. Volt Man's and Dyna Man's jumps are random: Volt Man's (x, y) velocity is random in ({4, 6}, {14, 16}); similarly Dyna Man's is random in ({4, 5, 6, 7}, {7, 9, 11, 13}). Against Volt Man, only the y velocity is important: you want a short jump because he becomes vulnerable only after he lands (saves 2 frames). Against Dyna Man, both components matter: you want him to jump toward you at maximum speed so that you can use the Force Field sooner, and not to jump so high that he's out of reach. The Raptorbot that I kill in Volt Man's stage and the Sentry Bee I kill in Dyna Man's stage are sacrifices to get favorable RNG against the bosses.
Regarding entertainment, I tried to make as many 1-ups drop as possible (1/64 chance) without losing time. I managed to get 9 1-ups to drop, out of 35 kills. I also attempted to humanize the run a bit by avoiding 1-frame shots (press and release Space on the same frame) except where needed.
| #2247 | first draft | diff | second draft | diff |
boot | 91 | 59 | −32 | 59 | 0 |
configuration menu | 3 | 2 | −1 | 2 | 0 |
title screen | 127 | 129 | +2 | 129 | 0 |
intro stage | 386 | 382 | −4 | 376 | −6 |
stage select SVD | 146 | 154 | +8 | 154 | 0 |
Sonic Man stage | 2040 | 1928 | −112 | 1902 | −26 |
Sonic Man | 111 | 102 | −9 | 100 | −2 |
stage select _VD | 352 | 367 | +15 | 368 | +1 |
Volt Man stage | 2125 | 186 | −1939 | 186 | 0 |
Volt Man | 109 | 100 | −9 | 98 | −2 |
stage select __D | 305 | 314 | +9 | 314 | 0 |
Dyna Man stage | 1988 | 1783 | −205 | 1768 | −15 |
Dyna Man | 77 | 57 | −20 | 52 | −5 |
Wily title cards | 363 | 374 | +11 | 374 | 0 |
Wily stage | 1734 | 1686 | −48 | 1666 | −20 |
Wily | 104 | 40 | −64 | 38 | −2 |
total | 10061 | 7663 | −2398 | 7586 | −77 |
The
optimization Lua script deserves special mention. A lot of the smooth movement in this TAS—just barely avoiding a bonk while going around corners—is due to computer optimization. The script takes as input a search space over sequences of keypresses, tests each one of them (repeatedly reloading an initial savestate), and outputs the one that gives the best result according to some metric function. Say, for example, you need to climb a ladder after running right through a corridor. You want to jump before grabbing the ladder because jumping is faster than climbing, except near the top of the jump where climbing is faster. If there's a low ceiling, you don't want to jump too early, or you'll bonk your head, but jumping too late wastes time. You may also need to stop moving right at some point, or you'll go past the ladder before reaching the full height of your jump. Here's a real example from the TAS:
-- Optimize jump and batcave exit ladder
-- Start at frame 2161, x=131.12
optimize(pattern(pad(12,
overlay(
concat(rep(0, 3, frame()), atom(KEY_J)), -- jump
concat(rep(4, 6, frame()), atom(KEY_RIGHT)), -- stop moving right
concat(rep(6, 10, frame()), atom(KEY_UP)) -- grab
)
)), metric_going_up)
What this means is, try waiting between 0 and 3 frames before jumping (
KEY_J). Try waiting 4 to 6 frames before releasing Right, and try waiting between 6 and 10 frames before pressing Up. (These bounds were found through manual experimentation.) Overlay these three input sequences so they happen concurrently. Pad all the inputs to a length of 12. Return the sequence that does the best according to the
metric_going_up function, which is
function metric_going_up()
return {-megaman_y(), -camera_y(), -megaman_dy()}
end
In this example, the script will test all 4×3×5=60 possibilities before outputting
-- best schedule is {{}, {36}, {}, {}, {}, {}, {205, 200}}
-- best score is {-563, -512, 0}
Which means: jump (keycode
36) on frame 2, and press Right (
205) and Up (
200) on frame 7; the best
y position you can get after 12 frames is 563.
I used this optimize script in 46 places. Some of the instances went through thousands and inputs and took a few hours to run, which accounts for the large number of rerecords. I found out about the horizontal camera recentering alignment issue when the script output some surprising sequences; the script figured out that you need to get your
x alignment right. The script needs
a patch to JPC-RR, or else it quickly deadlocks the whole process. A side effect of the patch is that you no longer get an instant screen refresh after loading a savestate, which is annoying but you get used to it 😄
In addition to the optimization script, I used a
HUD script that shows HP and hitboxes, with additional information off to the side. The game draws the most recent frame while already having computed the next frame, so seeing the hitbox overlay is a little like seeing 1 frame into the future. The HUD script shows the RNG sequence, which is how I knew when a 1-up was approaching.
The potential enhancement I mentioned at the beginning: Say a stage appears visually onscreen at frame
X. So you try frames
X and
X−1 and find that the game actually starts accepting input at frame
X−1. I had already done this in every stage. But it turns out that in some stages, you can actually start moving at frame
X−2, if you place the input later in the frame and not right at the beginning. So at frame
X−2 you might insert some padding inputs at the start of the frame before the input you want: Up Up Up Up Up Up Up Up Up Up Right, and sometimes this works.