I recommend downloading my
release package as it includes the movie file as well as other accessories such as memory watch files, lua scripts, etc.
This run aims for the fastest time to complete the game with the good ending, on normal difficulty.
The run starts with no predefined save, but it completes the game in 8 minutes and 20.12 seconds,
by utilizing a variant of a famous zipping glitch.
The in-game completion time of this run should be 00:07:18 (26310 frames),
but since game timers have been overwritten by a glitch,
it is shown as 00:43:43 after the game has been completed.
- Emulator used: DeSmuME 0.9.4+ (with no lag reduction options; i.e. official timing)
- The game sometimes lags more than the real console, because of the emulator's inaccurate timing emulation.
- I recommend OpenGL. Both OpenGL and SoftRasterizer work well, but I think SoftRasterizer causes a minor display glitch.
- Aims for fastest time (to get the good ending)
- Abuses programming errors in the game / Uses zipping glitch
- Manipulates luck (for soul/item drops, boss moves, etc.)
- Takes damage to save time
- Genre: Action, Platform, RPG
- Colors a warp room
About the run
The run completes the game in the following process.
- Prologue
- Meet Yoko and Julius, obtain Magic Seal 1
- Obtain Axe from Axe Armor
- Defeat Flying Armor
- Defeat Balore
- Save game for later glitch use
- Obtain Magic Seal 2
- Defeat Dmitrii
- Perform zipping to collect achieve things and suspend/restart the game
- Go to Warp Room and warp to The Abyss. The castle will self-destruct in a few seconds
It is well known that the famous zipping glitch, which is called Succubus glitch,
allows you to obtain a bunch of souls and items immediately.
That Succubus glitch is used in a few of speedruns. However, they needed to get to
The Pinnacle to obtain Succubus' soul.
While making this run, I invented a variation of Succubus glitch,
a method to interrupt special attack by a cutscene.
It allowed me to perform a zipping much earlier than before.
Moreover, I enthusiastically investigated what can be done by the zipping,
and I finally concluded that the last warp room can be activated and also Menace can be skipped.
Since the emulator generates a ridiculous amount of lags,
I did not mind lags and 1 frame losses very strictly
(still I generally did my best, of course!).
Instead, I tried to optimize the whole route/strategy hard,
and also to discover general tricks of the game.
Tricks
Here I show you what I have found.
Backdash
There are three methods of move, backdash-jump, forward-backdash-blank and backdash-crouching.
I compared their average speeds to see which is faster. I show the comparison on flat ground below.
- Backdash-jump: (13784+13232+12680+12128+11576+11024+10472+9920+9368+8816+8264+7712)/12 = 10748
- Forward-backdash-blank: (6144 + 13784 + 11384)/3 = 10437.3333
- Backdash-crouching: (13784+13232+12680+12128+11576+11024+8624+0)/8 = 10381 (8 frame pattern = fastest one)
- Additional note: Replacing crouching to subweapon use seems to be slightly faster if it does not lag. It will greatly decrement your MP, though.
That is why Soma jumps again and again.
Crouching is used as an alternative move method,
when Soma is on a downslope, or when Soma wants to jump earlier.
Sometimes, it is also used for some luck-manipulations.
Actually, I also considered about one more method, "forward, backward + backdash, release".
It has never been used since it was slower than the two methods above.
Motion cancels
- Backdash can cancel an attack motion. It is a well-known behavior.
- Landing can cancel an attack motion of some weapons. It is a well-known behavior, too.
- Axe Armor's ability can cancel a landing motion after damage.
- To cancel Black Panther's ability just before landing can cancel a landing motion.
Randomness
Some rooms (more precisely, enemies, backgrounds, and other effects) change the randomness on every frame, some rooms do not.
Soma can affect the randomness at least by a normal attack (changes it once),
backdashing (sometimes changes it while doing that), or turning around (changes it once).
It cannot be changed by jumping, crouching, or using a soul that I used in the run.
Backdash+crouching is a handy way to manipulate randomness without losing speed much.
The recurrence equation of RNG is identical with later 2 DS games, Portrait of Ruin and Order of Ecclesia. Also, it is almost identical with Aria of Sorrow. It is a sort of linear congruential generator. The following C code emulates the behavior of the RNG.
static int32_t x = 0; // $020c07e4
int32_t rand() {
x = (x >> 8) * 0x3243f6ad + 0x1b0cb175;
return x;
}
Crystal blocks
Crystal blocks are shattered by touching the screen (Balore's soul needed).
In a tool-assisted speedrun, so many blocks can be shattered immediately, by moving the stylus incredibly fast.
I just discovered it after I finished the run, so I cannot tell you the detail at present.
I assume that it happens under a limited pixel conditions.
This trick might improve the run a little. I hope it will because it looks good.
Activate a switch behind the wall (not used)
One of famous glitches. The Cutall/Cinquedea/Alucard Sword's special attack is teleporting behind an enemy and stabbing it.
By utilizing that, you can activate a switch, which is on the other side of the wall.
Infinite jump glitch (not used)
I don't know the detail since I have never had a look at it.
Zipping: General info
When Soma goes outside the room, the game writes "visited" flag to abnormal location.
As a result, it causes various things like the run has done.
In other words, the write address is determined from Soma's location.
I studied how to calculate the exact write address from Soma's position,
and wrote up a lua script which shows the write address in realtime.
It definitely helped me to consider the optimal route of the zipping part.
When Soma goes to the right as long as one room, the write bit gets shifted to the left.
However, the range of the write address changes per 16 rooms (i.e. 2 bytes). Read the script below for details.
-- Address view for memory writing with zipping
-- Open the memory viewer to see what's actually going on.
if not emu then
error("This script runs under DeSmuME.")
end
if not bit then
require("bit")
end
function cvdosPosToMapFlag(x, y)
x, y = x % 256, y % 256
local xl, xh = x % 16, math.floor(x / 16) % 16
local i = (y * 16) + (xh * 46 * 16) + xl
local pos = 0x20F6E34 + math.floor(i / 8)
local mask = math.pow(2, math.floor(i % 8))
return pos, mask
end
gui.register(function()
local x = memory.readbyte(0x0210F018)
local y = memory.readbyte(0x0210F014)
local i = (y * 16) + x
local pos, mask = cvdosPosToMapFlag(x, y)
agg.text(140, 5, string.format("%08X:%02x", pos, mask))
agg.text(140, 24, string.format("[%04X-%04X]", cvdosPosToMapFlag(x - (x % 0x10), 0) % 0x10000, cvdosPosToMapFlag(bit.bor(x, 0x0f), 255) % 0x10000))
agg.text(140, 43, string.format("[%04X-%04X]", cvdosPosToMapFlag(x - (x % 0x10) + 0x10, 0) % 0x10000, cvdosPosToMapFlag(bit.bor(x, 0x0f) + 0x10, 255) % 0x10000))
agg.text(140, 62, string.format("(%03d/%X,%03d)", x, x % 16, y))
end)
Here are some of addresses I planned to (not) manipulate in the run.
Address | Size | Description |
---|
020F70D7 | lower 4 bits | Yorick |
020F70E0 | lower 4 bits | Succubus |
020F70E1 | lower 4 bits | Erinys |
020F70EB | higher 4 bits | Black Panther |
020F70F3 | higher 4 bits | Final Guard |
020F70FC | higher 4 bits | Golem |
020F70FD | higher 4 bits | Lilith |
020F7104 | higher 4 bits | Draghignazzo |
020F7109 | lower 4 bits | Stolas |
020F710A | higher 4 bits | Malphas |
020F710B | lower 4 bits | Doppelganger |
020F710B | higher 4 bits | Rahab |
020F710C | lower 4 bits | Hippogryph |
020F710C | higher 4 bits | Procel |
020F710D | lower 4 bits | Mud Demon |
| | |
020F71E2 | lower 4 bits | Cutall |
020F71E2 | higher 4 bits | Cinquedia |
020F71ED | lower 4 bits | Kaladbolg |
020F71F2 | higher 4 bits | Claimh Solais |
| | |
020F720E | lower 4 bits | Dracula's Tunic |
020F7216 | higher 4 bits | Death's Robe |
| | |
020F721B | lower 4 bits | Rosary |
020F7226 | lower 4 bits | Chaos Ring |
| | |
020F6E20 | 1 byte | Room X position (for start from suspend) |
020F6E22 | 1 byte | Room Y position (for start from suspend) |
020F6DFC | 4 bytes | If the value bitand 0x44000007 is non-zero, disable Suspend |
020F7018 | 4 bytes | X position for start from suspend |
020F701C | 4 bytes | Y position for start from suspend |
020F703C | 4 bytes | Ingame time |
020F7187 | bit2|bit3 (04|08) | Save Room & Warp Room first time message flag |
020F718A | 1 byte | Menace related flags ($30 is the best value to skip him) |
020F71A8 | 2 bytes | Activated warp destinations (bit11/$0800 = The Abyss) |
020F7228 | 2 bytes * 6 | Button settings |
020F7256 | 2 bytes | Disable Suspend/Pause (Julius Mode) |
020F7259 | 1 byte | Hard Mode if nonzero |
Note: As you can see in the run, Soma can move through the wall by sliding.
Zipping: The Succubus glitch and its variations
All of these glitches cancels a special attack halfway to go into the wall.
The difference is only how to cancel an attack.
The steps to achieve the most famous Succubus glitch are:
- Equip Cutall/Cinquedea/Alucard Sword and Succubus
- Use its special attack then cancel the motion by Succubus immediately
- Walk/Attack for about 2.5 seconds (do not stop, jump, backdash, and so on. It's important)
- Use the special attack again against the wall
- The special attack will be canceled in the wall if it succeed
Axe also can be used to perform the glitch. Here is a
demonstration video (youtube).
When you use Axe, you'll need to switch your equipment during the glitch, because Axe's special attack is too long.
Axe also can be used to perform the glitch. However, you'll need to unequip the Axe once
after you used a special attack (it's good to use Doppelganger to unequip a weapon quickly, by the way),
because Axe's special attack is too long.
A cutscene after the boss fight also cancels the special attack (it's the core of the run).
It can be used earlier since it doesn't require a Succubus' soul.
Memory addresses
Most of the zipping related addresses are shown above.
Address | Length | Description |
---|
020CAA40 | 4 bytes | X position |
020CAA44 | 4 bytes | Y position |
020CA95C | 4 bytes | X position (next) |
020CA960 | 4 bytes | Y position (next) |
020CA968 | 2 bytes | X velocity |
020CA96C | 2 bytes | Y velocity |
020C07E4 | 4 bytes | Random number |
020F703C | 4 bytes | Ingame frame |
020CA9F3 | 1 byte | Soma invulnerability 1 |
020CAA24 | 1 byte | Soma invulnerability 2 |
020F7410 | 2 bytes | Current HP value |
020F7412 | 2 bytes | Max HP value |
020F7414 | 2 bytes | Current MP value |
020F7416 | 2 bytes | Max MP value |
020F7448 | 4 bytes | Current EXP value |
0210AF42 | 2 bytes | Left of sprite hitbox |
0210AF46 | 2 bytes | Right of sprite hitbox |
0210AF44 | 2 bytes | Top of sprite hitbox |
0210AF48 | 2 bytes | Bottom of sprite hitbox |
020F2A88 | 1 byte | Magic Seal Step |
020F2A8C | 1 byte | Magical Seal Substep |
020CED88 | 4 bytes | Succubus timer |
The following addresses might change randomly.
Address | Length | Description |
---|
020D36A8 | 2 bytes | Flying Armor HP |
020D3513 | 1 byte | Flying Armor invincibility #1 |
020D3514 | 1 byte | Flying Armor invincibility #2 |
020D3515 | 1 byte | Flying Armor invincibility #3 |
020D26E8 | 2 bytes | Balore HP |
020D2553 | 1 byte | Balore invincibility #1 |
020D2554 | 1 byte | Balore invincibility #2 |
020D2555 | 1 byte | Balore invincibility #3 |
020D2EC8 | 2 bytes | Dmitrii HP |
020D2D33 | 1 byte | Dmitrii Invisibility #1 |
020D2D34 | 1 byte | Dmitrii Invisibility #2 |
020D2D35 | 1 byte | Dmitrii Invisibility #3 |
Prologue
In fact, the first Skeleton can be defeated 1 frame faster,
by not jumping at second backdash and sliding earlier.
Golem battle is one of my favorite parts. Soma attacks him with insane speed.
Lost Village - Up to Flying Armor
Actually, Soma moves faster than a bone of Skeleton's soul.
So it's rather difficult to throw a bone optimally.
After all, I tested all patterns I came up with. I am not so smart :P
It might be better to defeat Peeping Eye by using Knife (with backdash and 2 frame jump)
instead of Armor Knight, but I am not sure.
The Axe Armor room is very luck-manipulative room. You need to manipulate the randomness to
obtain Axe Armor's soul and Axe, and also to manipulate Zombies' positions.
I manipulated the randomness hard by brandishing a knife here and there, but
I could not manage to eliminate one zombie who is just after Axe. Also,
this is the first room I really wanted a lag-free emulator :P
Upgrading Axe Armor's soul here (and possibly in Wizardry Lab) might save time later
(if a lag-free version of the emulator is used).
Perhaps it will make that luck-manipulative room even harder. *shivers*
Stylus can be used in the menu screen. It is sometimes faster than using keys.
Lost Village & Wizardry Lab - From Flying Armor, up to Balore
As Atma told me in the forum, in the Flying Armor battle, I need to manipulate him to position
as low as possible to make attacking him easier, and defeat him with scrolling to the left
as far as is possible.
I tried hard but I could not get to the most left side of the room.
A better luck might improve the battle a little.
You may think that I can perform the cutscene zipping with Flying Armor.
That is right, but I cannot do anything during the zipping so that it takes a few minutes to return.
Additionally, I can get nothing there (the range of write address: 020f6e34-020f7033).
After I used Flying Armor, I switched it back to Armor Knight soon,
to make the Balore battle a bit faster and to cancel damage motions.
I considered that it is faster than keeping Flying Armor equipped.
If I can go down floating footholds without losing speed here and there, it will save 10-20 frames.
Wizardry Lab & Garden of Madness - From Balore, up to the save room
Balore is defeated without Mind Up.
It makes the battle about 3 seconds longer, but later I can gain about 10 seconds.
After I defeated Balore, there is a chance to leave a message on crystal blocks.
However, I did not do that since it lags too much.
This is one of obvious improvable point, isn't it? ;)
I used two Potions after the Balore battle, but I guess it might be a mistake. Reducing the amount of damage might make the run faster, rather than potion use.
In the Slaughterer room, it might be better to take damage instead of defeat them.
It gives Soma less exps, and it might allow us to defeat more enemies later.
By the way, it was a little hard to manipulate Slaughterer's position and hitbox.
If you want to make a tas of the game, I suggest to you writing a hitbox display lua.
To activate the suspend command, I saved the game in Garden of Madness.
Particles in the room might affect the time, but I really did not care.
Even a zipping cannot skip this process. That is known via memory value.
Garden of Madness & The Dark Chapel - Up to the end of the Dmitrii battle
I didn't defeat enemies much because it causes a levelup and it freezes Soma for a few seconds.
Also, I didn't equip Breastplate because it takes time a little to equip and I assumed that it doesn't save time after all.
I did not go up until the last axe hits Yorick. If I go, the collision detection will be skipped and I cannot defeat him.
Outside of the world - The zipping part
It's the most difficult section to explain... Well, read the general info section about zipping above first of all.
What I did in the run are:
- Obtain Black Panther
- Obtain Hippogryph (optional; I think it saves time rather than not use it, or more than obtaining Malphas instead)
- Set $30 to $020F718A to skip Menace (as a result, the game considers the first conversation and the battle are already done)
- Activate the warp to The Abyss
What I did not do in the run (because it seems to take time to do that) are:
- Obtain Malphas
- Skip the "Warp Room" description of the first time
I sometimes stopped to not overwrite the starting position after suspend much.
I constructed this route in the following steps:
- List up all possible useful locations (and dangerous locations as well)
- Divide them into 4 groups (odd number address or even number address, in higher 4 bit or in lower 4 bit)
- Count the minimum horizontal distance to get to each location, and group them by it (in the 16-bit alignment)
- See what is near/far each other and consider about how to visit them
One vertical loop takes a few seconds, so that I think it is not so smart to wait again and again to visit more locations.
The finale
In the room of lift, I compared which is faster, going from the upper or lower. They were the same.
When I fall from high position, I cancel Black Panther immediately just before the landing and cancel the motion.
I had never thought that I would invent such a odd route.
In fact, before I met DeSmuME-rerecording, I had never considered about the speedrun of the game.
One day, I wanted to try recording a DS TAS movie with a new emulator,
so I TASed the very beginning part of this game (the first battle against three enemies) just for curiosity.
I learned some of the basic mechanisms of the game through the recording, and
it told me that I can improve
Atma's demonstration TAS in the forum.
This is why I started working on the game suddenly. I had never expected that I would make a Castlevania TAS.
The video completes the game in 28:58, by utilizing famous two glitches.
One is a way to open a gate from the opposite side of a switch by Cutall's special attack,
and the other is the one called "Succubus glitch" which allows us to go through/into a wall,
and get a bunch of souls and items (details are described later).
My original aim was to optimize his speedrun, and I assumed the completion time would be 20+ minutes at best.
However, that was not true. I'd like to thank all people who supported/encouraged me. I could not make the run without them.
Special thanks
- Satoryu: for his non tool-assisted speedrun
- Groobo: for his semi tool-assisted speedrun
- People in the Dawn of Sorrow topic: You guys are always kind. Especially, I thank Atma for posting his first TAS attempt of the game. That motivated me a lot.
- #TASers: A Japanese IRC channel. They motivate me always! Especially, I thank pirohiko for inventing a new style of video editing! ヽ(>ヮ<)
Hope you enjoy the run, thank you!
Screenshots
gocha: Hi! I am an idiot. I took a few screenshots of this movie and placed them here. Here goes! Feel free to clean up the list.
adelikat: Accepting for publication under one condition. A video like the one on the nicovideo be the published MKV.
ShinyDoofy: Processing...