Showing posts with label Icecap Zone. Show all posts
Showing posts with label Icecap Zone. Show all posts

Wednesday, January 24, 2018

Have Tails activate star posts as quickly as possible.

Reader Brainulator9 asked:
Do you plan to discuss avoiding Tails getting stuck in passageways or getting stuck in traps by activating a Star Post or entering a Special Stage as quickly as possible?
Brainulator9 is talking about a passage that appears in page 27 of the US Sonic 3 manual, directly before the infamous "diabolical traps" line. Here's what it reads:
In the IceCap Zone and throughout the game, avoid having Tails get stuck in passageways or get caught in traps. Have Tails activate Starposts or enter into a Special Stage as quickly as possible.
What could this oddly specific phrasing be referring to? If the "diabolical traps" thing is any indication, this is likely a bug acknowledgment and its suggested workaround.

Well, there is indeed an S3A-exclusive bug regarding Tails in Icecap Zone, which occurs roughly 100% of the time. You see, Icecap Zone 1 is normally a vertically-wrapping level. However, when you first start the level as Tails, the level size is modified in order to have Tails fall from the top of the screen.


Evidently, the developers never bothered playing past this sequence, because at no point is the level size ever restored back to its regular state. As a result, when Tails reaches the point where the level should wrap upon itself, he's instantly killed by a mysterious, unseen force.


The workaround? Well, the level size is only supposed to change when Tails first enters the stage, so the relevant code only runs if the Last_star_post_hit variable is zero. The variable is set to a non-zero value when the player activates a star post or enters a special stage, so doing either will prevent the bug from reoccurring after the player respawns.

Monday, December 25, 2017

Christmas corrections

Today is Christmas Day, a holiday which is typically celebrated by showering the people you love the most with copious amounts of gifts. Keeping with tradition, then, I thought this would be the perfect opportunity to celebrate three gifts that my readers have graciously offered me through the comments section of this blog.

(Stuttering Craig voice) This is Sonic 3 Unlocked's 2017 Top 3 Christmas Corrections!



Number Three!

As part of my short series on Lock-on Technology, I pointed out a difference with Knuckles' climbing animation between Knuckles in Sonic 2 and Sonic & Knuckles: exclusively in the latter, whenever Knuckles stands still on a wall, he reverts back to the first frame of the climbing animation.


I chalked this up to a feature introduced in the S&K version of the Knuckles object, but later, an anonymous commenter performed their own analysis of the source code, which I present below. Turns out, it's not actually a feature, it's a bug:
I think the second behaviour quirk you mentioned in this post is the result of a bug. There's some code in S3K that isn't in KiS2, at loc_16E10 in the current S3K Git disasm. The equivalent label in KiS2's Git disasm is loc_315B04.

What I think this new code does is handle floor collision, because Knuckles still seems to move briefly after the player stops pressing the D-Pad. The issue is, this new code overwrites d1 with the distance Knuckles is from the floor. d1 is checked immediately afterwards, has Knuckles's frame ID added to it, and is then used to calculate which frame Knuckles should display.

d1 will always be a positive number, usually a large one depending on how far Knuckles is from the ground. This means, when Knuckles's frame ID is added to it, it goes well beyond the ceiling value of $BC, causing the game to reset it to $B7, making Knuckles display the first frame of his animation. Chances are the number could overflow, too, causing him to display his last frame instead.

Safe to say, editing the code to properly back up d1 causes it to behave like KiS2 instead.
Let's take a look at the code mentioned. Knuckles in Sonic 2 to the left, Sonic & Knuckles to the right. Changes in bold:
loc_315B04:                                     loc_16E10:
                                                    move.b  (Ctrl_1_logical).w,d0
                                                    andi.b  #3,d0
                                                    bne.s   loc_16E34
                                                    move.b  $46(a0),d5
                                                    move.w  $14(a0),d2
                                                    addi.w  #9,d2
                                                    move.w  $10(a0),d3
                                                    bsr.w   sub_F828
                                                    tst.w   d1
                                                    bmi.w   loc_16D6E

                                                loc_16E34:
    tst.w   d1                                      tst.w   d1
    beq.s   loc_315B30                              beq.s   loc_16E60
    subq.b  #1,$1F(a0)                              subq.b  #1,$25(a0)
    bpl.s   loc_315B30                              bpl.s   loc_16E60
    move.b  #3,$1F(a0)                              move.b  #3,$25(a0)
    add.b   $1A(a0),d1                              add.b   $22(a0),d1
    cmp.b   #$B7,d1                                 cmpi.b  #$B7,d1
    bcc.s   loc_315B22                              bhs.s   loc_16E52
    move.b  #$BC,d1                                 move.b  #$BC,d1

loc_315B22:                                     loc_16E52:
    cmp.b   #$BC,d1                                 cmpi.b  #$BC,d1
    bls.s   loc_315B2C                              bls.s   loc_16E5C
    move.b  #$B7,d1                                 move.b  #$B7,d1

loc_315B2C:                                     loc_16E5C:
    move.b  d1,$1A(a0)                              move.b  d1,$22(a0)
In Sonic 2, when loc_315B04 is reached, the d1 register is set to 1, -1, or 0 depending on whether Knuckles is moving up, moving down, or standing still. Assuming neither branch to loc_315B30 is taken, Knuckles' current mapping frame is added to the value in d1, and then two bound checks are made before writing the resulting value back into Knuckles' mapping frame: if the value is less than $B7, d1 is set to $BC, and if it's greater than $BC, d1 is set to $B7.

The gist of it is: while Knuckles is climbing up a wall, his mapping frame gets progressively incremented, but when he's climbing down, it gets decremented instead. And if the mapping frame ever steps outside of the $B7-$BC range, it gets wrapped around to the other end of the range, in order to loop the animation.

In Sonic & Knuckles though, a call to sub_F828 was introduced, causing the FindFloor function to be called whenever the player is holding neither up nor down on the directional pad. The FindFloor function calculates an object's distance to the floor directly below it, and stores the result in register... d1.

The inevitable result follows: when the player lets go of the directional pad, sub_F828 is called and the value in d1 gets overwritten with the distance between the center of the Knuckles object and the floor. Knuckles' current mapping frame is then added to this value, which always produces a value greater than $BC. This triggers the bounds check, resetting Knuckles' mapping frame back to $B7, the first frame of the climbing animation.

In other words, the anonymous commenter's analysis is 100% correct. Good work!



Number Two!

On the subject of triggering slope glitch in Ice Cap Zone by having Tails break an ice block while Sonic is standing on it, Brainulator9 asked whether Tails could get slope glitch by instead breaking the block as Sonic. In Sonic 3 & Knuckles, this is impossible because player 2's status bits always get set, regardless of who breaks the blocks, and regardless of whether the Tails object is even present in the player 2 slot.


However, as Brainulator9 pointed out, the same isn't true of standalone Sonic 3, in which Sonic can indeed break Tails' gravity. Below is the relevant code: Sonic 3 to the left, Sonic & Knuckles to the right, once again changes in bold.
loc_58B3C:                                      loc_8B384:
    move.b  ($FFFFB020).w,$3A(a0)                   move.b  ($FFFFB020).w,$3A(a0)
    move.b  ($FFFFB06A).w,$3B(a0)                   move.b  ($FFFFB06A).w,$3B(a0)
    moveq   #$23,d1                                 moveq   #$23,d1
    moveq   #$10,d2                                 moveq   #$10,d2
    moveq   #$10,d3                                 moveq   #$10,d3
    move.w  $10(a0),d4                              move.w  $10(a0),d4
    jsr     (SolidObjectFull).l                     jsr     (SolidObjectFull).l
    bsr.w   sub_58B62                               bsr.w   sub_8B3AA
    jmp     (Sprite_OnScreen_Test).l                jmp     (Sprite_OnScreen_Test).l

sub_58B62:                                      sub_8B3AA:
    move.b  $2A(a0),d0                              move.b  $2A(a0),d0
    btst    #3,d0                                   btst    #3,d0
    beq.s   loc_58B78                               beq.s   loc_8B3C0
    lea     (Player_1).w,a1                         lea     (Player_1).w,a1
    cmpi.b  #2,$3A(a0)                              cmpi.b  #2,$3A(a0)
    beq.s   loc_58B8A                               beq.s   loc_8B3D2

loc_58B78:                                      loc_8B3C0:
    btst    #4,d0                                   btst    #4,d0
    beq.s   locret_58BD0                            beq.s   locret_8B430
    lea     (Player_1).w,a2                         lea     (Player_2).w,a1
    cmpi.b  #2,$3B(a0)                              cmpi.b  #2,$3B(a0)
    bne.s   locret_58BD0                            bne.s   locret_8B430

loc_58B8A:                                      loc_8B3D2:
    bset    #2,$2A(a1)                              bset    #2,$2A(a1)
    move.b  #$E,$1E(a1)                             move.b  #$E,$1E(a1)
    move.b  #7,$1F(a1)                              move.b  #7,$1F(a1)
    move.b  #2,$20(a1)                              move.b  #2,$20(a1)
    move.w  #-$300,$1A(a1)                          move.w  #-$300,$1A(a1)
    bset    #1,$2A(a1)                              bset    #1,$2A(a1)
    bclr    #3,$2A(a1)                              bclr    #3,$2A(a1)
    move.b  #2,5(a1)                                move.b  #2,5(a1)
                                                    btst    #4,$2A(a0)
                                                    beq.s   loc_8B41A
                                                    lea     (Player_2).w,a1
                                                    bset    #1,$2A(a1)
                                                    bclr    #3,$2A(a1)

                                                loc_8B41A:
    lea     ChildObjDat_58C20(pc),a2                lea     ChildObjDat_8B480(pc),a2
    jsr     CreateChild1_Normal(pc)                 jsr     CreateChild1_Normal(pc)
    moveq   #$6E,d0                                 moveq   #$6E,d0
    jsr     (Play_Sound_2).l                        jsr     (Play_Sound_2).l
    jsr     (Go_Delete_Sprite).l                    jsr     (Go_Delete_Sprite).l

locret_58BD0:                                   locret_8B430:
    rts                                             rts
Both versions of the code call the SolidObjectFull function, and then check bits 3 and 4 of the status bitfield along with the animation of the corresponding player, which is previously backed up to offsets $3A and $3B, in order to determine whether the player landed on the object whilst in their rolling animation.

Note how thoroughly botched the checks for player 2 are in Sonic 3, though: player 1's RAM address is loaded instead of player 2's, and it gets loaded to register a2 rather than register a1. The only reason this code works at all is because the SolidObjectFull function itself sets a1 to player 2's RAM address during the course of its execution, and then exits without overwriting the contents of the register with something else:
SolidObjectFull:
    lea     (Player_1).w,a1
    moveq   #3,d6
    movem.l d1-d4,-(sp)
    bsr.s   sub_1BA2A
    movem.l (sp)+,d1-d4
    lea     (Player_2).w,a1
    tst.b   4(a1)
    bpl.w   locret_1BA6A
    addq.b  #1,d6

sub_1BA2A:
    ...
That isn't the problem in and of itself, however: the problem is that the code at loc_58B8A only runs for a single player, which leaves the other player hanging if they happened to also be standing on the ice block at the time. Rather than fix this properly, Sonic 3 & Knuckles simply forces player 2 to fall off the block either way, resulting in the strange, lopsided behavior where player 1 can get slope glitch but not player 2.



Number One!

Finally, regarding the Japanese characters in the slot machine bonus stage, another anonymous commenter points out that if you read them vertically, top to bottom, then left to right, they make up the first sixteen letters of the Iroha.


Now, what is the Iroha? It is an ancient Japanese poem, which has the unique characteristic of using every single kana character exactly once. (The title refers to the first three characters used in the poem.)

γ‚€γƒ­γƒγƒ‹γƒ›γƒ˜γƒˆ iro ha nihoheto
チγƒͺγƒŒγƒ«γƒ²   chirinuru wo
ワカヨタレソ  wa ka yo tare so
γƒ„γƒγƒŠγƒ©γƒ    tsune naramu
γ‚¦γƒ°γƒŽγ‚ͺγ‚―γƒ€γƒž uwi no okuyama
ケフコエテ   kefu koete
γ‚’γ‚΅γ‚­γƒ¦γƒ‘γƒŸγ‚· asaki yume mishi
ヱヒヒセス   wehi mo sesu

Since each character only appears once, the Iroha serves as an alternative to the usual gojΕ«on ordering, but both work equally well as placeholder graphics for a level's animated PLCs.



That's all I've got. Thank you all so much for the valuable feedback; I hope every single one of you has a terrific holiday season, and don't forget:

Thursday, November 23, 2017

Unused graphics: ice rock and ice block

Two more unused sprites, this time in Icecap Zone. One of the PLCs from act 1 contains what appears to be a large ice boulder. No object in the final game uses the sprite, but the mappings are still in the ROM and... this thing is huge.


The mappings cleverly reuse the same pieces in the top and bottom halves of the sprite, as to maximize its size without using up too many tiles. It fits snugly in act 1's narrow corridors, so maybe that's where it was meant to be used.


The second sprite is present in both acts, and has a fully-functional object associated with it, which can be placed using debug mode. It's a square block that breaks when you jump on it, similar to an obstacle used in Sonic 1's Marble Zone.


Its size and appearance suggest it was meant to be placed in the chutes which lead into act 2's underground area. The final game plugs up the top of these chutes using a different object, which is in turn lifted from Sonic 2's Hill Top Zone.

Thursday, September 21, 2017

Solid object collisions

There are two kinds of collisions that can be registered between the player and other objects, which we might call solid collision and responsive collision. As I've previously mentioned, an object can obtain solid collision by calling a function such as SolidObjectFull.

A couple of important things happen when the player touches the top of one of these objects. First, the object sets bit 3 of the status bitfield, at offset $2A of the player's SST. This flag signals the player object that it's presently standing on top of an object, so it should skip over the code that checks the level blocks at the player's feet and determines whether the player should be falling or not.

The next thing the object does is copy the address of its own SST to offset $42 in the player's SST. This is done so the player object can later consult the state of the object it's standing on and react accordingly. In practice, this is only used to look up the object's x_pos and width_pixels values, to determine whether the player is close enough to the edge of the object that one of the teetering animations should play.

Lastly, the object also sets bit 3 or 4 of its own status bitfield, depending on which player is standing on it. This is sort of redundant, because the two player SSTs are both always at the same RAM address, meaning the object could just look up the previous two offsets to obtain the same information, but there you go.

It's important to note that the player object puts no effort into ever clearing its own "standing on object" flag: it's the solid object's responsibility to keep calling the SolidObjectFull function, check whether the players are still there, and if not, clear their "standing on object" flags depending on the value of its status bitfield. Breakable objects should take care to also clear the flags when they're destroyed.

Armed with this knowledge, you should be able to understand why the "slope glitch" behaves as it does.


Due to an oversight in the object's code, when Tails breaks the ice block, Sonic's "standing on object" flag isn't cleared. The code that checks the level blocks at his feet continues to be skipped, causing him to float, and since the block has been destroyed, it will never detect Sonic running off the edge, and so he'll stay up there until he jumps, which sets his "airborne" flag.

When he lands on the ground, Sonic is affected by the floor's angle for a single frame. Afterwards, his "airborne" flag is once again cleared, whereas his "standing on object" flag is still set, so the code that checks the level blocks at his feet gets skipped again, making him float once more.

Because the block's SST got zeroed out when it was destroyed, its x_pos and width_pixels report an infinitely narrow object whose center is all the way at the start of the level, activating the teetering animation.

The only way out of this mess is to jump on a different object, and then either run or jump off of that one. If nothing else goes wrong, the second object will clear the "standing on object" flag properly and disable the glitch.

Tuesday, September 5, 2017

Act transitions, part 7: putting it together

Anonymous commented on my post about the Marble Garden Zone act transition crash in standalone Sonic 3.
Though this kind of horizontal underflow is not Sonic 3 exclusive - it can be also done with Hyper Sonic in ICZ1. After glitching through Knuckles' path to his boss, nothing prevents you to trigger the transition and immediately double jump back to spawn outside of the level.

This doesn't freeze the game, but it does reset it.
Actually, the game only resets because that's the default handler for most errors in Sonic 3 & Knuckles. If you try doing it in standalone Sonic 3, you'll get a freeze as usual.

To elaborate on the glitch: if you go to the exact point where the Icecap Zone act transition occurs, get the camera just barely inside the transition area and then quickly double back, you can trigger the initial part of the transition but return to the act 1 layout before the level data finishes loading and the game switches over to act 2.


Hyper Sonic's double jump ability makes this exceedingly easy, but it can still be done otherwise. This glitch cannot be performed in Sonic and Tails' path, because a one-way barrier appears to prevent any backtracking. In Knuckles' path, the barrier only appears beyond the point where the transition takes place, in the act 2 layout.

When the act transition occurs under these circumstances, once again the camera's position is set to a negative value, causing the ring and object loading routines to go berserk and swiftly destroy the entire game. This time, however, not before treating us to some nice computer-generated art.


TASVideos forum user Chrezm recently posted about this glitch, having triggered it by zipping along the floor and then saving the game from death by entering debug mode. When he did this, he managed to spawn the Launch Base Zone act 1 boss in the Icecap Zone 2 layout, which is a pretty exciting prospect should speedrunners ever manage to abuse this to their advantage.

Wednesday, August 30, 2017

Act transitions, part 3: Icecap Zone

Icecap Zone's act transition also doesn't happen during the act 1 results. In fact, it holds the dubious honor of being the only transition which doesn't involve a screen lock: it takes place inside the long corridor right before the final star post.


While it was definitely a bold move, if you're going fast enough, the tunnel is unfortunately not long enough to cover up the load time, and you can catch the act 2 background while it's still decompressing. ICZ1's transition event specifically only waits for the Kosinski-compressed chunks/blocks to decompress and not the KosM tiles, almost certainly because otherwise, you would run straight into the act 1 loopback as the art slowly loads.
ICZ1BGE_Transition:
    tst.w   (Kos_decomp_queue_count).w
    bne.w   loc_53938
    move.w  #$501,(Current_zone_and_act).w
    ...
There's another quirk with this transition, and it has to do with the previously mentioned star post. If you hit it, and then enter/exit the bonus stage or lose a life, the title card will read "act 1", but the music playing will be that of act 2.


Internally, the game tracks two distinct values for the current zone and act. The first one is the actual zone and act, and picks which level actually gets loaded. The other is the apparent zone and act, which is what the game says the current level is. Specifically, the title card object uses the apparent act to give you an act 1 card when you're really in act 2.
    lea     (ArtKosM_TitleCardNum2).l,a1
    cmpi.w  #$1600,(Current_zone_and_act).w
    beq.s   loc_2D716
    cmpi.w  #$1700,(Current_zone_and_act).w
    beq.s   loc_2D716                       ; Death Egg Boss and LRZ Boss show act 2
    tst.b   (Apparent_act).w
    bne.s   loc_2D716
    lea     (ArtKosM_TitleCardNum1).l,a1

loc_2D716:
    move.w  #$A7A0,d2
    jsr     (Queue_Kos_Module).l
    lea     TitleCard_LevelGfx(pc),a1
    ...
The code that picks the level's music however, does not, so it picks the song for the level you're actually in, which is act 2. But wait a second. Angel Island Zone 1 also features a star post directly after the act transition, but when you spawn there, the game correctly plays the act 1 song.

Actually, the logic for the AIZ1 starpost is completely hardcoded into the music picking code. When you respawn from a big ring, the value of Last_star_post_hit is not restored by the time this code runs, so it picks the act 2 song again.
    move.w  (Current_zone_and_act).w,d0
    ror.b   #1,d0
    lsr.w   #7,d0
    lea     (LevelMusic_Playlist).l,a1
    move.b  (a1,d0.w),d0
    cmpi.w  #1,(Current_zone_and_act).w
    bne.s   loc_622A
    cmpi.b  #3,(Last_star_post_hit).w
    bne.s   loc_622A
    moveq   #1,d0

loc_622A:
    ...
    move.w  d0,(Level_music).w
    bsr.w   Play_Sound
    ...
Incidentally, this code is not present in standalone Sonic 3, so spawning in the act 2 layout will always play act 2 music.

Tuesday, August 22, 2017

Dynamic resize routines

Along with the camera bound variables we saw last time, there are also four matching "target" values, which are meant to smoothly change a level's size, such as after a boss fight. However, in Sonic 3, only Camera_target_max_Y_pos is wired up, and the other three do absolutely nothing. The code which gradually moves Camera_max_Y_pos towards its target position is in the Do_ResizeEvents function.
    Camera_target_min_X_pos = ramaddr( $FFFFEE0C ) ; word
    Camera_target_max_X_pos = ramaddr( $FFFFEE0E ) ; word
    Camera_target_min_Y_pos = ramaddr( $FFFFEE10 ) ; word
    Camera_target_max_Y_pos = ramaddr( $FFFFEE12 ) ; word
The Do_ResizeEvents function is also responsible for running each level's dynamic resize routines. These are called once every frame, and serve as a place where each stage can run logic to modify the level size based on the camera's current position.
loc_1CA20:
    cmpi.w  #$740,(Camera_X_pos).w
    blo.s   locret_1CA3A
    cmpi.w  #$400,(Camera_Y_pos).w
    bhs.s   locret_1CA3A
    move.w  #$740,(Camera_min_X_pos).w
    addq.b  #2,(Dynamic_resize_routine).w

locret_1CA3A:
    rts
For instance, in the routine for Icecap Zone 2 above, the game checks if the camera's X position is $740 or greater, and if so, locks the left boundary at the act 1 boss area. Note how it doesn't happen when the camera's Y position is greater than $400 for some reason: as a result, Knuckles can backtrack all the way to the pre-boss star post.


Similarly to "old"-style objects, resize routines behave like a state machine, with the current routine selected through an index byte stored somewhere in RAM. This byte is saved when you touch a star post or a special stage ring, so that the level is always in the appropriate state when the player respawns.
ICZ1_Resize:
    moveq   #0,d0
    move.b  (Dynamic_resize_routine).w,d0
    move.w  off_1C9DA(pc,d0.w),d0
    jmp     off_1C9DA(pc,d0.w)
; ---------------------------------------------------------------------------
off_1C9DA:
    dc.w loc_1C9E0-off_1C9DA
    dc.w loc_1C9FA-off_1C9DA
    dc.w locret_1CA0C-off_1C9DA
; ---------------------------------------------------------------------------
Much like the camera target variables though, dynamic resize routines have fallen out of style in the Sonic 3 codebase, with few stages actually using them. None of the stages in the Sonic & Knuckles half of the game have resize routines, but the system has to be carried around for backwards compatibility with the Sonic 3 stages.
LevelResizeArray:
    dc.w AIZ1_Resize-LevelResizeArray
    dc.w AIZ2_Resize-LevelResizeArray
    dc.w HCZ1_Resize-LevelResizeArray
    dc.w HCZ2_Resize-LevelResizeArray
    dc.w MGZ_Resize-LevelResizeArray
    dc.w MGZ_Resize-LevelResizeArray
    dc.w No_Resize2-LevelResizeArray
    dc.w No_Resize2-LevelResizeArray
    dc.w No_Resize2-LevelResizeArray
    dc.w No_Resize2-LevelResizeArray
    dc.w ICZ1_Resize-LevelResizeArray
    dc.w ICZ2_Resize-LevelResizeArray
    dc.w No_Resize-LevelResizeArray
    dc.w LBZ2_Resize-LevelResizeArray
    dc.w No_Resize3-LevelResizeArray
    dc.w No_Resize3-LevelResizeArray
    dc.w No_Resize3-LevelResizeArray
    dc.w No_Resize3-LevelResizeArray
    ...

Wednesday, July 26, 2017

One thing's for sure… you do exist

If you've picked anything up from my blogging style is that I like to first explain a topic in relative detail, and then follow it up with a practical example that illustrates what I'm talking about. Case in point, Icecap Zone 1:


When you break through one of these large walls as Knuckles, more often than not you'll come to a complete stop, and Knuckles will switch over to his pushing animation. I say "more often than not", because if your position happens to line up so that you're right next to the wall but not actually touching it, the wall will break before it kills your speed.

All of this happens because the breaking wall object in this stage does none of the things I mentioned before. This was fine in standalone Sonic 3, since the only thing that could break those walls was a sliding ice block.

Actually, here's a fun exercise: if you use debug mode to place one of those sliding blocks and then push it towards the walls guarding Knuckles' routes, you'll find that they'll break just fine. There's actually only one kind of breakable wall in this stage, and they prevent you from breaking into Knuckles' routes by simply not placing any ice blocks nearby.

Wednesday, July 12, 2017

The 2.5D waterline

Icecap Zone 2 also has a pseudo-3D effect going on in its background waterline. There's no vertical scrolling this time around, but on the other hand, the ice sheets floating on the water's surface display some realistic perspective as they scroll horizontally across the screen.


Once again, the parallax effect is achieved by scrolling each row of pixels faster than the one preceding it, but this time there's no DMA trickery involved. Turns out, the background itself was drawn skewed in advance, so by the time one of the four sets of ice sheets scrolls offscreen, the remaining three have deformed exactly the right amount in order for the animation to loop seamlessly.

Friday, May 26, 2017

It's probably even cooler now, when you think about it

There's an additional element which Sonic 3 inherited from Sonic 2's lost Hidden Palace Zone, other than its name. No, I'm not talking about the the large green brilliant-cut gemstone surrounded by equally green crystals sitting on top of an ornate cylindrical structure.


Sonic 2's Hidden Palace had these neat glowing bridges in which each individual segment would light up as Sonic and Tails walked across them. This cool object was not forgotten, and was refurbished for use in Sonic 3's Icecap Zone.


Alright, that's enough Sonic 2 for now. Next week, we'll resume our primer series by looking at what makes up a level.