Submission #1482: AnS's NES Hi no Tori: Gaou no Bouken in 14:16.73

Nintendo Entertainment System
(Submitted: Hi no Tori - Houou Hen - Gaou no Bouken (火の鳥))
baseline
(Submitted: Hi_no_Tori_-_Houou_Hen_-_Gaou_no_Bouken_(J).nes JPN)
FCEU 0.98.16
51404
60
9528
Unknown
AnS
Submitted by AnS on 3/13/2007 10:02 PM
Submission Comments

Hi no Tori Tool-Assisted Superplay

Features:
  • Manipulates luck
  • Aims for fastest time
  • Takes damage to save time
  • Genre: Platform
Recorded with FCEU 0.98.16
Completed in 14:17 (51404 frames)

Game features

I don't recall any story behind the game, but the objective is clear: you need to run through platform-style game and collect 16 pieces of big picture - by 1 piece at the end of each stage. When all pieces are collected, something marvellous will definitely happen.
The game is not so straightforward as most platformers. You'll need to search for teleports (hidden all over the game) and find your route through all 16 stages.
There are 4 zones:
1st - 7 levels, 6 bosses
2nd - 5 levels, 2 bosses
3rd - 3 levels, 1 boss
4th - 1 level
Oh those prime numbers.
One interesting feature makes the game rather unique - in case you're unhappy with level design, you can (right during main gameplay) destroy some platforms/blocks/walls/objects as well as ganerate blocks/platforms where necessary. The game could be looking more like puzzle if level designers would only try.

TAS features

Used tools:
  • Frame Advance + 9528 rerecords (not that much because luck manipulation was achieved without trial&error)
  • NES debugger (study RNG function and stuff)
  • Memory Watch + calculator (for luck manipulation/direct programming)
  • Hexeditor (most obvious example - Lv.15) + nesmock
  • FCEU with dual-movie support (used only because of Lv.15 dual walkthrough)
  • ModPlugTracker for sequencing hi-freqency marches
Comparing this heavy list with Morimoto's "slowdown+rerecords" you may come to thinking that 15 seconds isn't an improvement in the case. In fact, I agree.. Well, let's hope that some additional aestetic value will save the day.
No programm errors were found, but I use L+R frequently to shoot backwards without turning and losing speed.
Useful memory adresses:
12Global Timer of the game. It keeps increasing until 255, then it drops to zero and so on
3FRNG_Substractor value. Every time when RNG routine is called, this value increases inself by current value of GlobalTimer
40RNG_Result value. At the end of RNG routine this value decreases inself by Substractor
6FBoss damage (0-10h). Every boss needs 16 hits to defeat it. The highest frequency of delivering damage is: 1 hit per 6 frames
406Player_X (0-255)
408Player_Y (0-239)
402Current speed (C0h < 0 > 40h signed). Character's horisontal speed can vary from -64 to +64. Every level scrolls left-to-right, so I tried to keep this value at 0x40 (+64) for every frame possible.
414`freeze enemies` timer
415`flashing mode` timer (A0h-0)
416`mirror mode` timer (A0h-0)
417`invulnerability` timer (after taking damage)

Choosen route

Because of dozens of teleports, the game can be completed via different routes. Although Morimoto used quickest route, I wouldn't like to play the game as developers were expecting. Here's the picture.
16151413
12111009
08070605
04030201
Morimoto's route: 1 - 2 - 3 - 4 - 5 - 6 - 7 - 1 - 9 - 10 - 11 - 12 - 13 - 9 - 15 - 16 - 14 - 15 - 8
I've decided to take different path:
  • to show where you will appear after completing Volcanic level (Lv.8)
  • to make smth even more different from currently published run
  • to simulate Final Boss battle (failed)
1 - 9 - 10 - 11 - 12 - 13 - 9 - 15 - 16 - 14 - 15 - 8 - 1 - 2 - 3 - 4 - 5 - 6 - 7
There shouldn't be major time difference between my route and standard one. The only difference is that you can stop recording a little earlier before taking last piece at Volcano (if your speed is high enough to touch the piece after input stops).

About luck manipulation in the game

In most cases speedrunner tries to manipulate luck by choosing best variant of events that can be influenced by input. So actually "luck" depends on player's patience in rerecording.
But in case of this game there's no obvious difference regardless of any actions you perform right before necessary luck event. You may spend thousands of rerecords for nothing.
Instead, there's way to predict RAM behaviour and calculate needed strategy long before critical luck event. That's it, the game can be played on paper. The result is quite cool: at the beginning of every stage I know exactly what to do (and when) without rerecords.
Unfortunately, this method is game-specific (while rerecords+analysis provide universal way to manipulate luck) and in HinoTori it comes from simple RNG and strict usage of RNG calls.
Here's how randomness in the game works.
At most cases the game doesn't use RNG values to make decisions, most of events are predefined. RNG values can remain untouched during several levels! RNG can be used by some bosses (esp. by those `falling objects` rooms) and sometimes by rare item drops.
Falling rooms use RNG periodically to set X-coordinate of new falling object. Bosses use RNG to make decisions (jump/shoot/run) or set current angle of shooting. Item drops may use RNG to choose "random item". That's all, nothing else can modify my precious RNG values. Therefore speedrunner can manage those values precisely - everything you need is to know how RNG works.
Here's piece of disassembled code:
 C97A: LDA $12		' take GlobalTimer
 C97C: ADC $3F		' add it to RNG_Substractor
 C97E: STA $3F		' save sum to RNG_Substractor
 C980: LDA $0406		' take Player_X
 C983: ROR		' whether it is odd or even?
			' result is saved in Carry_Flag
 C984: LDA $40		' take RNG_Result
 C986: SBC $3F		' substract RNG_Substractor from RNG_Result,
			' taking into account Carry_Flag
 C988: STA $40		' save result of substraction to RNG_Result
 C98A: RTS		' return from subroutine, with random value
			' in A register
If you don't get it, here's Visual Basic translation.
 temp_a = GlobalTimer
 temp_a = temp_a + RNG_Substractor
 RNG_Substractor = temp_a		' therefore RNG_Substractor =
					' RNG_Substractor + GlobalTimer
 temp_a = Player_X
 Carry_Flag = temp_a MOD 2		' even/odd = remainder after division by 2
 temp_a = RNG_Result
 temp_a = temp_a - (RNG_Substractor + (1 - Carry_Flag))	' that is how SBC
						' instruction of
						' NES processor
						' actually works
 RNG_Result = temp_a		' therefore RNG_Result = RNG_Result -
				' (RNG_Substractor + (1 - Carry_Flag))
 RETURN (temp_a)
Judging by this listing, there can be two ways to influence RNG usage. First way is to increase GlobalTimer (by pausing the game for necessary amount of frames) before game event calls RNG. Second way is to call RNG routine manually. If you need to prepare useful "randomness", you can calculate how much times you need to call RNG (but taking into account bosses' own calls, this is tricky).
RNG can be manually called by shooting an enemy at right frame. Usually when some enemy disappears (after being shot) it leaves "Block" item - you need to collect those and waste them to build walls and platforms. Pretty simple. But KONAMI added really ingenious feature - IF at the moment of enemy disappearing GlobalTimer reaches zero, it's up to RNG to decide whether to drop usual Block or some other item (almost any item!)
While playing Hi No Tori on my NES back in early 90s I figured that if you're lucky enough, sometimes you can receive unexpected item from any enemy (even from skeleton boss from Lv.15).
Here's listing of the game code for item drop routine. This piece of code is executed at the moment you hit an enemy with throwing chisel.
 B917: LDA $12		' watch GlobalTimer
 B919: AND #$7F		' but dont take into account 8th bit of GlobalTimer value
 B91B: BNE $B939		' if GlobalTimer is not zero then drop normal Block
 B91D: JSR $C97A		' else call RNG subroutine
 B920: AND #$1F		' and truncate RNG value to needed form
 B922: CMP #$15		' if this value is still < 15h then drop normal Block
 B924: BCC $B939
 B926: CMP #$1E		' or if this value is > 1Eh
 B928: BCS $B939		' then drop normal Block
 B92A: STA $061B,X	' between 15h and 1Eh... this branch of code drops
			' item with ID = value from RNG
 B92D: LDA #$02
 B92F: STA $0668,X	' ... technical stuff ...
 B932: LDA #$FF		' ... make all needed to convert enemy into item ...
 B934: STA $067E,X
 B937: BNE $B94B		' finally EXIT ---------------------------
 B939: LDA #$14		' here is the branch of code which drops normal Block
 B93B: STA $061B,X
 B93E: LDA $067E,X	' ... technical stuff ...
 B941: EOR #$FF
 B943: STA $067E,X
 B946: LDA #$00
 B948: STA $0668,X
 ' EXIT is here
VB translation:
 EnemyHit_Routine:
 ' first we found out that current object was hit by player's weapon
 ' now we must decide whether to turn this enemy into Block or something
 temp_a = GlobalTimer
 temp_a = temp_a AND 01111111				' clear 8th bit
 if temp_a <> 0 then goto Drop_Normal_Block
   temp_a = RND(255)					' Call RNG once
   temp_a = temp_a AND 00011111				' crop generated value
						' to the range of 00-1F
   if temp_a < 15 then goto Drop_Normal_Block
   if temp_a >= 1E then goto Drop_Normal_Block
 else						' DropRareItem
     Object_ID(current_object_number) = temp_a		' Turn enemy object into item object
     temp_a = 2						' ... technical stuff begins ...
     ' each object has a number of properties like X/Y coordinates, speed and so on
     Object_some_property(current_object_number) = temp_a
     temp_a = FF
     Object_Direction(current_object_number) = temp_a		
     ' direction property: 00 means `goes to the left`, FF - `to the right`
     goto EXIT
 Drop_Normal_Block:
     temp_a = 14				' see Item Table - 14 means `Normal Block`
     Object_ID(current_object_number) = temp_a	' turn into Block
     temp_a = Object_Direction(current_object_number)	' take previous Direction of this enemy
     temp_a = temp_a XOR 11111111			' reverse direction
     Object_Direction(current_object_number) = temp_a	' write property into object descriptor table
     temp_a = 0
     Object_some_property(current_object_number) = temp_a	' write property into
							' object descriptor table
 EXIT:							'EXIT is here
So in fact the game is violence-free. :) You don't kill anyone, you just turn enemies into objects of another nature. Here's the list.
ITEMS TABLE:
0-14Blocks
15+1 Life
16Beads (turn all enemies on screen into Blocks)
17Rice Ball (restore energy)
18Bag of money (500 points)
19Whistle (freeze enemies)
1ATorch (temporary invincibility)
1BMirror (walk through walls and enemies)
1CSeaShell (power up for energy meter)
1DDark Block (+10 Blocks)
1EBlock
1FBlock
Particularly important for speenrun is Mirror item. It allows you walk through walls, so you can easily pass through undestructable areas of muschievously designed level - without slowing down for a single frame.
The strategy is as follows:
  1. Prepare RNG_Substractor and RNG_Result values so that (RNG_Result - RNG_Substractor) = 1Ch
  2. Before you need to walk through wall, find a moment when GlobalTimer = 0 (or 80) and shoot some enemy
  3. Pick Mirror Item. Now you have 320 frames of unstoppable movement. If you're stuck in a wall, the `mirror mode` timer will freeze at 05, and the game tries to shift player object to the right (but much slower then in Megaman)
Useful note:
You can shoot moving Block item (which is item object) and it'll turn into an item (usually - another Block). Most of players think that they can change direction of moving Block by shooting at it, but actually they destroy one Block and therefore release another (moving to reverse direction). Don't need to say that if you shoot some moving Block while timer reaches zero, you'll turn the Block into something more useful.
Conclusion:
Controlling the luck saved about 10 seconds in my run. Another 2 seconds were saved by better precision with bosses and level tactics. Plus 205 frames come from starting the game earlier.
Morimoto's run was very solid, and without this luck manipulation it couldn't be improved by more than several seconds. I wouldn't start this TAS without such interesting discovery, and I think the run deserves publishing not because of improving old Famtasia run, but because this insignificant small game suddenly proved to be well-suited for TASing.

Level by level

The section contains kind of spoilers, so you'd better watch the movie before reading.

Zone 1

Level 1

Quick and agressive start. If you didn't play the game before, here you can understand what the "Mirror" item can be used for.
Usually you are supposed to walkthrough this level and enter cave teleport only after second pass, but I'm way too impatient.

Zone 2

Level 9

At first I've tried to manipulate Moai heads to drill best path for me, but then realised it's faster just to crack one block by triplejump. The experience of luck manipulation comes in handy later.

Level 10

While it's absolutely unnoticeable, there are two critical moments of luck manipulation for level 12.
When the character walks on top of screen, he can shoot up with frequency of 1 shot per 2 frames. Hmm, this shouldd be abused...

Level 11

Notice that some enemies can be manipulated by player's coordinates or by blocking their way.
First boss encounter! This Ridley-like alien never uses RNG, so I have nothing to worry about.

Level 12

In the level I trade 5 frames (because of lag) to make three enemies move in certain way. Hehe, I'm fond of shoot-em-ups.
And now at the end of the level luck manipulation makes its first appearance.

Level 13

Okay. Here's secret base of KONAMI. :) Here's where they create those addictive games and compose hell-catchy musics.
I couldn't resist to improvise some noise-channel marches with the character's weapon. Check them out.

Level 9

Aww, now I see there's possible improvement - theoretically, by using mirror item you can take lower path (through walls) under broken bridge - therefore save 25 frames because of cracking teleport door faster.
But luck manipulation would cost 5-10 frames, and I've noticed this improvement too late to redo my further luck manipulations just for these 15 frames.

Zone 3

Level 15

You'll remember this level. And the boss battle. Not because they are remarkable or anything, but because you'll see all this at least TWICE!

Level 16

Technical note: in the level you gain speed twice slower and lose it (by releasing directional button) also twice slower. That's how ice surfaces work here.

Level 14

Rather ordinary level. But I had to heavily hexedit it several times to prepare finest case for Lv.16 "luck loop".

Level 15

Great. You are to play the same level up to the end and kill the same dino's skeleton.
I've intentionally made this level walkthrough similar to previous pass (in fact just copied buttons with hexeditor).
Of course I could play the level manually, but it would look even more boring then it looks now (hey, maybe not so boring after all). And it would be so LESS challenging (because actually there are differences in enemy spawns).
At other hand, hexediting finally has a chance to make its visual appearance - usually its work can't be figured by viewer. Here's my idea. Usually you can judge about used savestates by seeing extreme luck manipulation or risky movements. You can judge about used slowdown/frame advance by seeing perfect reaction and inhuman freaquency of button presses.
This level shows something that could be done only with The_Great_Hexeditor_Tool. No human can repeat his own actions so closely (even with savestates/slowdown/etc). And hey, that's what TA is about - show something that normal player cant show without tools.
Anyway, if you want to fast-forward this level, don't miss boss battle - at the end I have some free time, just enough to build my initial letter.

Level 16

The walkthrough looks different from previous one, but you still may feel urgent need to fast-forward all the stuff up to the moment when I've used Mirror item second time.
Note: the character can't enter portal door while being in `mirror mode`. That's why I took Mirror much earlier (in the middle of level) and managed to save its effect to the end by frame-critical actions.

Zone 4

Level 8

Generally, the level was designed to be the last. The main difficulty for non-TA player is shaking screen.
But for TAS player it's pretty boring, so just for fun I exploited luck manipulation to the extreme.
The evil scheme is following. Remember RNG code? Each time RNG just substracts RNG_Substractor from RNG_Result, and then RareItemDrop routine prunes this result to a value from 00h to 1Fh.
When we call RNG manually, RNG_Substractor doesn't change, because this time GlobalTimer = 0 or 80h.
Wouldn't it be great to have RNG_Substractor = 0 and RNG_Result = 1Ch (see Items Table)? This way we can have rain of Mirrors (or any other item - 1 RareItemDrop per 80h frames), because RNG_Result doesn't change after substraction.
What's more interesting, RNG_Substractor can be more then zero, doesn't matter until it is dividable by 20h. And RNG_Result can be 3Ch or 5Ch (20X + 1C).
So I rewrote couple of levels and created "luck loop". Wohoo!

Zone 1

Level 1

Even though previous boss tried to spoil the fun with "luck loop", i still saved half of its characteristics. No matter how many times I abuse luck, RNG_Substrcator and RNG_Result remain ready for another Mirror drop.
Boss of this level can be thrown out of room, which is faster then making 16 shots (1 per 6 frames).

Level 2

Luck loop continues. Boss room lags as hell, so I took a Torch Item and killed the most laggy enemy without releasing chisel weapon (additional object on screen would make lag even worse).

Level 3

Very irritating level. I was upset by repetitive design, so the level is mostly walked by air - over all obstacles.
The boss makes 2 RNG calls which can be neutralized by choosing right frame of encounter. I still need my luck loop for Lv.5. So during the level I had to lose 6 frames, which is a reason to fool around a little bit.

Level 4

The most boring level. An excuse to start and finish it with 0 blocks in stack. At the end you can hear famous "KONAMI pause" sound, which doesn't cost any frame. Hey, it's not like you can usually listen for it in speedruns.

Level 5

At last this luck loop actually saves good amount of time. I wonder if developers realized the boss room can be acessed without knocking the door.

Level 6

Unsteady bridge logs are objects as well as enemies and Blocks. Which means they can drop items too! While those logs don't have any collision box, we stiil can destroy them by taking Beads item (destroys every enemy on screen).

Level 7

With correct timing there's a way to jump through the ceiling (what's more important - not a single frame were lost) and remember the ability to drum.
Platformer game should have final boss, and let it be this poor guy.
Enjoy simple and beautiful ending theme.

Possible improvements

Level 9 - boss room. Maybe there's a way to change RNG_Substractor value so that Moai heads will destroy blocks for you. 10 frames.
Level 12. You can avoid 5-frame lag mentioned above by simply destroying ships and riuning the idea. 5 frames.
Level 9 - before teleport. It would be great to take Mirror and reach the teleport much earlier (running under bridge), but it's really hard to properly manipulate luck right after using Mirror at level 12. Up to 20 frames.
I didn't try to compare standard route (when Lv.8 = last) and my current one - but everything speaks that there's no significant defference. Only you can stop movie several frames earlier with standard route.
Note: I've tried to kill bosses instantly with Torch item (with this item you kill enemies by touching them), but bosses don't react on this.
Also, while entering any boss battle or piece room there's one unavoidable frame of lag.
That's all, I guess. Всем бабай! :)

Bisqwit: Accepting and processing. I like how you went beyond normal in TASing this game. I didn't expect the route to change, and your usage of the mirrors to speed up reaching the bosses was very clever.
Last Edited by on 1/1/2022 6:13 PM
Page History Latest diff List referrers