So it was that time of the year again.
Great TASes Chosen Carefully
It was late in August 2017. A few months after all of our TAS content was declined from SGDQ 2017, dwangoAC was asked to submit TAS content for the next event: AGDQ 2018.
Of course, we can create the best entertaining TAS content if we have total control over the game, even if we don't turn it into technical showcases. So p4plus2, total and me worked a month on Donkey Kong Country 2 to find a stable exploit. A lot of late nights were spent doing reverse engineering and console testing. We seemed to get closer and closer to it, but it was already the time we had to finalize our content that was to be pitched.
In the 5 games we decided to submit, we had DKC2 and Kaizo Mario World 3, which I was asked to assist as well. These were all pretty entertaining TASes, but of course not all of the choices would be accepted, even if they specifically asked us to submit content.
Clearly they couldn't accept all the games.
Instead everything was declined.
Little Extras Accepted Eventually
After the second time all of our main content was declined, the spirit was broken. However, one single game, Super Monkey Ball, got in as a bonus run. We were contacted again and they told us that KMW3 almost made it in, but they decided against it in favor of the Super Dram World 2 speedrun.
After all, they can't possibly both make it into AGDQ, for whatever reason.
However, a SDW2 speedrun means we can get in as a bonus, even if all that work for DKC2 and the small preparations for KMW3 was now in vain. We still needed to make a pitch for the donation incentive, so I worked on a small playaround to serve as a first impression to get us accepted.
We were accepted this time.
Total Control Failed Horribly
SDW2 is just a ROM hack of Super Mario World, a really sync stable game, so reaching ACE should be no problem was what I thought when I started working on it at the start of December, a few weeks after the plans were set in stone.
The plan was to start the playaround after a 100% completion so we could access all levels at will. This was important because there is not much breaking you can do in this game. It was designed in a very minimalistic way to avoid accidentally increasing the difficulty even more.
So the task now was to do entertaining things while also finding a sync stable crash that could lead to controller registers, giving us total control.
Luckily there are a lot of ways to crash this game engine, so all I had to do was try the ones that are possible in this game. However, no matter which ones I tried, they didn't look promising. An important part of most total control runs is the memory setup, rather than the glitch. This game has so few elements that it was basically impossible to setup memory in any meaningful way.
All the time in December was spent finding a way to gain total control over this game.
There was no content for the actual playaround yet.
I took a break for Christmas and New Year.
Arbitrary Code Executed Flawlessly
January 3rd. It was 11 days before the showcase, there was barely any content finished and we had no way to gain total control. Yet on the first day the idea was to just use the SRAM for the memory setup. This was a possibility already considered before but unfortunately my focus was on getting to controller registers, not what to do afterwards.
Which was definitely the wrong state of mind to have.
There was no further problems with gaining total control and it was time for the content. Not enough time though.
How do I create entertaining content fast? I have the total control so what do I do next?
A common wrong idea people have is that breaking out of the rules of the game is hard. The reality is different. Taking over the console once you have arbitrary bytes, and thus creating new rules is easy. However, slightly tweaking the rules but letting them run in the background is the hard part. You can't change the ROM, so once you jump back to a routine, the whole set of instructions being executed next is set in stone.
To tweak the rules, they have to be rewritten. There was not enough time for complicated scripting. I couldn't make new sprites appear. I couldn't implement new physics.
The answer I came up with was to create a normal playaround, but to play hex editor at the same time. By using the bytes of the second, third and fourth controller I could, one byte per frame, change one memory address.
This was enough for a short playaround. Changing the powerup, some sprite properties, or even the score and coin count. Indeed, the most important part was the fact that it let me create content without having to pay attention to the difficulty of the game or the engine itself. Falling down without a way to recover? Change your y-velocity and start flying. Enemies in the way? Remove their fireproof property and turn to Fire Mario.
To create even more content, I had the idea of using already existing content. I thought of using YI2, the level played at AGDQ 2014. But precisely that level was overwritten in the ROM hack. Thankfully the subroom, the underground section, was still in it, and I could use a pipe to get there. But simply recycling everything was boring to me, so I wrote a simple subroutine to overwrite the CGRAM, the palettes, with grayscale colors. Simple, but effective.
This was the point where we first tested the movie on console, and thankfully it just worked. The SMW engine is stable after all.
Looking back on it, the next idea I implemented was not the best one. The part where Luigi appears and "saves" Mario from the crash was a fun little idea indeed, but it was a very sophisticated problem to implement it. The simple approach of just running the subroutines again obviously couldn't work, due to memory addresses being updated twice and several other side effects. It also takes up quite a bit of time during the frame, so there would be a lot of lag. Solving these problems wouldn't fit into the tight schedule. An easier approach was to just make the sprite appear, creating the illusion of something actually existing. This would require two 16x16 tiles to appear, meaning 8 bytes per frame. After setting the palette to fit Luigi, I made him appear and realized that there is not enough space for different animation states. I could change the position, but not the animation, so he would always look like whatever Mario looks like. The next problem was actually making him move. I quickly recreated SMW-like physics in my Lua script and made him at least move towards Mario and jump into the pipe at the end. But the scripting itself took a long time, and it didn't look as great as I had hoped. Him just disappearing after the pipe was inevitable.
So after creating the cutscene with Luigi, I realized the difficulty in keeping him on screen, so I just didn't think about it and continued with more content, as it was getting closer and closer to the deadline. So I kept going with creating simple content, just like before. I went into a pipe, thinking about changing a level, and since the bonus room is the 000 level (default) you get into, I had the idea of playing around in there as well.
While creating the content I had to keep in mind how much time I had left and how much I would need to do. The credits sequence was already a plan from the start. And I also had the idea of the bossfight I came up with when I was trying to sleep sometime when I implemented the Luigi sequence. So that would be at least 3 days of work right there. Two for the bossfight and one for the credits.
After finishing the bonus room I really wanted to play around in other levels, but the minimalistic design of the game made it hard to choose one. It would either end up being a level where I just needed to fly around a lot, or the limited set of items would make it boring. So having 3 days left I started the next part in my plan, the bossfight. I tried to make the final part something to remember.
I already had the setup to make a sprite appear from the Luigi part, so I just had to upload a Cappy sprite which I quickly drew. Scripting the Chainsaw to move around was simpler than I thought and I used the Luigi physics script from before to make it jump. Hitting the Reznors and making them disappear was easy with the hex editor mode, and, indeed, after 2 days, that part was done.
A single day left in my schedule and I started the credits part. The credits text is taken directly from ROM so I couldn't stop it from appearing. Indeed I didn't stop it. I overwrote it only a few frames after it appeared, and it looks flawless due to it being positioned offscreen. I wrote a quick script to generate the text for me given a string, and it worked.
For the first time since the new year I felt like I wasn't under time pressure anymore.
It wasn't over just yet, though. It still had to be tested on console. And it didn't work.
What didn't work? I have no idea. I analyzed the recording frame by frame and it seemed like the loading time was off by one frame, starting the room with a wrong global timer. With the hex editor I simply set it to a value already there on the console and it seemed to sync finally.