Submission #6780: scrimpeh's NES Castlevania III: Dracula's Curse "Grant path, warp glitch" in 14:29.42

Nintendo Entertainment System
Grant path, warp glitch
(Submitted: Grant path, warp glitch)
(Submitted: Castlevania III - Dracula's Curse (U).nes USA)
BizHawk 2.3.0
52251
60.0988138974405
39198
Unknown
Submitted by scrimpeh on 6/14/2020 4:51 PM
Submission Comments
Castlevania III is a gothic horror themed platformer by Konami for the NES. This movie obsoletes the current wrong warp run on the U version by 3161 frames, or about 52.6 seconds through new tricks and strategies.

Game objectives

  • Emulator used: BizHawk 2.3.0, NESHawk Core
  • Takes Damage to Save Time
  • Uses Death and Game Over to Save Time
  • Manipulates Luck
  • Genre: Platform

Comments

Castlevania III is a tie-in game by Konami for the hit animated television series of the same name. This run uses a secret bonus character who was likely included by the developers as an easter egg. He seems to have been kinda rushed and not very well implemented as there is a large warp glitch that allows bypassing most of the game with him. This run saves 3161 frames, or about 52.6 seconds over the previous run by arandomgameTASer, Samsara and Mothrayas through new tricks and better strategies. Like the previous run, this run uses the USA version of the game as it facilitates the warp glitch. There was also a new trick found by the RTA community involving Grant which is exclusive to the U version, which saves several seconds over the course of this run. There is a number of significant differences between U and J. Most importantly, instead of Grant's throwing dagger, he only has a stabbing dagger with a tiny range. It is still a fairly convenient attack due to its low start lag and fast attack speed. In addition, the game has been made substantially harder in a number of ways, especially Dracula himself. Details are outlined in the comments below.

Tricks

Subweapon Cancelling

By pressing the attack button on the right frame, the throwing animation for projectile attacks can be skipped. This works by pressing the attack button...
  • ...immediately after crouching (Grant's Throwing Dagger (J), Alucard's Fireballs)
  • ...on the frame you're walking off a ledge
  • ...on the frame you're landing on the ground
  • ...when dismounting stairs as the camera scrolls vertically

Skipping Forced Crouch

By attacking on the right frame in midair, you can skip the forced crouch animation when landing from a high ledge

Vertical Screen Wrapping (New!)

This trick was discovered by KutsuShita. If you're in midair and attack with Grant's dagger, approaching a wall and letting go of Right immediately as you touch the wall will zip Grant upwards by 128 pixels with a lot of vertical momentum. This can be used to instantly go to the other half of the screen or do huge vertical leaps. If you are on the top of the screen, you wind up at the bottom, if you are on the bottom of the screen, you wind up at the top. It only works facing right. If you do it at the wrong position, Grant may up below the screen and die. This trick is the reason why I wanted to do the run.

Entering Ceilings

Jumping into a ceiling will add 32 subpixels to Grant's horizontal possition, irrespective of any walls in his way. This can be used to clip into walls by jumping into an inner corner several times. It is not possible to move right up to a wall to do it in one jump because Grant will try to cling to the wall. Instead, with optimal subpixel positioning, 5 jumps are required to get into a ceiling.

Horizontal Screen Wrapping (New!)

At the left or right edge of the screen, Grant can interact with walls on the other side. This allows him to wrap the screen horizontally by pulling himself up a corner. This is normally not useful because it's hard to get to the edge of the screen quickly, but some exceptions exist.

The Warp

This is the big one. By dismounting from the stairs in BLK-4-01 and walking through the glitched worlds, it is possible to wrong warp to the final stage, saving a huge amount of time. During the course of the making of this run, I tried to figure out the wrong warp to understand what is going on. While I didn't discover any of this, here is a write up detailing its workings. The wrong warp is effectively an errant write, writing an unexpected value to an unexpected location in memory. Get ready for some dry, dry, theory.

Where to Write

This is the simpler part. The culprit code is located at $28132 (loaded in at $8132 during runtime) and is executed whenever a room is loaded or the camera crosses a 64 pixel boundary. The code looks like this:
LDY #$00   
LDA ($00),Y
STA $07C2,X
BEQ $8178  
INY        
LDA ($00),Y
CLC        
ADC $09    
STA $07DA,X
LDA $0A    
ADC #$00   
AND #$01   
STA $07E0,X
INY        
LDA ($00),Y
STA $07D4,X
INY        
LDA ($00),Y
STA $07E6,X
INY        
LDA ($00),Y
STA $07CE,X
...

Effectively, a pointer to a 5-byte struct is dereferenced and the component bytes are written to $7C2,X, $7DA,X, $7D4,X, $7E6,X and $7CE,X, respectively. Either $0 or $1 is also written to $7E0,X for good measure. Ordinarily, the data is somehow related to the enemies, and X is supposed to be between 0 and 5, writing to a number of 6 byte tables in RAM. The value for X is loaded from a table located at $2840C ($840C at runtime), containing 32 entries going 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0 and so on, i.e. a mod 6 table. This table is indexed by the current camera position divided by 64. However, if the camera position is way out of bounds (above 2048), we read outside of the table and find a value from regular program code. If the camera position is between $3540 and $357F, we read from $840C,$D5, which happens to be $70. Added to $7C2, this happens to be $7C2 + $70 = $832, which happens to be the same address as $32, the level index. In other words, with a specific out-of-bounds camera position, we can corrupt the level index.

What Value is Written

This part is substantially more complicated, but it also comes down to a combination of camera position and the current room. As mentioned, we write data from a 5-byte struct into RAM. These structs are accessed indirectly through a pointer table located at $2A03F. We must find a struct where its first byte is $0E, the final level index. Fortunately, several such structs exist, at indices $0D, $11 and several others.
The next part is then how to access this struct. It is accessed like this, immediately before we write the data (see above):
LDA $76    
ASL A      
TAY
...
LDA ($98),Y
ASL A      
BCS $811A 
TAY        
LDA $A03F,Y
STA $00    
LDA $A040,Y
STA $01
...

$76 is the camera position divided by 64 as before, which is then multiplied by 2. The index for the struct is fetched indirectly from a pointer at ($98),Y. The value of this pointer is determined by the current screen index. Every screen in the game has a unique pointer. The pointers for the screens are arranged like this in ROM:
$293F1  Block 0  (Beginning)
$29401  Block 1  (Clock Tower)
$29425  Block 2  (Mad Forest)
$29439  Block 3  (Ship)
$29451  Block 4  (Death Tower)
$29463  Block 5  (Bridge)
$2946F  Block 6  (Swamp)
$29478  Block 7  (Caves)
$2948F  Block 8  (Sunken City)
        <Large Gap...>
        ...
$29BF1  Block 9  (Crypt)
$29BFB  Block A  (Cliffs)
$29C17  Block B  (Rafters)
$29C25  Block C  (Entry Hall)
$29C2F  Block D  (Riddle)
$29C45  Block E  (Final Approach)

As you can see, the pointers for the screens are mostly adjacent to eachother in ROM, save for one large gap in the middle. The combination of screen and camera position is used to index the struct pointer table at $2A03F. However, we need to have a specific camera position (see above). This means that we read from the screen pointer itself out of bounds, but it also limits the number of possible rooms we can use for this. Unfortunately, no valid room in the game is practical to use with the camera position that we need. However, the gap in-between those screens is our saviour. Most of the bytes in this gap are $00, which means that $98 may be loaded with zero, which allows us to read an index from zero-page!
We read from $D5 << 1 = $1AA, so in practical terms, we read a value from $AA to use as an index for the table of structs. The task then becomes finding a way to get the correct index ($0D, $11, etc.) into $AA to read it. Unfortunately, again, under normal circumstances $AA doesn't contain a suitable value. We therefore need to repeat this whole process to get $AA to contain $11, which finally enables the wrong warp to work. This is in itself not trivial, but at this point I decided it was simpler to just copy the old route and be done with it.
The reason why the previous run and this run both go to the swamp is because, as you can see, the screens for the swamp are physically closer in ROM to the invalid gap: We skip several blocks worth of screens that do not contain usable wrong warps. At this point, we simply need to venture enough screens out of bounds to hit a screen that points to $00. Another potential approach could be to go out of bounds in the clock tower and use yet more memory corruption to get a large enough screen index to reach the gap early.
I wrote some tools to find possible rooms and camera positions to find memory corruptions in this game. Messy as the code is, it helped immensely in figuring out the possibilities.
It is also worth noting that unfortunately, there is no credits warp using this method. A credits warp would work by setting the gamestate variable $18 to $0C. Unfortunately, $0C only ever occurs in the first byte of the 5-byte structs, but there is no suitable offset to add to $7C2 to get to $81E. Knowing this would have saved me a lot of frustration and hours wasted many years ago.

In practice

In theory, the wrong warp is simple and repeatable: Just go to the right screen with the right camera position, and make sure any variables in zero page are set correctly if required. In practice, every time the screen changes, other variables are overwritten as well, causing all manner of strange effects and instability. In addition, actually getting the correct camera positions is also easier said than done. I still haven't fully deciphered how the camera position gets set between screen changes all the time. The current wrong warp as it is is an utter miracle, because the specific sequence of room changes it executes happen to be exactly right to prevent the game from freezing or otherwise glitching out with undesired effects. I do not know how much research the original discoverer of the glitch did, but it is nothing short of stunning that it is as optimal as it is. Not only does the warp work, but it is also repeatable enough that real time runners successfully do it.
It still is not trivial however. While the wrong warp itself is repeatable, the screens you come across in the glitched worlds are somewhat based on RNG, so it is very possible to get dismounted from the stairs and die or get stranded in the glitched world.
As for the J version of the game, the principles of the wrong warp apply just the same, however, some of the exact values you get are different. The J version is also substantially more unstable, causing freezes and general havoc much more easily.
Phew.

Stage by stage comments

Block 1 (Village)

As usual, the goal for this stage is to get a triple shot holy water to roast the boss with. Right off the bat, you see me pick up the dagger. This in itself loses 5 frames, but I make up the time later by using the dagger in the vertical climbing section. There is no practical time difference, but I was tired of doing the same start over and over again. A jump loses 3 frames, unless you jump from close to the edge of a block, in which case it only loses 2 frames. Similarly, landing loses 2 frames, unless the ground stops shortly before your feet where you're landing, in which case you save a frame again.
I manipulate Medusa Heads out of my way by turning around for a frame. When turning, it is generally better to let go of any directional input for a frame first, as you do not move back that way. Similarly, I manipulate some zombies to not spawn in my way so I do not need to jump over them. I generally got lucky, and did not need to manipulate a lot to get through this section.
The rest of the stage goes as usual. The flames on the boss take a random amount of frames to disappear. There can be up to 5 frames between the best RNG and the worst RNG. Overall, I save 41 frames over the old run.

Block 2 (Clocktower Ascent)

Some slight changes in approaching certain sections end up saving a few frames each. Walking on the large gears for a few frames also saves time. It also saves a considerable amount of lag in the final climbing section, because I do not advance the camera as fast.
For Grant, I must make sure that he is close to the floor as he dies, because the orb only spawns once he hits the floor. Overall, not much to mention about this stage. 54 frames saved, and 95 in total.

Block 2 (Clocktower Descent)

This is where things get fun.
Pausing manipulates the Medusa Head to spawn in a different place, so it does not hit me. I need to carefully time my pause to minimize lag. I save a bunch of time over the old run by having better control of Grant. We also see the first instances of vertical screen wraps, using them to get to the bottom of the screen very quickly. Because Grant moves up with a lot of velocity, I need to make sure there's a ceiling below me to grab on to.
In the final room, it is faster to destroy the candle and pick up an extra heart than it is to avoid it. While it means I need to wait for one heart to tick down, this is better than not being able to trigger the stage end early. I also jump into the stage end trigger because it is faster than walking into it. Overall, 418 frames were saved in this stage.

Block 3 (Mad Forest)

I need to carefully plan out my stopwatch usage throughout this section to minimize my jumps. Grant loses between 9 and 12 frames for a simple jump, depending on the specifics. Overall, I can get by with several fewer jumps than the previous run. Taking damage in this section also affects the wrong warp later, so I must make sure to exit this stage with full health. Overall, 62 frames were saved in this stage.

Block 4 (Swamp)

I make use of a different stair dismount method than the previous run. Using Grant, I jump with enough height to wind up 4 pixels above the floor, switch to Trevor, and then immediately switch back. This causes Grant to stand in the air, allowing him to climb the stairs several pixels high. I still need to change to Trevor once to actually clear the end of the stairs, leading to me starting the wrong warp as Trevor. Later on, the game changes my character to a glitched Trevor anyway, so it makes no difference for the wrong warp. Trevor also climbs up screens about 10 frames faster (although he loses that time when moving down screens again), which also saves a bit of time.
The wrong warp you see is the same route as the previous run but with all of the fat trimmed. I eliminate several redundant character switches and don't go as far into certain rooms as the previous run. Beyond that, as mentioned previously, it all comes down to executing the right sequences of screen changes with the right camera positions to corrupt memory in just the right way that the wrong warp activates without messing up anything vital. Immediately as the game level is changed, the stairs beneath Grant's feet disappear and I deathwarp to the final level. Overall, the wrong warp is executed 1356 frames faster.

Block A (Final Approach)

This stage immediately puts me in a difficult spot. I need to get an axe, a shot multiplier, and as many hearts as I can muster up to Dracula. There are not enough enemies and candles on the way to comfortably get everything I need without unacceptably long detours. Enemies drop a heart on every 8th frame. In addition, once 4 enemies have dropped a heart, the fifth enemy will drop a subweapon, which is determined by RNG. I use this to skip the axe in the first screen and save 238 frames. This only exacerbates the crunch for hearts I'm under.
The next screen is lag city. The Medusa Heads spawning cause an unreal amount of lag, so I stay at the top of the screen to despawn them until the gears are unloaded. Again, this starves me of several hearts I would really like, but it saves a whopping 188 frames of lag. While the gears are on screen, you cannot really do much of anything, even moving or jumping causes lag. Once the gears are gone though, it's smooth sailing and I kill as many Medusa Heads as I can. Sometimes, two spawn clumped up together on the same spot. You can manipulate the drop timer by pausing to always force a heart drop. Thankfully, I did not have to use this. It is also worth noting than an axe will only cause the first Medusa Head it hits to spawn a heart, since the others spawn the score bonuses instead. They still count toward my shot multiplier though, letting me hit several targets with one axe. In the next room, I take more damage to get through the bone pillars faster.
In the next section, there is probably the only useful instance of horizontal screen wrapping in the game. By manipulating the correct subpixel position and minimizing jumps, Grant can jump into the block right next to the screen edge, jump toward the wall on the left side of the screen and wrap around. Not only does this save 75 frames, but it also conveniently puts a pork chop right in my way, which is more than a little handy.
The next room also uses a vertical warp to reach the pendulum faster. Lag management is imperative here, as is carefully scrolling the camera in the right way. Crouching also helps to reduce lag by a few frames. Finally, I approach Dracula himself...

Dracula

I experimented with several attack patterns to find the optimal damage cycle. Typically, using an animation cancel to throw an axe and then jumping and attacking twice is the fastest method, making short work of Drac 1 and Drac 2. Each form of Dracula has 64 HP, but they take differing amounts of damage. Drac 1 takes 5 damage per knife, and 7 damage per axe, but stacking multiple sources of damage will often reduce the damage inflicted for some reason. It's still worth it, just not as efficient as you might think.
Dracula's second form is quite involved. He has 5 heads, all of which have 13 HP. He takes 2 damage from a dagger, and 3 damage from an axe. If a head dies, the hitbox for the attack remains active and can damage another head. Finding the most efficient way to spread damage while minimizing lag is therefore the goal of the fight. I deliberately take damage from Drac 2 to get on the other side of him, which also conveniently reduces lag, probably because Grant's sprite is only visible every other frame.
Finally, we get to Drac 3 who is a major PITA. His laser beams have been buffed significantly compared to the J version, but the only thing that has been buffed more is the lag. While the fight is an easy affair in the J version, minimizing lag is vital and incredibly difficult here. I have 16 axes which do 4 damage each, allowing me to finish the fight using just axe throws. The dagger does 3 damage. I can also stack multiple attacks on his hitbox, but it's not as convenient as it might seem because I need to wait for the axes to go offscreen before I can attack again. The positioning for this is also very precise, making using an animation cancel the only viable option to get two axes on him consistently.
The entire fight is also on a timer, due to the platforms. I need to finish the fight so I can land on the moving platform on the earliest possible frame. The goal is therefore to finish him off while still landing on the platform as early as possible while also minimizing lag. I manipulate his lasers to go into the ground always so they disappear as fast as possible. While his lasers are active, I stop attacking him for a while to minimize lag. Doing anything, moving, jumping or attacking causes lag during this time. There is not enough time to eliminate all lag, I do need to have some axes on screen while the lasers are out. I make use of damage stacking to get more axes out during the time there is no lag. While waiting for his lasers to disappear I can ride out the delay of waiting for the existing axes to leave the screen.
Finally, with the final axe, I jump up to the platform just in time, finishing the stage 1230 frames ahead. While my final axe throw is later than the previous run, I need to hold the jump button for the entire time I'm moving up to the platform anyway, so it does not delay input end.

Other comments

Loose Ends and Improvements

I investigated the wrong warp for a long time and I'm in awe just how rickety it is. Despite all my research, I still cannot rule out that there is a faster method to execute the wrong warp, either by doing a different route in the swamp, or using a new route starting in the clock tower.
As mentioned, there is also no credits warp using this method. A proper credits warp would therefore need a different glitch, that would likely veer directly into ACE territory on the J version.
Finally, the current run is not perfect. I lose on the order of about 10 frames in the first stage through bad strategy. I should have picked up fewer hearts early, because this spawns a whip upgrade from the candle in the last room of BLK-1-02, which I need to jump over. I also duck under a fleaman, waiting for several frames, but it would've likely been faster to take damage from the previous fleaman instead.
I also would've liked a triple shot against Dracula, as this would've probably sped up the fight on the order of about 60 frames. However, the number of hearts I would've needed to pick up to get a triple shot and to get enough hearts to take advantage of it would've killed that improvement.

Final Comments

And that was Castlevania 3. Again.
I've been more than a little infatuated with the game for quite a while now, and I'm glad to finally have some sort of understanding of those glitched worlds which mystified me for so many years. Big thanks go out to the RTA speedrunning community of the game, in particular KutsuShita for finding the upwarp, as well as Burb, Spriven, SBDWolf, Pianotorious and bogaa. I'd also like thank the previous TASers of this game, especially Sanhaim-Grim, Bablo, zggzdydp and arandomgameTASer, Samsara and Mothrayas for their many years of discoveries and findings. Special thanks also go out to FerretWarlord for listening to my incessant yelling about this game.
Finally, thank you very much for making it this far, and I hope you enjoy the run.
-scrm

ThunderAxe31: Judging.
ThunderAxe31: Accepting as improvement over the current publication.
feos: Pub.
Last Edited by scrimpeh 20 days ago
Page History Latest diff List referrers