don't click here

Some changes/fixes for Sonic 1

Discussion in 'Engineering & Reverse Engineering' started by RetroKoH, Sep 4, 2012.

  1. StephenUK

    StephenUK

    Liquor in the front, poker in the rear Tech Member
    1,678
    0
    16
    Manchester, UK
    Quackshot Disassembly
    I'd probably say locking the controls from the moment the final hit has landed and playing out that entire section with pre-recorded movement would be the best way of doing it. An instant control lock would also prevent people from being able to overfow the boss hit counter back to 255.
     
  2. Rika Chou

    Rika Chou

    Tech Member
    5,276
    169
    43
    Then you couldn't get that last hit. I would just put an invisible wall.
     
  3. Mercury

    Mercury

    His Name Is Sonic Tech Member
    1,740
    21
    18
    Location Location
    AeStHete
    This is what I did in ReadySonic.
     
  4. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    I did THIS... for Sonic 1. Wasn't all that hard, more time consuming than anything else, what with all of the separated .asm's and all. (One of the few drawbacks to this layout)

    Follow these steps to do the same:

    1. GET A FREE UNIVERSAL SST - You can do this a couple of ways... I chose to do this. (Based on RHS' SST guide)
    obActWid: Switch from $19 to $14.
    obPriority: Switch from $24 to $18.
    obInertia: Switch from $14 to $20. - This is the same SST as obColType, but we don't need to worry about this. Only one object in the game uses both.

    2. Perform Steps 2-10. If you applied my change to remove the functions that freeze objects when Sonic dies, you won't need to apply step 10. You can find it here, as well as a second step here.

    3. Caterkiller's collision flags bit is overwritten by the inertia variable... meaning that Sonic cannot interact with it. Let's quickly fix this:
    In 78 Caterkiller.asm, replace EVERY instance of obInertia with $1E. This is anim_frame_duration (I forget the S1 name for this variable... since I converted all of my equates to the S2 nomenclature)
    If you want, you can even give $1E a new local equate for use with this object. below "Cat_Index", place this line:

    Code (Text):
    1. cat_gvel:   = $1E           ; ground speed. (To replace the normal g_vel SST, as it is used for collision purposes)
    Other than Caterkiller, I found ZERO bugs. I haven't had ANY time to do wiki work... just barely had time to squeeze in some changes to RHS' Sonic 2 Recreation page... but I'll get this up in due time.
     
  5. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,200
    430
    63
    Japan
    Congratulations on porting the system over, it's nice to see non-techies trying these things out for themselves and making good progress, well done!
     
  6. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    I also used your code for the fades, and got my own white in and white out effect, similar to yours. I'm looking into re-working all of it to appear similar to this effect, but in the original 21 frames instead of the new 7. I'll post that here too.
     
  7. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    I'm re-bumping this thread again, to give you all a tutorial on a powerup I just finished for my new hack in progress: Sonic 1: Freehand (The Sketchhog hack)
    This powerup is called Ring Time. For those who don't know what this is... it turns your enemies into rings when in proximity.

    So, the first thing you will want to do, before anything else... is create a new frame for this monitor... using your art editor of choice (*cough* Insert shameless Sonic Triad plug here *cough*)
    If you can't be arsed to do art, just have it use the normal ring art, which is what I did when I first put this together.

    Now, go into _incObj/2E Monitor Content Power-Up.asm and scroll down to the end of the powerup checks. Replace "Pow_ChkRings" with the following:
    Code (Text):
    1.  
    2.  
    3. ; ===========================================================================
    4.  
    5. Pow_ChkRings:
    6.         cmpi.b  #5,d0       ; does monitor contain 10 rings?
    7.         bne.s   Pow_ChkRingTime ; <---- ADD NEW CHECK HERE
    8.  
    9.         addi.w  #10,(v_rings).w ; add 10 rings to the number of rings you have
    10.         ori.b   #1,(f_ringcount).w ; update the ring counter
    11.         cmpi.w  #100,(v_rings).w ; check if you have 100 rings
    12.         bcs.s   Pow_RingSound
    13.         bset    #1,(v_lifecount).w
    14.         beq.w   ExtraLife
    15.         cmpi.w  #200,(v_rings).w ; check if you have 200 rings
    16.         bcs.s   Pow_RingSound
    17.         bset    #2,(v_lifecount).w
    18.         beq.w   ExtraLife
    19.  
    20.     Pow_RingSound:
    21.         music   sfx_Ring,1  ; play ring sound
    22. ; ===========================================================================
    23.  
    24. Pow_ChkRingTime:
    25.         cmpi.b  #6,d0       ; does monitor contain 'Ring Time'?
    26.         bne.s   Pow_ChkGoggles
    27.         move.b  #1,(v_ringtime).w   ; make Sonic able to turn enemies into rings
    28.         move.w  #$4B0,(v_player+$2A).w  ; time limit for the power-up
    29.         music   sfx_Ring,1  ; play ring sound
    30. ; ===========================================================================
    31.  

    Don't mind the check for Goggles, you can change that to Pow_ChkEnd if you don't want more powerups.
    Notice v_ringtime and v_player+$2A. The first one... just make an equate for it in Variables.asm whereever you want to. I'd do it somewhere near v_shoes.
    For the second one, $2A and $2B are unused SST's for Sonic, so they are now our RingTime powerup SST's. When we get the powerup, these will be set to a value, and they will ultimately tick down to 0, much like Speed Shoes and Invincibility do...

    Now, to make this expire after a limited time, go to _incObj/Sonic Display.asm. Below @chkshoes, add this block of code:
    Code (Text):
    1.  
    2.  
    3. @chkringtime:
    4.         tst.b   (v_ringtime).w  ; does Sonic have the powerup?
    5.         beq.s   @chkgoggles     ; if not, branch
    6.         tst.w   ringtime(a0)    ; check time remaining
    7.         beq.s   @chkgoggles
    8.         subq.w  #1,ringtime(a0) ; decrement timer
    9.         bne.s   @chkgoggles
    10.                 move.b  #0,(v_ringtime).w   ; cancel powerup
    11.  
    Replace @chkgoggles with @exit if you have no other timed powerups intended. Now if you are familiar with the above @chkshoes code, you already know what this does. It decrements the ring time timer, and disables the powerup.

    Now finally, to the piece de res.... ok I'll shut up. Here is how the powerup bears fruit. Go to _incObj/sub ReactToItem.asm. Replace "@proximity" with this (I attempted to comment stuff to help understand the code. My apologies if my comments aren't 100% accurate. I tried.)

    Code (Text):
    1.  
    2. @proximity:
    3.         andi.w  #$3F,d0
    4.         add.w   d0,d0
    5.         lea @sizes-2(pc,d0.w),a2 ; a2=other object's collision box size.
    6.         moveq   #0,d1
    7.         move.b  (a2)+,d1        ; d1=a1's collision width.
    8.         move.w  obX(a1),d0      ; d0=a1's x-position
    9.         sub.w   d1,d0           ; d0=a1's x-position-minus its collision width. (Co-ordinate of its left side bounding box)
    10.         sub.w   d2,d0           ; d0=a1's left bounding box co-ordinate-(Sonic's position-8)
    11.         bcc.s   @outsidex   ; branch if not touching (positive value or 0)
    12.         add.w   d1,d1           ; d1=a1's collision width*2 - a1's RIGHT bounding box co-ordinate
    13.         add.w   d1,d0           ; d0=(a1's left bounding box co-ordinate-(Sonic's position-8))+ d1, to check the right side.
    14.         bcs.s   @withinx    ; branch if touching (negative value)
    15.         bra.w   @next
    16. ; ===========================================================================
    17.  
    18. @outsidex:
    19.         tst.b   (v_ringtime).w   ; is Ring Time active?
    20.         beq.s   @skipx           ; if not, branch
    21.         cmp.w   #$50,d0          ; is the enemy within range, x-wise?
    22.         bcc.s   @skipx           ; if not, branch
    23.         move.b  obColType(a1),d1 ; load collision type
    24.         andi.b  #$C0,d1     ; is object an enemy?
    25.         bne.s   @skipx          ; if no, branch
    26.         tst.b   obColProp(a1)   ; is enemy a badnik?
    27.         beq.w   RingTime_Enemy  ; if yes, branch
    28.  
    So basically, this checks if an object is colliding with Sonic's sides. If it is NOT, then it checks to see if the object is in proximity. ($50 pixels). If it is, it then checks if the object is a badnik, by first checking obColType(a1) o see if it is an enemy, then checking obColProp(a1) to see if it is a boss. if every thing checks out... then we proceed. We can replace @outsidey with essentially the same code, as you can see below:

    Code (Text):
    1.  
    2.  
    3. @outsidey:
    4.         tst.b   (v_ringtime).w   ; is Ring Time active?
    5.         beq.s   @skipy           ; if not, branch
    6.                 cmp.w   #$50,d0          ; is the enemy within range, y-wise?
    7.                 bcc.s   @skipy           ; if not, branch
    8.                 move.b  obColType(a1),d1 ; load collision type
    9.         andi.b  #$C0,d1     ; is object an enemy?
    10.         bne.w   @skipy          ; if no, branch
    11.         tst.b   obColProp(a1)   ; is enemy a badnik?
    12.         beq.s   RingTime_Enemy  ; if yes, branch
    13.  
    Now, finally let's get to RingTime_Enemy:
    Code (Text):
    1.  
    2.  
    3. RingTime_Enemy:
    4.                 move.b  #id_RingBounce,0(a1) ; change object to bouncing ring
    5.         move.b  #0,obRoutine(a1)
    6.         rts
    7.  
    Essentially turns the badnik into rings. Now I used a new ring object for this... partly because using the standard lost rings will be a headache... IMO, THAT and you cannot collect lost rings in Sonic Freehand. If you do use RingLoss object for this... you'll have to do a little tweaking... as it is the RingLoss object that also reduces your rings to zero as it spawns the other rings.

    So... yep, that's what I've got. It works fine considering the circumstances of my hack, but perhaps it isn't perfect for the standard Sonic 1 hack YET. If I did something wrong, or if something could be improved, please help me to make this better. I haven't tested this in stock Sonic 1, and the conditions of that game are far different than my hack... so it may work fine for me, while it could cause bugs for you. I hope that someone experienced enough can help us perfect this.

    Hope this can be of use to anyone.
    -KoH
     
  8. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,587
    10
    18
    United Kingdom
    YouTuber
    Wrong. Very FIRST thing you do... is back-up your disassembly =P
     
  9. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    Ah yes... I should've put that in. I'd say that I thought that would come naturally to anyone looking to implement this... but I know I'd be surprised at how wrong I am. XD
     
  10. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    Took this from Mercury's ReadySonic. I've had so much use for this mod... and it's already on a public forum with ReadySonic, so there's no reason why it isn't in the SCHG already. Added the page just now. It's not my best writing, I apologize.
    http://info.sonicretro.org/SCHG_How-to:Dynamic_Special_Stage_Walls_system

    I also made a small note about the fix Cinossu made for smoother rotation at the end of this guide.
     
  11. One simple hack I thaught about -originally for S3 but could apply to all MegaDrive titles as well- is to escape the 60 frames per second in the collissions engine.

    Let me explain: The MegaDrive outputs 60 images per second, but the whole game physiscs are based on that clock.
    My idea was to initially test how the engine changes, but not aletering the physics. This could be done processing twice for object collisions per frame, but moving objects by half every step.
    This way, sonic would be moved twice per frame, but at half speed, checking for collissions evey time.

    Why mod this? Remember high speed glitches that don't happen at lower speeds? Maybe this could be a way to go around that limitation, because the collissions engine would be moving objects at half speed. Also, this mod would also allow to go for higher speeds in the game, exceeding those 16 pixels per frame < maybe this is a little too much (even unneeded), but it'd be easy to achieve.

    Yes, physics would be altered, but would be almost unnoticiable - this according to some simple simulations I've done.

    Taking this concept further, the clock rate could be made variable and switch to this supposed higher precision only at higher speeds, and keep the lower speed sections with their former physics.

    Maybe some of you experts out there could tell if this is possible, or if this could help achieve more continuity in the fastest sections of some levels -especially in S3.
     
  12. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    I'm no expert, but considering how the game sometimes lags just trying to run at 60fps, there's pretty much no way you'd be able to double the amount of processing it does per frame.
     
  13. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,161
    65
    28
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    As someone that has taken extreme measures in my hack to reduce the lag caused by an additional character, I can attest to what MainMemory is saying -- adding an extra character is nowhere near the same in terms of processing requirements as doubling all the updates for all objects, and it is bordering the limits of what the poor 68k can handle. Doubling all the game logic would basically make the game unplayable: you would end up with something like 60fps in physics updates displaying at maybe 30fps.
     
  14. Ok, I accept that. But since the character is moving half the distance, the only processing that should be done is character repositioning. I mean, this would only double character repositioning when colliding with tiles.
    All other processing should remain the same, and once per frame.
    To get a bit more technical, instead of doing this:

    X[n+1] = X[n] + V[n]
    check for tile collision
    <continue to check other things and move objects>

    do this:
    X[n+1/2] = X[n] + V[n]/2
    check for tile collision
    X[n+1] = X[n+1/2] + V[n]/2
    check for tile collision
    <continue to check other things and move objects>

    As far as I suppose, this would similar to having 2 characters on screen. If so, this shouldn't need much more processing power
     
  15. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    Would there be any interest in a S2 Ring Manager in Sonic 1 guide? I had some time today... for once... and managed it. If so I'll try to find time to whip one up.
     
  16. E-122-Psi

    E-122-Psi

    Member
    2,470
    612
    93
    Okay, most hackers here have likely caught onto this bug ages ago, but since there's no tutorial or anything of it, here's one for beginners.

    The Roller badnik suffers from a slight graphics glitch due to it's VRAM clashing with leftover Caterkiller tiles, leading to this:

    [​IMG]

    The inclusion of Caterkiller's data in Spring Yard Zone seems to be an error as it isn't used anywhere, thus we can take it out of here fine. Simply go to inc/pattern load cues.asm and find the SYZ data.

    Code (ASM):
    1. ; ---------------------------------------------------------------------------
    2. ; Pattern load cues - Spring Yard
    3. ; ---------------------------------------------------------------------------
    4. PLC_SYZ:    dc.w 4
    5.         dc.l Nem_SYZ        ; SYZ main patterns
    6.         dc.w 0
    7.         dc.l Nem_Crabmeat   ; crabmeat enemy
    8.         dc.w $8000
    9.         dc.l Nem_Buzz       ; buzz bomber enemy
    10.         dc.w $8880
    11.         dc.l Nem_Yadrin     ; yadrin enemy
    12.         dc.w $8F60
    13.         dc.l Nem_Roller     ; roller enemy
    14.         dc.w $9700
    15. PLC_SYZ2:   dc.w 7
    16.         dc.l Nem_Bumper     ; bumper
    17.         dc.w $7000
    18.         dc.l Nem_SyzSpike1  ; large spikeball
    19.         dc.w $72C0
    20.         dc.l Nem_SyzSpike2  ; small spikeball
    21.         dc.w $7740
    22.         dc.l Nem_Cater      ; caterkiller enemy
    23.         dc.w $9FE0
    24.         dc.l Nem_LzSwitch   ; switch
    25.         dc.w $A1E0
    26.         dc.l Nem_Spikes     ; spikes
    27.         dc.w $A360
    28.         dc.l Nem_HSpring    ; horizontal spring
    29.         dc.w $A460
    30.         dc.l Nem_VSpring    ; vertical spring
    31.         dc.w $A660
    Now edit out Caterkiller and change the object number accordingly like so:

    Code (ASM):
    1. ; ---------------------------------------------------------------------------
    2. ; Pattern load cues - Spring Yard
    3. ; ---------------------------------------------------------------------------
    4. PLC_SYZ:    dc.w 4
    5.         dc.l Nem_SYZ        ; SYZ main patterns
    6.         dc.w 0
    7.         dc.l Nem_Crabmeat   ; crabmeat enemy
    8.         dc.w $8000
    9.         dc.l Nem_Buzz       ; buzz bomber enemy
    10.         dc.w $8880
    11.         dc.l Nem_Yadrin     ; yadrin enemy
    12.         dc.w $8F60
    13.         dc.l Nem_Roller     ; roller enemy
    14.         dc.w $9700
    15. PLC_SYZ2:   dc.w 6
    16.         dc.l Nem_Bumper     ; bumper
    17.         dc.w $7000
    18.         dc.l Nem_SyzSpike1  ; large spikeball
    19.         dc.w $72C0
    20.         dc.l Nem_SyzSpike2  ; small spikeball
    21.         dc.w $7740
    22.         dc.l Nem_LzSwitch   ; switch
    23.         dc.w $A1E0
    24.         dc.l Nem_Spikes     ; spikes
    25.         dc.w $A360
    26.         dc.l Nem_HSpring    ; horizontal spring
    27.         dc.w $A460
    28.         dc.l Nem_VSpring    ; vertical spring
    29.         dc.w $A660
    Voila. Roller's graphics should be fixed.

    I've one query that's puzzled me since I was a kid, I've noticed the debug mode causes some VERY odd occurrences in Special Zone. It doesn't implement objects properly or even display most of them and exiting out of the maze can float you into these strange random areas with limited collision and numerous beta objects (solid emeralds can also be collected there). What would this be classified as exactly? Glitch? Unfinished programming? Necessary garbage data? What the heck is going on in this zone?

    I also noticed the HUD objects are in the VRAM for this level as well, leaving me wondering whether it was supposed to be used in there (has anyone tried lasting ten minutes in there?).
     
  17. nineko

    nineko

    I am the Holy Cat Tech Member
    6,307
    483
    63
    italy
    Actually :P

    Still, I believe this actually is the first time that the fix is detailed in a proper form, so nice work for that. It's one of those little things which pop up from time to time and everyone thinks "it's already known", so it's good to have it posted in a proper topic.
     
  18. AURORA☆FIELDS

    AURORA☆FIELDS

    The cute one here Tech Member
    216
    24
    18
    Finland
    AMPS
    Exiting out of the maze can make you go on areas which read incorrect RAM for layout data, which so happens to be garbage. The actual layouts are loaded on RAM and displayed as you move, while you go out of bounds you find random broken things. The stage does not have any actual objects, aside from Sonic, so they will follow the stage position, and not the stage graphics, so it seems all wonky. The HUD is likely something unimplemented on the stage, I don't know why its there, but likely for no purpose, just leftover. I am not sure if being on the stage for 10 minutes will kill you, its something to look into.
     
  19. DigitalDuck

    DigitalDuck

    Arriving four years late. Member
    5,349
    436
    63
    Lincs, UK
    TurBoa, S1RL
    This is the same reason you find garbage tiles underneath Sonic 3's maps - it's doing exactly the same thing.
     
  20. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    The HUD art appears in VRAM as its not overwritten during the transition from zone to SS. Unless I'm mistaken... You won't find it in VRAM going there from Level Select immediately upon booting the game.

    Secondly there is no time over in the Special Stage, that is... Unless you hack it in.