ASM The Sonic 3 shield porting guide to Sonic 1

Discussion in 'Engineering & Reverse Engineering' started by DeltaWooloo, Nov 14, 2021.

  1. DeltaWooloo

    DeltaWooloo

    The sheep goes quack! Member
    Alright, lads and lassies, I wanted to do this for quite a while so here we are. For those who’ve seen videos of my hack on Discord, Twitter or YouTube, you may have noticed that I’ve included the insta-shield and elemental shield in my hack. Since you like those features, I figured I should demonstrate how to port them to Sonic 1. This post will show you how to port the Insta-Shield to Sonic 1. Later at some point, I’ll be sending my elemental shields guide port. If I forget, gimmie a shout. =P

    So if you want to get a bit of context as to what the Insta-Shield is and how it works in Sonic 3 and Knuckles, check out this link.

    This guide is meant for the 2005 Hivebrain disassembly, and I will reference the code in the tutorial. Original S3K code will be from GitHub’s disassembly; please download it as we will need some of its assets later throughout the tutorial. However, it should work if you want to add it into any other disassembly, such as GitHub or Sonic 2. Just be mindful of the code and label changes, especially with RAM and constants.

    Also, I don’t want to see an argument that the insta-shield will be “overused” in Sonic 1 hacks. This is an absurd view, in my opinion, as this tutorial teaches people how to port code from one disassembly to another and add new features. Also, if you are adding it, be sure to know if it’s worth porting over to your hack. I know that sounds vague, but I’d rather see the insta-shield have interesting purposes than just a gimmick.

    The tutorial still works, although I haven’t managed to get around to commentating on a few things. That will be done very soon. And also, this isn’t a copy and paste guide, and this guide teaches people how to differentiate between different disassemblies and add new moves to their hacks. Also, since this is a crosspost, I apologize about the gaps seen in code tags. That will be fixed when I have the time to do so.

    Also, be sure to install QueueDMATransfer by checking out here, and this has to be installed too. Click me. However, the uncompressed art links are dead so I uncompressed the art myself and linked it here.

    Before we add the code in, let’s check how Sonic 3 and Knuckles manages the object:


    Code (Text):
    1.  
    2. Obj_Insta_Shield:
    3.  
    4.         ; Init
    5.  
    6.         move.l #Map_InstaShield,mappings(a0)
    7.  
    8.         move.l #DPLC_InstaShield,DPLC_Address(a0) ; Used by PLCLoad_Shields
    9.  
    10.         move.l #ArtUnc_InstaShield,Art_Address(a0) ; Used by PLCLoad_Shields
    11.  
    12.         move.b #4,render_flags(a0)
    13.  
    14.         move.w #$80,priority(a0)
    15.  
    16.         move.b #$18,width_pixels(a0)
    17.  
    18.         move.b #$18,height_pixels(a0)
    19.  
    20.         move.w #ArtTile_Shield,art_tile(a0)
    21.  
    22.         move.w #tiles_to_bytes(ArtTile_Shield),vram_art(a0) ; Used by PLCLoad_Shields
    23.  
    24.         btst #7,(Player_1+art_tile).w
    25.  
    26.         beq.s .nothighpriority
    27.  
    28.         bset #7,art_tile(a0)
    29.  
    30.  
    31.  
    32.     .nothighpriority:
    33.  
    34.         move.w #1,anim(a0) ; Clear anim and set prev_anim to 1
    35.  
    36.         move.b #-1,LastLoadedDPLC(a0) ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    37.  
    38.         move.l #Obj_Insta_Shield_Main,(a0)
    39.  
    40.  
    41.  
    42. Obj_Insta_Shield_Main:
    43.  
    44.         movea.w parent(a0),a2
    45.  
    46.         btst #Status_Invincible,status_secondary(a2) ; Is the player invincible?
    47.  
    48.         bne.s locret_195A4 ; If so, return
    49.  
    50.         move.w x_pos(a2),x_pos(a0) ; Inherit player's x_pos
    51.  
    52.         move.w y_pos(a2),y_pos(a0) ; Inherit player's y_pos
    53.  
    54.         move.b status(a2),status(a0) ; Inherit status
    55.  
    56.         andi.b #1,status(a0) ; Limit inheritance to 'orientation' bit
    57.  
    58.         tst.b (Reverse_gravity_flag).w
    59.  
    60.         beq.s .normalgravity
    61.  
    62.         ori.b #2,status(a0) ; Reverse the vertical mirror render_flag bit (On if Off beforehand and vice versa)
    63.  
    64.  
    65.  
    66.     .normalgravity:
    67.  
    68.         andi.w #drawing_mask,art_tile(a0)
    69.  
    70.         tst.w art_tile(a2)
    71.  
    72.         bpl.s .nothighpriority
    73.  
    74.         ori.w #high_priority,art_tile(a0)
    75.  
    76.  
    77.  
    78.     .nothighpriority:
    79.  
    80.         lea (Ani_InstaShield).l,a1
    81.  
    82.         jsr (Animate_Sprite).l
    83.  
    84.         cmpi.b #7,mapping_frame(a0) ; Has it reached then end of its animation?
    85.  
    86.         bne.s .notover ; If not, branch
    87.  
    88.         tst.b double_jump_flag(a2) ; Is it in its attacking state?
    89.  
    90.         beq.s .notover ; If not, branch
    91.  
    92.         move.b #2,double_jump_flag(a2) ; Mark attack as over
    93.  
    94.  
    95.  
    96.     .notover:
    97.  
    98.         tst.b mapping_frame(a0) ; Is this the first frame?
    99.  
    100.         beq.s .loadnewDPLC ; If so, branch and load the DPLC for this and the next few frames
    101.  
    102.         cmpi.b #3,mapping_frame(a0) ; Is this the third frame?
    103.  
    104.         bne.s .skipDPLC ; If not, branch as we don't need to load another DPLC yet
    105.  
    106.  
    107.  
    108.     .loadnewDPLC:
    109.  
    110.         bsr.w PLCLoad_Shields
    111.  
    112.  
    113.  
    114.     .skipDPLC:
    115.  
    116.         jmp (Draw_Sprite).l
    117.  
    118. ; ---------------------------------------------------------------------------
    119.  
    120.  
    121.  
    122. locret_195A4:
    123.  
    124.         rts
    125.  
    126. ; ---------------------------------------------------------------------------
    127.  



    This code loads the insta shield object and makes sure it swaps checks for reverse gravity or sets different reviews when loading different frames. To get it working under Sonic 1, we need to make a few changes. Firstly, we need to remove checks that sets the gravity. Find this bit of code:

    Code (Text):
    1. move.b status(a2),status(a0) ; Inherit status
    2.  
    3.         andi.b #1,status(a0) ; Limit inheritance to 'orientation' bit
    4.  
    5.         tst.b (Reverse_gravity_flag).w
    6.  
    7.         beq.s .normalgravity
    8.  
    9.         ori.b #2,status(a0) ; Reverse the vertical mirror render_flag bit (On if Off beforehand and vice versa)
    10.  
    11.  
    12.     .normalgravity:

    And just remove it as we wouldn’t need it in Sonic 1.


    Now we need to compare the SSTs and RAM bytes between Sonic 3K and Sonic 1. You need to open up this and this. You need to open up sonic3k.constants.asm in the S3K disassembly during this process. Let’s do a quick example:


    Code (Text):
    1. move.l #Map_InstaShield,mappings(a0)

    We need to focus on this bit of the line: “mappings” as that is an SST we need to change. In s3k.constants.asm, it says the SST is $C. If we check here, but in Sonic 1, it’s $4, so replace mappings with $4. Do you not get the concept? Let me show you the line below what I’ve shown:


    Code (Text):
    1. move.l #DPLC_InstaShield,DPLC_Address(a0)

    We need to look at this bit “DPLC_Address” as that’s an SST byte. Copy and paste and search the file in s3k.constants.asm. The byte is $3C, and in Sonic 1, it’s doesn’t have a byte so just change DPLC_Address to $3C. It will still work as $3C is a free byte in Sonic 1. Now, if you get the gist of it, try using the two links I sent above to compare the SST byte. If there isn’t a matching SST byte


    This will take time to do, but once you do a few on your own, you should get it.


    Find this line:


    Code (Text):
    1. jmp (Draw_Sprite).l
    Draw_Sprite is an equivalent of DisplaySprite so replace that with:


    Code (Text):
    1. jmp (DisplaySprite).l

    We need to fix the priorities too to make it support Sonic 1, on so replace:


    Code (Text):
    1. move.w #$80,$18(a0)

    With


    Code (Text):
    1. move.w #$1,$18(a0)

    And find:


    Code (Text):
    1. move.b #$18,$16(a0)

    This sets the height for the object, which Sonic 1 doesn’t need to do, so remove or comment it out.


    If you did everything right, your result should be something like this:


    Code (Text):
    1. ; --------------------------------------------------
    2.  
    3. ; InstaShieldObj: Insta-Shield
    4.  
    5. ; --------------------------------------------------
    6.  
    7.  
    8. InstaShieldObj: ; XREF: Obj_Index
    9.  
    10.         moveq #0,d0
    11.  
    12.         move.b $24(a0),d0
    13.  
    14.         move.w InstaShieldObj_Index(pc,d0.w),d1
    15.  
    16.         jmp InstaShieldObj_Index(pc,d1.w)
    17.  
    18. ; ===========================================================================
    19.  
    20. InstaShieldObj_Index:
    21.  
    22.         dc.w InstaShieldObj_Main-InstaShieldObj_Index
    23.  
    24.         dc.w loc3_1952A-InstaShieldObj_Index
    25.  
    26. ; ===========================================================================
    27.  
    28. InstaShieldObj_Main:
    29.  
    30.         tst.b ($FFFFFE2D).w
    31.  
    32.         bne.w locret3_195A4
    33.  
    34.         move.l #Map_InstaShield,$4(a0) ;Mappings
    35.  
    36.         move.l #DPLC_InstaShield,$3C(a0) ;Dynamic Pattern Load Cues
    37.  
    38.         move.l #ArtUnc_InstaShield,$38(a0)
    39.  
    40.         move.b #$14,1(a0)
    41.  
    42.         move.b #$1,$18(a0)
    43.  
    44.         move.b #$18,$16(a0)
    45.  
    46.         move.w #$A820,$36(a0)
    47.  
    48.         move.w #$541,2(a0)
    49.  
    50.  
    51. loc3_19518: ; CODE XREF: ROM:00019510j
    52.  
    53.         move.w #1,$1C(a0)
    54.  
    55.         move.b #-1,$30(a0)
    56.  
    57.         addq.b #2,$24(a0)
    58.  
    59.  
    60. loc3_1952A: ; DATA XREF: ROM:00019524o
    61.  
    62.         lea ($FFFFD000).w,a2
    63.  
    64.         tst.b ($FFFFFE2D).w
    65.  
    66.         bne.s locret3_195A4
    67.  
    68.         move.w $8(a2),$8(a0)
    69.  
    70.         move.w $C(a2),$C(a0)
    71.  
    72.  
    73.  
    74. ;loc3_1955A: ; CODE XREF: ROM:00019552j
    75.  
    76.         andi.w #$7FFF,art_tile(a0)
    77.  
    78.         tst.w $A(a2)
    79.  
    80.         bpl.s loc3_1956C
    81.  
    82.         ori.w #$8000,2(a0)
    83.  
    84.  
    85. loc3_1956C: ; CODE XREF: ROM:00019564j
    86.         lea (Ani_InstaShield).l,a1 ; load animation script address to a1
    87.         jsr AnimateSprite
    88.         cmpi.b #7,$1A(a0)
    89.         bne.s loc3_1958C
    90.         tst.b $2F(a2)
    91.         beq.s loc3_1958C
    92.         move.b #2,$2F(a2)
    93.  
    94.  
    95. loc3_1958C: ; CODE XREF: ROM:0001957Ej
    96.  
    97.                     ; ROM:00019584j
    98.  
    99.         tst.b $1A(a0)
    100.  
    101.         beq.s loc3_1959A
    102.  
    103.         cmpi.b #3,$1A(a0)
    104.  
    105.         bne.s loc3_1959E
    106.  
    107.  
    108. loc3_1959A: ; CODE XREF: ROM:00019590j
    109.         bsr.w PLCLoad_Shields
    110.  
    111.  
    112. loc3_1959E: ; CODE XREF: ROM:00019598j
    113.         jmp DisplaySprite
    114.  
    115. ; ???????????????????????????????????????????????????????????????????????????
    116.  
    117.  
    118. locret3_195A4: ; CODE XREF: ROM:00019534j
    119.         rts


    Next, we need to set up PLCLoad_Shields this is where it loads the DPLCs for the insta-shield art. Here is the original code:


    Code (Text):
    1. PLCLoad_Shields:
    2.  
    3.         moveq #0,d0
    4.  
    5.         move.b mapping_frame(a0),d0
    6.  
    7.         cmp.b LastLoadedDPLC(a0),d0
    8.  
    9.         beq.s locret_199E8
    10.  
    11.         move.b d0,LastLoadedDPLC(a0)
    12.  
    13.         movea.l DPLC_Address(a0),a2
    14.  
    15.         add.w d0,d0
    16.  
    17.         adda.w (a2,d0.w),a2
    18.  
    19.         move.w (a2)+,d5
    20.  
    21.         subq.w #1,d5
    22.  
    23.         bmi.s locret_199E8
    24.  
    25.         move.w vram_art(a0),d4
    26.  
    27.  
    28. PLCLoad_Shields_ReadEntry:
    29.  
    30.         moveq #0,d1
    31.  
    32.         move.w (a2)+,d1
    33.  
    34.         move.w d1,d3
    35.  
    36.         lsr.w #8,d3
    37.  
    38.         andi.w #$F0,d3
    39.  
    40.         addi.w #$10,d3
    41.  
    42.         andi.w #$FFF,d1
    43.  
    44.         lsl.l #5,d1
    45.  
    46.         add.l Art_Address(a0),d1
    47.  
    48.         move.w d4,d2
    49.  
    50.         add.w d3,d4
    51.  
    52.         add.w d3,d4
    53.  
    54.         jsr (Add_To_DMA_Queue).l
    55.  
    56.         dbf d5,PLCLoad_Shields_ReadEntry
    57.  
    58.  
    59. locret_199E8:
    60.  
    61.         rts
    62.  
    63. ; End of function PLCLoad_Shields

    Like before, you need to modify the SST and change some labels such as Add_To_DMA_Queue to QueueDMATransfer. The result should be this:


    Code (Text):
    1. PLCLoad_Shields:
    2.  
    3.         moveq #0,d0
    4.  
    5.     move.b $1A(a0),d0 ; load frame number
    6.  
    7.     cmp.b $33(a0),d0
    8.  
    9.     beq.s locret2_13C96
    10.  
    11.     move.b d0,$33(a0)
    12.  
    13.     move.l $3C(A0),a2
    14.  
    15.         add.w D0,D0
    16.  
    17.     adda.w (a2,D0),a2
    18.  
    19.     move.w (a2)+,d5
    20.  
    21.     subq.w #1,D5
    22.  
    23.     bmi.s locret2_13C96
    24.  
    25.         move.w $36(A0),D4
    26.  
    27.  
    28. loc_199BE:
    29.  
    30.         moveq #0,d1
    31.  
    32.     move.b (a2)+,d1
    33.  
    34.     lsl.w #8,d1
    35.  
    36.     move.b (a2)+,d1
    37.  
    38.     move.w d1,d3
    39.  
    40.     lsr.w #8,d3
    41.  
    42.     andi.w #$F0,d3
    43.  
    44.     addi.w #$10,d3
    45.  
    46.     andi.w #$FFF,d1
    47.  
    48.     lsl.l #5,d1
    49.  
    50.         add.l $38(a0),d1
    51.  
    52.         move.w D4,D2
    53.  
    54.     add.w D3,D4
    55.  
    56.     add.w D3,D4
    57.  
    58.         jsr (QueueDMATransfer)
    59.  
    60.     dbf d5,loc_199BE ; repeat for number of entries
    61.  
    62.  
    63. locret2_13C96:
    64.  
    65.     rts
    66.  
    67. ; End of function PLCLoad_Shields

    Now we need to load the object in your hack, so open up Object pointers.asm, and we need to find an accessible object ID. Open it up, and you’ll see a bunch of lines with “ObjectFall” in them. These are dummy object files, so you can replace one of them with the object name you wrote. Just make sure you know what object ID you selected. For example, if you replace the first ObjectFall after Obj01, it will be object ID 02.


    Now comes loading in the object within Sonic’s object. You would want to go to Obj01_Main, and at the end of the subroutine, add this:



    Code (Text):
    1. move.b #XX,$FFFFD180.w ; load the insta shield object

    XX - that will be the object ID you set in the object pointers file.


    Up next is to modify Sonic_JumpHeight. The reason is due to the way Sonic 3 handles double jumping. Let me show you a snippet of code:


    Code (Text):
    1. loc_118D2:
    2.  
    3.         cmp.w y_vel(a0),d1 ; is y speed greater than 4? (2 if underwater)
    4.  
    5.         ble.w Sonic_InstaAndShieldMoves ; if not, branch
    6.  
    7.         move.b (Ctrl_1_logical).w,d0
    8.  
    9.         andi.b #$70,d0 ; are buttons A, B or C being pressed?
    10.  
    11.         bne.s locret_118E8 ; if yes, branch
    12.  
    13.         move.w d1,y_vel(a0) ; cap jump height
    14.  
    15.  
    16. locret_118E8:
    17.  
    18.         rts
    19.  
    20. ; ---------------------------------------------------------------------------
    21.  
    22.  
    23. Sonic_UpVelCap:
    24.  
    25.         tst.b spin_dash_flag(a0) ; is Sonic charging his spin dash?
    26.  
    27.         bne.s locret_118FE ; if yes, branch
    28.  
    29.         cmpi.w #-$FC0,y_vel(a0) ; is Sonic's Y speed faster (less than) than -15.75 (-$FC0)?
    30.  
    31.         bge.s locret_118FE ; if not, branch
    32.  
    33.         move.w #-$FC0,y_vel(a0) ; cap upward speed
    34.  
    35.  
    36. locret_118FE:
    37.  
    38.         rts
    39.  
    40. ; ---------------------------------------------------------------------------
    41.  
    42.  
    43. Sonic_InstaAndShieldMoves:
    44.  
    45.         tst.b double_jump_flag(a0) ; is Sonic currently performing a double jump?
    46.  
    47.         bne.w locret_11A14 ; if yes, branch
    48.  
    49.         move.b (Ctrl_1_pressed_logical).w,d0
    50.  
    51.         andi.b #$70,d0 ; are buttons A, B, or C being pressed?
    52.  
    53.         beq.w locret_11A14 ; if not, branch
    54.  
    55.         bclr #Status_RollJump,status(a0)
    56.  
    57.         tst.b (Super_Sonic_Knux_flag).w ; check Super-state
    58.  
    59.         beq.s Sonic_FireShield ; if not in a super-state, branch
    60.  
    61.         bmi.w Sonic_HyperDash ; if Hyper, branch
    62.  
    63.         move.b #1,double_jump_flag(a0)
    64.  
    65.         rts
    66.  
    67. ; ---------------------------------------------------------------------------
    68.  
    69.  
    70. Sonic_FireShield:
    71.  
    72.         btst #Status_Invincible,status_secondary(a0) ; first, does Sonic have invincibility?
    73.  
    74.         bne.w locret_11A14 ; if yes, branch
    75.  
    76.         btst #Status_FireShield,status_secondary(a0) ; does Sonic have a Fire Shield?
    77.  
    78.         beq.s Sonic_LightningShield ; if not, branch
    79.  
    80.         move.b #1,(Shield+anim).w
    81.  
    82.         move.b #1,double_jump_flag(a0)
    83.  
    84.         move.w #$800,d0
    85.  
    86.         btst #Status_Facing,status(a0) ; is Sonic facing left?
    87.  
    88.         beq.s loc_11958 ; if not, branch
    89.  
    90.         neg.w d0 ; reverse speed value, moving Sonic left
    91.  
    92.  
    93. loc_11958:
    94.  
    95.         move.w d0,x_vel(a0) ; apply velocity...
    96.  
    97.         move.w d0,ground_vel(a0) ; ...both ground and air
    98.  
    99.         move.w #0,y_vel(a0) ; kill y-velocity
    100.  
    101.         move.w #$2000,(H_scroll_frame_offset).w
    102.  
    103.         bsr.w Reset_Player_Position_Array
    104.  
    105.         move.w #sfx_FireAttack,d0
    106.  
    107.         jmp (Play_Sound_2).l
    108.  
    109. ; ---------------------------------------------------------------------------


    To summarise, the code checks for the double jump flag before setting it to 1 for the fire shield to do its action. It also prevents the move from happening when your Super which is a useful move to do. In order to make the move work, we need to install Sonic_InstaAndShieldMoves. Go to loc_134AE and replace


    Code (Text):
    1. ble.s locret_134C2

    With


    Code (Text):
    1. ble.s Sonic_InstaAndShieldMoves:

    Under locret_134D2, place this:


    Code (Text):
    1. Sonic_InstaAndShieldMoves:
    2.  
    3.         tst.b $2F(a0)
    4.  
    5.         bne.w locret_134D2
    6.  
    7.         move.b ($FFFFF603).w,d0
    8.  
    9.         andi.b #$70,d0
    10.  
    11.         beq.w locret_134D2
    12.  
    13.         bclr #4,$2A(a0)
    14.  
    15.         bne.w Sonic_InstaShield
    16.  
    17.         move.b #1,$2F(a0)
    18.  
    19.         rts
    20.  
    21.  
    22. Sonic_InstaShield:
    23.  
    24.         tst.b ($FFFFFE2D).w
    25.  
    26.         bne.s Shieldrts
    27.  
    28.         tst.b ($FFFFFE2C).w
    29.  
    30.         bne.s Shieldrts
    31.  
    32.         move.b #1,($FFFFD180+$1C).w
    33.  
    34.         move.w #$B8,d0
    35.  
    36.         jsr (PlaySound_Special).l  
    37.  
    38.  
    39. Shieldrts:
    In my hack, I set $B8 as the sound effect, but you can change it to any sound ID you desire. $AB sounds close enough, so use that if you like. Also, if you’ve been following the code, I set double_jump_flag to be $2F since there is no equivalent in Sonic 1 and a free SST. Now we’ve come to the last two steps of modifying the code: Making sure the insta shield works when we’ve jumped after landing and expanding the touch’s size when the insta-shield is performed. Let’s go with the easiest step.



    We want to make sure the double jump flag is reset to 0 when Sonic lands on the floor; otherwise, he wouldn’t perform the insta-shield move after he jumps again. Let’s go over to Sonic_ResetOnFloor. Under the label “loc_137E4”, insert this:


    Code (Text):
    1. move.b #0,$2F(a0)

    This will reset the double jump flag the next time Sonic hits the ground.


    For the last bit, modify TouchResponse so Sonic’s hitbox largens when he makes the insta-shield attack. Go to TouchResponse, and under the label, insert this:


    Code (Text):
    1. nop
    2.  
    3.         tst.b ($FFFFFE2C).w ; Does Sonic Have Shield?
    4.  
    5.         bne.s Touch_NoInstaShield ; If so, branch
    6.  
    7.         tst.b ($FFFFFE2D).w ; Does Sonic Is invincible?
    8.  
    9.         bne.s Touch_NoInstaShield ; If so,branch
    10.  
    11.         ; By this point, we're focussing purely on the Insta-Shield
    12.  
    13.         cmpi.b #1,double_jump_flag(a0) ; Is the Insta-Shield currently in its 'attacking' mode?
    14.  
    15.         bne.s Touch_NoInstaShield ; If not, branch
    16.  
    17.         move.b #1,($FFFFFE2D).w ; make Sonic invincible
    18.  
    19.         move.w x_pos(a0),d2 ; Get player's x_pos
    20.  
    21.         move.w y_pos(a0),d3 ; Get player's y_pos
    22.  
    23.         subi.w #$18,d2 ; Subtract width of Insta-Shield
    24.  
    25.         subi.w #$18,d3 ; Subtract height of Insta-Shield
    26.  
    27.         move.w #$30,d4 ; Player's width
    28.  
    29.         move.w #$30,d5 ; Player's height
    30.  
    31.         bsr.s Touch_Process
    32.  
    33.         clr.b ($FFFFFE2D).w
    34.  
    35.  
    36. Alreadyinvincible:
    37.  
    38.         moveq #0,d0
    39.  
    40.         rts
    41.  
    42. ; ---------------------------------------------------------------------------
    43.  
    44. ; Normal TouchResponse comes after this

    You may notice that we have a new label: Touch_Process. This allows the code to interact with the object RAM. To utilise this, just replace Touch_NoDuck with this:


    Code (Text):
    1. Touch_NoDuck:
    2.  
    3.         move.w #$10,d4
    4.  
    5.         add.w d5,d5
    6.  
    7. Touch_Process:
    8.  
    9.         lea ($FFFFD800).w,a1 ; begin checking the object RAM
    10.  
    11.         move.w #$5F,d6

    All right, the code side is done. Now it’s time to add the art in:


    First of all, make sure you got your SK disassembly ready. The easiest thing to do is to go to:


    “skdisasm-master\General\Sprites\Shields” and look for DPLC - Insta-Shield.asm. Since PLCLoad_Shield retains using the same S3K format when it comes to DPLC mappings, we don’t need to change anything, so copy that file and paste it in the disassembly in your hack. As for the mappings, we need to convert to format to Sonic 1. You can use one of the three tools to do this:


    One tool is called SonMapEd. Open it up and set the Game Format setting to "Sonic 3 & Knuckles", load the mappings file, change the Game Format setting to "Sonic 1", and save the mappings as “Map - Insta-Shield.asm” in the “_maps” folder of your disassembly.


    Another tool is MappingsConverter. Open it up and load the mappings with input set to Game: "Sonic 3 & Knuckles", Format: ASM and output set to Game: "Sonic 1", Format: ASM, and save the mappings as “Map - Insta-Shield.asm” in the “_maps” folder of your disassembly.


    Another tool is Flex2. Open it up and insert the mappings and make sure the format is set as S3K. Load it, convert it to Sonic 1 and save it. The tool should overwrite the original mapping, and you should go back to the shields folder of the SK disassembly, copy the mappings and place it in “_maps” folder of your disassembly.


    As for the art, you’ll need to load PaletteConverter as the player palettes between Sonic 3K and 1 are vastly different. Open it up and you will see this:





    We are going to do this according to the numbers I added.


    First of all, click on load BIN and open up Sonic’s S3K palette file. It should be under:

    “skdisasm-master\General\Sprites\Sonic\Palettes\SonicandTails.bin”


    2. Secondly, open up Sonic’s S1 palette file. It should be under:


    “yourdisassembly\pallet\Sonic.bin”


    You need to match the palette bytes by dragging Sonic 3K’s byte to match S1’s; and the outcome should be this:


    3. Open up convert uncompressed file and open up the Insta-Shield art; It should be located at:

    Code (Text):
    1. “skdisasm-master\General\Sprites\Shields\Insta-Shield.bin”

    It will give you a pop-up asking if you want to overwrite the source with the converted artwork. Say no and save the file in your disassembly in artunc and name it: “Insta-Shield.bin”


    I’ve linked a .rar file with everything converted here if you struggled to get that set up. Then, just drag and drop the files to its respective folder.


    Open up sonic1.asm, and under the insta-shield object, insert this:


    Code (Text):
    1. ; --------------------------------------------------------------
    2.  
    3. ; Insta-Shield art and mappings
    4.  
    5. ; --------------------------------------------------------------
    6.  
    7. Map_InstaShield:
    8.  
    9.     include "_maps\Map - Insta-Shield.asm" ; Insta-Shield mappings
    10.  
    11. DPLC_InstaShield:
    12.  
    13.     include "_inc\DPLC - Insta-Shield.asm" ; Insta-Shield DPLCs
    14.  
    15. Ani_InstaShield:
    16.  
    17.         dc.w byte_199EE3-Ani_InstaShield
    18.  
    19.         dc.w byte_199F13-Ani_InstaShield
    20.  
    21. byte_199EE3: dc.b $1F, 6, $FF
    22.  
    23. byte_199F13: dc.b 0, 0, 1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 7, $FD, 0
    24.  
    25.     even
    26.  
    27. ArtUnc_InstaShield: incbin "artunc\Insta-Shield.bin"
    28.  
    29.         even ; Insta-Shield uncompressed art

    You need to make sure the object loads if you managed to obtain a shield and remove it. Go to the shield object and find the line that clears the shield RAM and place this:


    Code (Text):
    1. move.b #$XX,(a0) ;Revert shield sprite to Insta-Shield
    2.  
    3.         move.b #$0,$24(a0) ;Reset the routine counter to trigger Insta-Shield Init


    Again, XX should be the object ID you set for the insta-shield. Build your disassembly, and it should run well. Next time, I’ll get the elemental shields to guide up and running alongside making references to GitHub’s disassembly. If you have any questions or if it works, feel free to comment down below. If you are considering adding this to the SCHG wiki, can you please let me know and credit me? =D

    Also, can you please credit me if you used this guide as it took me quite a while to write this and set up with a bit of guidance?


    Until then~

    EDIT: Fixed the links not showing here. Must be a bug with crossposting.
     
    Last edited: Nov 19, 2021
  2. DeltaWooloo

    DeltaWooloo

    The sheep goes quack! Member
    OK, my lads. Now comes the meat, and we’re getting that up and running. How to port the elemental shields to Sonic 1. Now I'm aware Clownacy did this last year but some people like to learn through text than watching videos.

    (also sorry for the horrible code tagging and large spaces. I copied the post from an XF2 forum here and the translation didn't go very well. I'll fix it later at some point)

    So I won’t repeat myself; read the disclaimer in the first post before adding it in. Then, if you’ve done so, keep on reading. One last thing to say is rather than me referencing GitHub labels, and I will showcase how to do it if you’re considering porting the elemental shields to GitHub disassembly. (for Hivebrain 2005 users, you may want to read this too but be prepared to replace variables with RAM and constants with SSTs.)

    As I said with the insta-shield, we would need to read the code in S3K. So let’s start with one of my favourite shields: the lightning shield.

    Code (Text):
    1. Obj_Lightning_Shield:
    2.  
    3.         ; init
    4.  
    5.         ; Load Spark art
    6.  
    7.         move.l    #ArtUnc_Obj_Lightning_Shield_Sparks,d1            ; Load art source
    8.  
    9.         move.w    #tiles_to_bytes(ArtTile_Shield_Sparks),d2        ; Load art destination
    10.  
    11.         move.w    #(ArtUnc_Obj_Lightning_Shield_Sparks_end-ArtUnc_Obj_Lightning_Shield_Sparks)/2,d3    ; Size of art (in words)
    12.  
    13.         jsr    (Add_To_DMA_Queue).l
    14.  
    15.  
    16.  
    17.         move.l    #Map_LightningShield,mappings(a0)
    18.  
    19.         move.l    #DPLC_LightningShield,DPLC_Address(a0)            ; Used by PLCLoad_Shields
    20.  
    21.         move.l    #ArtUnc_LightningShield,Art_Address(a0)            ; Used by PLCLoad_Shields
    22.  
    23.         move.b    #4,render_flags(a0)
    24.  
    25.         move.w    #$80,priority(a0)
    26.  
    27.         move.b    #$18,width_pixels(a0)
    28.  
    29.         move.b    #$18,height_pixels(a0)
    30.  
    31.         move.w    #ArtTile_Shield,art_tile(a0)
    32.  
    33.         move.w    #tiles_to_bytes(ArtTile_Shield),vram_art(a0)    ; Used by PLCLoad_Shields
    34.  
    35.         btst    #7,(Player_1+art_tile).w
    36.  
    37.         beq.s    .nothighpriority
    38.  
    39.         bset    #7,art_tile(a0)
    40.  
    41.  
    42.  
    43.     .nothighpriority:
    44.  
    45.         move.w    #1,anim(a0)                ; Clear anim and set prev_anim to 1
    46.  
    47.         move.b    #-1,LastLoadedDPLC(a0)            ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    48.  
    49.         move.l    #Obj_Lightning_Shield_Main,(a0)
    50.  
    51.  
    52.  
    53. Obj_Lightning_Shield_Main:
    54.  
    55.         movea.w    parent(a0),a2
    56.  
    57.         btst    #Status_Invincible,status_secondary(a2)    ; Is player invincible?
    58.  
    59.         bne.w    locret_197C4                ; If so, do not display and do not update variables
    60.  
    61.         cmpi.b    #$1C,anim(a2)                ; Is player in their 'blank' animation?
    62.  
    63.         beq.s    locret_197C4                ; If so, do not display and do not update variables
    64.  
    65.         btst    #Status_Shield,status_secondary(a2)    ; Should the player still have a shield?
    66.  
    67.         beq.s    Obj_Lightning_Shield_Destroy        ; If not, change to Insta-Shield
    68.  
    69.         btst    #Status_Underwater,status(a2)        ; Is player underwater?
    70.  
    71.         bne.s    Obj_Lightning_Shield_DestroyUnderwater    ; If so, branch
    72.  
    73.         move.w    x_pos(a2),x_pos(a0)
    74.  
    75.         move.w    y_pos(a2),y_pos(a0)
    76.  
    77.         move.b    status(a2),status(a0)            ; Inherit status
    78.  
    79.         andi.b    #1,status(a0)                ; Limit inheritance to 'orientation' bit
    80.  
    81.         tst.b    (Reverse_gravity_flag).w
    82.  
    83.         beq.s    .normalgravity
    84.  
    85.         ori.b    #2,status(a0)                ; If in reverse gravity, reverse the vertical mirror render_flag bit (On if Off beforehand and vice versa)
    86.  
    87.  
    88.  
    89.     .normalgravity:
    90.  
    91.         andi.w    #drawing_mask,art_tile(a0)
    92.  
    93.         tst.w    art_tile(a2)
    94.  
    95.         bpl.s    .nothighpriority
    96.  
    97.         ori.w    #high_priority,art_tile(a0)
    98.  
    99.  
    100.  
    101.     .nothighpriority:
    102.  
    103.         tst.b    anim(a0)                ; Is shield in its 'double jump' state?
    104.  
    105.         beq.s    Obj_Lightning_Shield_Display        ; Is not, branch and display
    106.  
    107.         bsr.s    Obj_Lightning_Shield_Create_Spark    ; Create sparks
    108.  
    109.         clr.b    anim(a0)                ; Once done, return to non-'double jump' state
    110.  
    111.  
    112.  
    113. Obj_Lightning_Shield_Display:
    114.  
    115.         lea    (Ani_199EA).l,a1
    116.  
    117.         jsr    (Animate_Sprite).l
    118.  
    119.         move.w    #$80,priority(a0)            ; Layer shield over player sprite
    120.  
    121.         cmpi.b    #$E,mapping_frame(a0)            ; Are these the frames that display in front of the player?
    122.  
    123.         blo.s    .overplayer                ; If so, branch
    124.  
    125.         move.w    #$200,priority(a0)            ; If not, layer shield behind player sprite
    126.  
    127.  
    128.  
    129.     .overplayer:
    130.  
    131.         bsr.w    PLCLoad_Shields
    132.  
    133.         jmp    (Draw_Sprite).l
    134.  
    135. ; ---------------------------------------------------------------------------
    136.  
    137.  
    138.  
    139. locret_197C4:
    140.  
    141.         rts
    142.  
    143. ; ---------------------------------------------------------------------------
    144.  
    145.  
    146.  
    147. Obj_Lightning_Shield_DestroyUnderwater:
    148.  
    149.         tst.w    (Palette_fade_timer).w
    150.  
    151.         beq.s    Obj_Lightning_Shield_FlashWater
    152.  
    153.  
    154.  
    155. Obj_Lightning_Shield_Destroy:
    156.  
    157.         andi.b    #$8E,status_secondary(a2)    ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0
    158.  
    159.         move.l    #Obj_Insta_Shield,(a0)        ; Replace the Lightning Shield with the Insta-Shield
    160.  
    161.         rts
    162.  
    163. ; ---------------------------------------------------------------------------
    164.  
    165.  
    166.  
    167. Obj_Lightning_Shield_FlashWater:
    168.  
    169.         move.l    #Obj_Lightning_Shield_DestroyUnderwater2,(a0)
    170.  
    171.         andi.b    #$8E,status_secondary(a2)    ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0
    172.  
    173.  
    174.  
    175.         ; Flashes the underwater palette white
    176.  
    177.         lea    (Water_palette).w,a1
    178.  
    179.         lea    (Target_water_palette).w,a2
    180.  
    181.         move.w    #($80/4)-1,d0            ; Size of Water_palette/4-1
    182.  
    183.  
    184.  
    185. loc_197F2:
    186.  
    187.         move.l    (a1),(a2)+            ; Backup palette entries
    188.  
    189.         move.l    #$0EEE0EEE,(a1)+        ; Overwrite palette entries with white
    190.  
    191.         dbf    d0,loc_197F2            ; Loop until entire thing is overwritten
    192.  
    193.  
    194.  
    195.         move.w    #0,-$40(a1)            ; Set the first colour in the third palette line to black
    196.  
    197.         move.b    #3,anim_frame_timer(a0)
    198.  
    199.         rts
    200.  
    201.  
    202.  
    203. ; =============== S U B R O U T I N E =======================================
    204.  
    205.  
    206.  
    207.  
    208.  
    209. Obj_Lightning_Shield_Create_Spark:
    210.  
    211.         moveq    #1,d2
    212.  
    213.  
    214.  
    215. Obj_Lightning_Shield_Create_Spark_Part2:
    216.  
    217.         lea    (SparkVelocities).l,a2
    218.  
    219.         moveq    #3,d1
    220.  
    221.  
    222.  
    223. loc_19816:
    224.  
    225.         bsr.w    Create_New_Sprite        ; Find free object slot
    226.  
    227.         bne.s    locret_19862            ; If one can't be found, return
    228.  
    229.         move.l    #Obj_Lightning_Shield_Spark,(a1)    ; Make new object a Spark
    230.  
    231.         move.w    x_pos(a0),x_pos(a1)        ; (Spark) Inherit x_pos from source object (Lightning Shield, Hyper Sonic Stars)
    232.  
    233.         move.w    y_pos(a0),y_pos(a1)        ; (Spark) Inherit y_pos from source object (Lightning Shield, Hyper Sonic Stars)
    234.  
    235.         move.l    mappings(a0),mappings(a1)    ; (Spark) Inherit mappings from source object (Lightning Shield, Hyper Sonic Stars)
    236.  
    237.         move.w    art_tile(a0),art_tile(a1)    ; (Spark) Inherit art_tile from source object (Lightning Shield, Hyper Sonic Stars)
    238.  
    239.         move.b    #4,render_flags(a1)
    240.  
    241.         move.w    #$80,priority(a1)
    242.  
    243.         move.b    #8,width_pixels(a1)
    244.  
    245.         move.b    #8,height_pixels(a1)
    246.  
    247.         move.b    d2,anim(a1)
    248.  
    249.         move.w    (a2)+,x_vel(a1)            ; (Spark) Give x_vel (unique to each of the four Sparks)
    250.  
    251.         move.w    (a2)+,y_vel(a1)            ; (Spark) Give y_vel (unique to each of the four Sparks)
    252.  
    253.         dbf    d1,loc_19816
    254.  
    255.  
    256.  
    257. locret_19862:
    258.  
    259.         rts
    260.  
    261. ; End of function Obj_Lightning_Shield_Create_Spark
    262.  
    263.  
    264.  
    265. ; ---------------------------------------------------------------------------
    266.  
    267. SparkVelocities:dc.w  -$200, -$200
    268.  
    269.         dc.w   $200, -$200
    270.  
    271.         dc.w  -$200,  $200
    272.  
    273.         dc.w   $200,  $200
    274.  
    275. ; ---------------------------------------------------------------------------
    276.  
    277.  
    278.  
    279. Obj_Lightning_Shield_Spark:
    280.  
    281.         jsr    (MoveSprite2).l
    282.  
    283.         addi.w    #$18,y_vel(a0)
    284.  
    285.         lea    (Ani_199EA).l,a1
    286.  
    287.         jsr    (Animate_Sprite).l
    288.  
    289.         tst.b    routine(a0)            ; Changed by Animate_Sprite
    290.  
    291.         bne.s    Obj_Lightning_Shield_Spark_Delete
    292.  
    293.         jmp    (Draw_Sprite).l
    294.  
    295. ; ---------------------------------------------------------------------------
    296.  
    297.  
    298.  
    299. Obj_Lightning_Shield_Spark_Delete:
    300.  
    301.         jmp    (Delete_Current_Sprite).l
    302.  
    303. ; ---------------------------------------------------------------------------
    304.  
    305.  
    306.  
    307. Obj_Lightning_Shield_DestroyUnderwater2:
    308.  
    309.         subq.b    #1,anim_frame_timer(a0)        ; Is it time to end the white flash?
    310.  
    311.         bpl.s    locret_198BC            ; If not, return
    312.  
    313.         move.l    #Obj_Insta_Shield,(a0)        ; Replace Lightning Shield with Insta-Shield
    314.  
    315.         lea    (Target_water_palette).w,a1
    316.  
    317.         lea    (Water_palette).w,a2
    318.  
    319.         move.w    #($80/4)-1,d0            ; Size of Water_palette/4-1
    320.  
    321.  
    322.  
    323. loc_198B6:
    324.  
    325.         move.l    (a1)+,(a2)+            ; Restore backed-up underwater palette
    326.  
    327.         dbf    d0,loc_198B6            ; Loop until entire thing is restored
    328.  
    329.  
    330.  
    331. locret_198BC:
    332.  
    333.         rts
    334.  
    335. ; ---------------------------------------------------------------------------



    Firstly, let’s change a few things; if you ported the S3K priority manager, you don’t need to do this, but Sonic 1 doesn’t check the height for an object, so go to Obj_Lightning_Shield_Main and comment out/remove this line:



    Code (Text):
    1. move.b    #$18,height_pixels(a0)


    Next go and find:



    Code (Text):
    1. move.w    #$80,priority(a0)


    Change $80 to a 1 and change move.w to move.b



    Now we need to add a routine counter so below the label to the object, insert this:



    Code (Text):
    1.         moveq   #0,d0
    2.  
    3.         move.b  obRoutine(a0), D0
    4.  
    5.         move.w  LightningShield_Index(pc,d0.w),d1
    6.  
    7.         jmp     LightningShield_Index(pc,d1.w)
    8.  
    9.          
    10.  
    11. ; ===========================================================================
    12.  
    13.  
    14.  
    15. LightningShield_Index:
    16.  
    17.         dc.w LightningShield_Init-LightningShield_Index
    18.  
    19.         dc.w LightningShield_Main-LightningShield_Index
    20.  
    21.         dc.w Obj_Lightning_Shield_DestroyUnderwater2-LightningShield_Index
    22.  
    23. ; ===========================================================================


    Now we need to replace this:



    Code (Text):
    1. move.w    #ArtTile_Shield,art_tile(a0)
    2.  
    3.         move.w    #tiles_to_bytes(ArtTile_Shield),vram_art(a0)    ; Used by PLCLoad_Shields

    With this:


    Code (Text):
    1. move.w    #$541,art_tile(a0)
    2.  
    3.         move.w    #$A820,vram_art(a0)    ; Used by PLCLoad_Shields

    This will make editing VRAM miles easier. We need to do this to the sparks, above the lines replaced, replace this:


    Code (Text):
    1. move.l    #ArtUnc_Obj_Lightning_Shield_Sparks,d1            ; Load art source
    2.  
    3.         move.w    #tiles_to_bytes(ArtTile_Shield_Sparks),d2        ; Load art destination
    4.  
    5.         move.w    #(ArtUnc_Obj_Lightning_Shield_Sparks_end-ArtUnc_Obj_Lightning_Shield_Sparks)/2,d3    ; Size of art (in words)
    6.  
    7.         jsr    (Add_To_DMA_Queue).l


    With this:



    Code (Text):
    1. move.l    #ArtUnc_LightningSparks,d1
    2.  
    3.         move.w    #$AC00,d2
    4.  
    5.         move.w    #$50,d3
    6.  
    7.         jsr     (QueueDMATransfer)




    The next step is to go to .nothighpriority, and in the last line, replace this:



    Code (Text):
    1. move.l    #Obj_Lightning_Shield_Main,(a0)


    With this:



    Code (Text):
    1. addq.b    #2,obRoutine(a0) ; => LightningShield_Main


    This will load the routine counter and load LightningShield_Main’s subroutine. If you’re struggling to find similar results, be sure to look at lines of code that you believe it’s a label and if it is a label, replace it with the code above and include it under your routine counter. The is one last line we need to replace to make usage of the routine. So go to Obj_Lightning_Shield_FlashWater and replace this:



    Code (Text):
    1. move.l    #Obj_Lightning_Shield_DestroyUnderwater2,(a0)
    With this:



    Code (Text):
    1. addq.b    #2,obRoutine(a0)


    Also, any labels that have a dot at the beginning, please replace it with an @ (unless you’re using a Sonic 1 disassembly with an AS assembler)



    Now, we need to make sure this is compatible with Sonic 1. For that to work, we would need to change the wording of the constants and modify the RAM for it to work. So, open up constants.asm in your disassembly and replace the constants in the object with what Sonic 1 has. The wording may be a bit different but try to find equivalent constants. If you can’t, don't worry. You can cross-reference what I have for my final result. Also, add this to constants.asm you can replace any equivalent SSTs with that as I’ll showcase these constants as an example:



    Code (Text):
    1. shield_LastLoadedDPLC = $33
    2.  
    3. shield_DPLC_Address = $3C
    4.  
    5. shield_Art_Address = $38
    6.  
    7. shield_vram_art = $36




    For Hivebrain users, you would need to follow a similar procedure by comparing RAM and SST bytes. If you don’t understand how it works, please check the first post to get a better idea of how to do it. Click here for Sonic 1 RAM Editing and here for Sonic 3K RAM editing. This will take time to do in Hivebrain but if you’re struggling to find equivalents, be sure to look at the list of constants in a GitHub disassembly. If you’ve done everything successfully, your final output should be something like this:





    Code (Text):
    1. ; ==========================================================================
    2.  
    3. ; ---------------------------------------------------------------------------
    4.  
    5. ; Object XX and XX - lightning shield and sparks
    6.  
    7. ; ---------------------------------------------------------------------------
    8.  
    9. Obj38:                    ; XREF: Obj_Index
    10.  
    11.         moveq   #0,d0
    12.  
    13.         move.b  obRoutine(a0), D0
    14.  
    15.         move.w  LightningShield_Index(pc,d0.w),d1
    16.  
    17.         jmp     LightningShield_Index(pc,d1.w)
    18.  
    19.          
    20.  
    21. ; ===========================================================================
    22.  
    23.  
    24.  
    25. LightningShield_Index:
    26.  
    27.         dc.w LightningShield_Init-LightningShield_Index
    28.  
    29.         dc.w LightningShield_Main-LightningShield_Index
    30.  
    31.         dc.w Obj_Lightning_Shield_DestroyUnderwater2-LightningShield_Index
    32.  
    33. ; ===========================================================================
    34.  
    35.  
    36.  
    37. LightningShield_Init:
    38.  
    39.         move.l    #ArtUnc_LightningSparks,d1
    40.  
    41.         move.w    #$AC00,d2
    42.  
    43.         move.w    #$50,d3
    44.  
    45.         jsr     (QueueDMATransfer)
    46.  
    47.         move.l    #Map_LightningShield,obMap(a0)
    48.  
    49.         move.l    #DPLC_LightningShield,shield_DPLC_Address(a0)    ; Used by PLCLoad_Shields
    50.  
    51.         move.l    #ArtUnc_LightningShield,shield_Art_Address(a0)    ; Used by PLCLoad_Shields
    52.  
    53.         move.b    #4,obRender(a0)
    54.  
    55.         move.b    #1,obPriority(a0)
    56.  
    57.         move.b    #$18,obActWid(a0)
    58.  
    59.         move.w    #$541,obGfx(a0)
    60.  
    61.         move.w    #$A820,shield_vram_art(a0)    ; Used by PLCLoad_Shields
    62.  
    63.         btst    #7,(v_player+obGFX).w
    64.  
    65.         beq.s    loc_195F0L
    66.  
    67.         bset    #7,obGFX(a0)
    68.  
    69.  
    70.  
    71. loc_195F0L:
    72.  
    73.         move.w    #1,obAnim(a0)    ; Clear anim and set prev_anim to 1
    74.  
    75.         move.b    #-1,shield_LastLoadedDPLC(a0)    ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    76.  
    77.         addq.b    #2,obRoutine(a0) ; => LightningShield_Main
    78.  
    79.  
    80.  
    81. LightningShield_Main:
    82.  
    83.         lea    (v_player).w,a2
    84.  
    85.         tst.b    (v_invinc).w
    86.  
    87.         bne.w    locret_197C4    ; If so, do not display and do not update variables
    88.  
    89.         cmpi.b    #$1C,obAnim(a2)    ; Is player in their 'blank' animation?
    90.  
    91.         beq.s    locret_197C4    ; If so, do not display and do not update variables
    92.  
    93.         tst.b    (v_shield).w     ; Should the player still have a shield?
    94.  
    95.         beq.s    Obj_Lightning_Shield_Destroy    ; If not, change to Insta-Shield
    96.  
    97.         btst    #6,status(a2)    ; Is player underwater?
    98.  
    99.         bne.s    Obj_Lightning_Shield_DestroyUnderwater    ; If so, branch
    100.  
    101.         move.w    obX(a2),obX(a0)
    102.  
    103.         move.w    obY(a2),obY(a0)
    104.  
    105.         andi.w    #$7FFF,obGFX(a0)
    106.  
    107.         tst.w    obGFX(a2)
    108.  
    109.         bpl.s    @nothighpriority2
    110.  
    111.         ori.w    #$8000,obGFX(a0)
    112.  
    113.  
    114.  
    115.     @nothighpriority2:
    116.  
    117.         tst.b    obAnim(a0)    ; Is shield in its 'double jump' state?
    118.  
    119.         beq.s    Obj_Lightning_Shield_Display    ; Is not, branch and display
    120.  
    121.         bsr.w    Obj_Lightning_Shield_Create_Spark    ; Create sparks
    122.  
    123.         clr.b    obAnim(a0)    ; Once done, return to non-'double jump' state
    124.  
    125.  
    126.  
    127. Obj_Lightning_Shield_Display:
    128.  
    129.         lea    (Ani_19A2A).l,a1
    130.  
    131.         jsr    (AnimateSprite).l
    132.  
    133.         move.b    #1,obPriority(a0)    ; Layer shield over player sprite
    134.  
    135.         cmpi.b    #$E,obFrame(a0)    ; Are these the frames that display in front of the player?
    136.  
    137.         blo.s    @overplayer1    ; If so, branch
    138.  
    139.         move.b    #3,obPriority(a0)    ; If not, layer shield behind player sprite
    140.  
    141.  
    142.  
    143.     @overplayer1:
    144.  
    145.         bsr.w    PLCLoad_Shields
    146.  
    147.         jmp    (DisplaySprite).l
    148.  
    149. ; ---------------------------------------------------------------------------
    150.  
    151.  
    152.  
    153. locret_197C4:
    154.  
    155.         rts
    156.  
    157. ; ---------------------------------------------------------------------------
    158.  
    159.  
    160.  
    161. Obj_Lightning_Shield_DestroyUnderwater:
    162.  
    163.         bra.s    Obj_Lightning_Shield_FlashWater
    164.  
    165.  
    166.  
    167. Obj_Lightning_Shield_Destroy:
    168.  
    169. ;        move.b    #$XX,(a0)        ;Revert shield sprite to Insta-Shield (uncomment this if you installed the insta-shield) and change XX to the object ID you selected for the shields
    170.  
    171.     ;    move.b    #$0,$24(a0)    ;Reset the routine counter to trigger Insta-Shield Init
    172.  
    173.         clr.b    (v_shield).w    ; remove shield
    174.  
    175.         rts
    176.  
    177. ; ---------------------------------------------------------------------------
    178.  
    179.  
    180.  
    181. Obj_Lightning_Shield_FlashWater:
    182.  
    183.         addq.b    #2,obRoutine(a0)
    184.  
    185.         clr.b    (v_shield).w    ; remove shield
    186.  
    187. ;        move.b    #$XX,(a0)        ;Revert shield sprite to Insta-Shield (uncomment this if you installed the insta-shield) and change XX to the object ID you selected for the shields
    188.  
    189.     ;    move.b    #$0,$24(a0)    ;Reset the routine counter to trigger Insta-Shield Init        
    190.  
    191.  
    192.  
    193. ;    Flashes the underwater palette white
    194.  
    195.         lea    ($FFFFFA80).w,a1
    196.  
    197.         lea    ($FFFFFB80).w,a2
    198.  
    199.         move.w    #($80/4)-1,d0    ; Size of Water_palette/4-1
    200.  
    201.  
    202.  
    203. loc_197F2:
    204.  
    205.         move.l    (a1),(a2)+    ; Backup palette entries
    206.  
    207.         move.l    #$0EEE0EEE,(a1)+    ; Overwrite palette entries with white
    208.  
    209.         dbf    d0,loc_197F2    ; Loop until entire thing is overwritten
    210.  
    211.  
    212.  
    213.         move.b    #3,obTimeFrame(a0)
    214.  
    215.         rts
    216.  
    217.  
    218.  
    219. ; =============== S U B R O U T I N E =======================================
    220.  
    221.  
    222.  
    223.  
    224.  
    225. Obj_Lightning_Shield_Create_Spark:
    226.  
    227.         moveq    #1,d2
    228.  
    229.  
    230.  
    231. Obj_Lightning_Shield_Create_Spark_Part2:
    232.  
    233.         lea    (SparkVelocities).l,a2
    234.  
    235.         moveq    #3,d1
    236.  
    237.  
    238.  
    239. loc_19816:
    240.  
    241. ; Sprite_1D8F2:
    242.  
    243.         jsr    (FindFreeObj).l    ; Set up for a new object (SingleObjLoad for Hivebrain users instead of FindFreeObj)
    244.  
    245.         bne.s    locret_19862
    246.  
    247.         move.b    #XX,(a1)        ; Create Lightning Shield Spark (READ ME: XX WILL BE THE ID OF THE SPARKS)
    248.  
    249.         move.w    obX(a0),obX(a1)    ; (Spark) Inherit x_pos from source object (Lightning Shield, Hyper Sonic Stars)
    250.  
    251.         move.w    obY(a0),obY(a1)    ; (Spark) Inherit y_pos from source object (Lightning Shield, Hyper Sonic Stars)
    252.  
    253.         move.l    obMap(a0),obMap(a1)    ; (Spark) Inherit mappings from source object (Lightning Shield, Hyper Sonic Stars)
    254.  
    255.         move.w    obGfx(a0),obGfx(a1)    ; (Spark) Inherit art_tile from source object (Lightning Shield, Hyper Sonic Stars)
    256.  
    257.         move.b    #4,obRender(a1)
    258.  
    259.         move.b    #1,obPriority(a1)
    260.  
    261.         move.b    #8,obActWid(a1)
    262.  
    263.         move.b    d2,obAnim(a1)
    264.  
    265.         move.w    (a2)+,obVelX(a1)    ; (Spark) Give x_vel (unique to each of the four Sparks)
    266.  
    267.         move.w    (a2)+,obVelY(a1)    ; (Spark) Give y_vel (unique to each of the four Sparks)
    268.  
    269.         dbf    d1,loc_19816
    270.  
    271.  
    272.  
    273. locret_19862:
    274.  
    275.         rts
    276.  
    277. ; End of function Lightning_Shield_Create_Spark
    278.  
    279. ; ---------------------------------------------------------------------------
    280.  
    281. SparkVelocities:dc.w  -$200, -$200
    282.  
    283.     dc.w   $200, -$200
    284.  
    285.     dc.w  -$200,  $200
    286.  
    287.     dc.w   $200,  $200
    288.  
    289. ; ---------------------------------------------------------------------------
    290.  
    291. Obj_Lightning_Shield_Spark:
    292.  
    293.         jsr    (SpeedToPos).l
    294.  
    295.         addi.w    #$18,obVelY(a0)
    296.  
    297.         lea    (Ani_19A2A).l,a1
    298.  
    299.         jsr    (AnimateSprite).l
    300.  
    301.         tst.b    obRoutine(a0)    ; Changed by Animate_Sprite
    302.  
    303.         bne.s    Obj_Lightning_Shield_Spark_Delete
    304.  
    305.         jmp    (DisplaySprite).l
    306.  
    307. ; ---------------------------------------------------------------------------
    308.  
    309.  
    310.  
    311. Obj_Lightning_Shield_Spark_Delete:
    312.  
    313.         jmp    (DeleteObject).l
    314.  
    315. ; ---------------------------------------------------------------------------
    316.  
    317.  
    318.  
    319. Obj_Lightning_Shield_DestroyUnderwater2:
    320.  
    321.         subq.b    #1,obTimeFrame(a0)    ; Is it time to end the white flash?
    322.  
    323.         bpl.s    locret_198BC    ; If not, return
    324.  
    325.         clr.b    ($FFFFFE2C).w    ; remove shield
    326.  
    327.         lea    ($FFFFFB80).w,a1
    328.  
    329.         lea    ($FFFFFA80).w,a2
    330.  
    331.         move.w    #($80/4)-1,d0    ; Size of Water_palette/4-1
    332.  
    333.  
    334.  
    335. loc_198B6:
    336.  
    337.         move.l    (a1)+,(a2)+    ; Restore backed-up underwater palette
    338.  
    339.         dbf    d0,loc_198B6    ; Loop until entire thing is restored
    340.  
    341.  
    342.  
    343. locret_198BC:
    344.  
    345.         rts
    346.  
    347. ; ===========================================================================



    If you see $XX, that means you need to replace them with the ID of the insta-shield. If you don’t have that installed, leave it as it is. Let’s do the bubble and fire shields. For this, go back to the beginning of the tutorial and make sure you included a routine counter and have followed the areas needed to make



    Code (Text):
    1. Obj_Bubble_Shield:
    2.  
    3.         ; Init
    4.  
    5.         move.l    #Map_BubbleShield,mappings(a0)
    6.  
    7.         move.l    #DPLC_BubbleShield,DPLC_Address(a0)            ; Used by PLCLoad_Shields
    8.  
    9.         move.l    #ArtUnc_BubbleShield,Art_Address(a0)            ; Used by PLCLoad_Shields
    10.  
    11.         move.b    #4,render_flags(a0)
    12.  
    13.         move.w    #$80,priority(a0)
    14.  
    15.         move.b    #$18,width_pixels(a0)
    16.  
    17.         move.b    #$18,height_pixels(a0)
    18.  
    19.         move.w    #ArtTile_Shield,art_tile(a0)
    20.  
    21.         move.w    #tiles_to_bytes(ArtTile_Shield),vram_art(a0)    ; Used by PLCLoad_Shields
    22.  
    23.         btst    #7,(Player_1+art_tile).w
    24.  
    25.         beq.s    .nothighpriority
    26.  
    27.         bset    #7,art_tile(a0)
    28.  
    29.  
    30.  
    31.     .nothighpriority:
    32.  
    33.         move.w    #1,anim(a0)                ; Clear anim and set prev_anim to 1
    34.  
    35.         move.b    #-1,LastLoadedDPLC(a0)            ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    36.  
    37.         movea.w    parent(a0),a1
    38.  
    39.         bsr.w    Player_ResetAirTimer
    40.  
    41.         move.l    #Obj_Bubble_Shield_Main,(a0)
    42.  
    43.  
    44.  
    45. Obj_Bubble_Shield_Main:
    46.  
    47.         movea.w    parent(a0),a2
    48.  
    49.         btst    #Status_Invincible,status_secondary(a2)    ; Is player invincible?
    50.  
    51.         bne.s    locret_1998A                ; If so, do not display and do not update variables
    52.  
    53.         cmpi.b    #$1C,anim(a2)                ; Is player in their 'blank' animation?
    54.  
    55.         beq.s    locret_1998A                ; If so, do not display and do not update variables
    56.  
    57.         btst    #Status_Shield,status_secondary(a2)    ; Should the player still have a shield?
    58.  
    59.         beq.s    Obj_Bubble_Shield_Destroy        ; If not, change to Insta-Shield
    60.  
    61.         move.w    x_pos(a2),x_pos(a0)
    62.  
    63.         move.w    y_pos(a2),y_pos(a0)
    64.  
    65.         move.b    status(a2),status(a0)            ; Inherit status
    66.  
    67.         andi.b    #1,status(a0)                ; Limit inheritance to 'orientation' bit
    68.  
    69.         tst.b    (Reverse_gravity_flag).w
    70.  
    71.         beq.s    .normalgravity
    72.  
    73.         ori.b    #2,status(a0)                ; Reverse the vertical mirror render_flag bit (On if Off beforehand and vice versa)
    74.  
    75.  
    76.  
    77.     .normalgravity:
    78.  
    79.         andi.w    #drawing_mask,art_tile(a0)
    80.  
    81.         tst.w    art_tile(a2)
    82.  
    83.         bpl.s    .nothighpriority
    84.  
    85.         ori.w    #high_priority,art_tile(a0)
    86.  
    87.  
    88.  
    89.     .nothighpriority:
    90.  
    91.         lea    (Ani_BubbleShield).l,a1
    92.  
    93.         jsr    (Animate_Sprite).l
    94.  
    95.         bsr.w    PLCLoad_Shields
    96.  
    97.         jmp    (Draw_Sprite).l
    98.  
    99. ; ---------------------------------------------------------------------------
    100.  
    101.  
    102.  
    103. locret_1998A:
    104.  
    105.         rts
    106.  
    107. ; ---------------------------------------------------------------------------
    108.  
    109.  
    110.  
    111. Obj_Bubble_Shield_Destroy:
    112.  
    113.         andi.b    #$8E,status_secondary(a2)    ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0
    114.  
    115.         move.l    #Obj_Insta_Shield,(a0)        ; Replace the Bubble Shield with the Insta-Shield
    116.  
    117.         rts





    If you followed the changes successfully, this should be the final result:



    Code (Text):
    1. BubbleShield_Obj:
    2.  
    3.         moveq    #0,d0
    4.  
    5.         move.b    obRoutine(a0),d0
    6.  
    7.         move.w    BubbleShield_Index(pc,d0.w),d1
    8.  
    9.         jmp    BubbleShield_Index(pc,d1.w)
    10.  
    11.  
    12.  
    13. ; ==========================================================================
    14.  
    15.  
    16.  
    17. BubbleShield_Index:
    18.  
    19.         dc.w BubbleShield_Init-BubbleShield_Index
    20.  
    21.         dc.w BubbleShield_Main-BubbleShield_Index
    22.  
    23.  
    24.  
    25. ; ==========================================================================
    26.  
    27. BubbleShield_Init:
    28.  
    29. ;    Init
    30.  
    31.         move.l    #Map_BubbleShield,obMap(a0)
    32.  
    33.         move.l    #DPLC_BubbleShield,shield_DPLC_Address(a0)    ; Used by PLCLoad_Shields
    34.  
    35.         move.l    #ArtUnc_BubbleShield,shield_Art_Address(a0)    ; Used by PLCLoad_Shields
    36.  
    37.         move.b    #4,obRender(a0)
    38.  
    39.         move.b    #1,obPriority(a0)
    40.  
    41.         move.b    #$18,obActWid(a0)
    42.  
    43.         move.w    #$541,obGfx(a0)
    44.  
    45.         move.w    #$A820,shield_vram_art(a0)    ; Used by PLCLoad_Shields
    46.  
    47.         btst    #7,(v_player+obGfx).w
    48.  
    49.         beq.s    loc_195F0B
    50.  
    51.         bset    #7,obGFX(a0)
    52.  
    53.  
    54.  
    55. loc_195F0B:
    56.  
    57.         move.w    #1,obAnim(a0)    ; Clear anim and set prev_anim to 1
    58.  
    59.         move.b    #-1,shield_LastLoadedDPLC(a0)    ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    60.  
    61.         lea    (v_player).w,a1
    62.  
    63.         jsr    (ResumeMusic).l
    64.  
    65.         addq.b    #2,obRoutine(a0) ; => ObjE5_Shield
    66.  
    67. ; loc_1D92C:
    68.  
    69. BubbleShield_Main:
    70.  
    71.         lea    (v_player).w,a2 ; a2=character
    72.  
    73.         tst.b    (v_invinc).w
    74.  
    75.         bne.s    locret_1998A
    76.  
    77.         cmpi.b    #$1C,obAnim(a2)    ; Is player in their 'blank' animation?
    78.  
    79.         beq.s    locret_1998A    ; If so, do not display and do not update variables
    80.  
    81.         tst.b    (v_shield).w
    82.  
    83.         beq.s    Obj_Bubble_Shield_Destroy    ; If not, change to Insta-Shield
    84.  
    85.         move.w    obX(a2),obX(a0)
    86.  
    87.         move.w    obY(a2),obY(a0)
    88.  
    89.         andi.w    #$7FFF,obGFX(a0)
    90.  
    91.         tst.w    obGFX(a2)
    92.  
    93.         bpl.s    @nothighpriority1
    94.  
    95.         ori.w    #$8000,obGFX(a0)
    96.  
    97.  
    98.  
    99.     @nothighpriority1:
    100.  
    101.         lea    (Ani_19A7A).l,a1
    102.  
    103.         jsr    (AnimateSprite).l
    104.  
    105.         jsr    PLCLoad_Shields
    106.  
    107.         jmp    (DisplaySprite).l
    108.  
    109. ; ---------------------------------------------------------------------------
    110.  
    111.  
    112.  
    113. locret_1998A:
    114.  
    115.         rts
    116.  
    117. ; ---------------------------------------------------------------------------
    118.  
    119.  
    120.  
    121. Obj_Bubble_Shield_Destroy:
    122.  
    123.     ;    move.b    #$XX,(a0)        ;Revert shield sprite to Insta-Shield
    124.  
    125.         ;move.b    #$0,$24(a0)    ;Reset the routine counter to trigger Insta-Shield Init
    126.  
    127.         clr.b    (v_shield).w    ; remove shield
    128.  
    129.         rts
    130.  
    131. ; ==========================================================================



    Lastly, for the fire shield, here’s the code before:



    Code (Text):
    1. Obj_Fire_Shield:
    2.  
    3.         ; Init
    4.  
    5.         move.l    #Map_FireShield,mappings(a0)
    6.  
    7.         move.l    #DPLC_FireShield,DPLC_Address(a0)            ; Used by PLCLoad_Shields
    8.  
    9.         move.l    #ArtUnc_FireShield,Art_Address(a0)            ; Used by PLCLoad_Shields
    10.  
    11.         move.b    #4,render_flags(a0)
    12.  
    13.         move.w    #$80,priority(a0)
    14.  
    15.         move.b    #$18,width_pixels(a0)
    16.  
    17.         move.b    #$18,height_pixels(a0)
    18.  
    19.         move.w    #ArtTile_Shield,art_tile(a0)
    20.  
    21.         move.w    #tiles_to_bytes(ArtTile_Shield),vram_art(a0)    ; Used by PLCLoad_Shields
    22.  
    23.         btst    #7,(Player_1+art_tile).w
    24.  
    25.         beq.s    loc_195F0
    26.  
    27.         bset    #7,art_tile(a0)
    28.  
    29.  
    30.  
    31. loc_195F0:
    32.  
    33.         move.w    #1,anim(a0)                ; Clear anim and set prev_anim to 1
    34.  
    35.         move.b    #-1,LastLoadedDPLC(a0)            ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    36.  
    37.         move.l    #Obj_Fire_Shield_Main,(a0)
    38.  
    39.  
    40.  
    41. Obj_Fire_Shield_Main:
    42.  
    43.         movea.w    parent(a0),a2
    44.  
    45.         btst    #Status_Invincible,status_secondary(a2) ; Is player invincible?
    46.  
    47.         bne.w    locret_19690                ; If so, do not display and do not update variables
    48.  
    49.         cmpi.b    #$1C,anim(a2)                ; Is player in their 'blank' animation?
    50.  
    51.         beq.s    locret_19690                ; If so, do not display and do not update variables
    52.  
    53.         btst    #Status_Shield,status_secondary(a2)     ; Should the player still have a shield?
    54.  
    55.         beq.w    Obj_Fire_Shield_Destroy            ; If not, change to Insta-Shield
    56.  
    57.         btst    #Status_Underwater,status(a2)        ; Is player underwater?
    58.  
    59.         bne.s    Obj_Fire_Shield_DestroyUnderwater    ; If so, branch
    60.  
    61.         move.w    x_pos(a2),x_pos(a0)
    62.  
    63.         move.w    y_pos(a2),y_pos(a0)
    64.  
    65.         tst.b    anim(a0)                ; Is shield in its 'dashing' state?
    66.  
    67.         bne.s    .nothighpriority            ; If so, do not update orientation or allow changing of the priority art_tile bit
    68.  
    69.         move.b    status(a2),status(a0)            ; Inherit status
    70.  
    71.         andi.b    #1,status(a0)                ; Limit inheritance to 'orientation' bit
    72.  
    73.         tst.b    (Reverse_gravity_flag).w
    74.  
    75.         beq.s    .normalgravity
    76.  
    77.         ori.b    #2,status(a0)                ; If in reverse gravity, reverse the vertical mirror render_flag bit (On if Off beforehand and vice versa)
    78.  
    79.  
    80.  
    81.     .normalgravity:
    82.  
    83.         andi.w    #drawing_mask,art_tile(a0)
    84.  
    85.         tst.w    art_tile(a2)
    86.  
    87.         bpl.s    .nothighpriority
    88.  
    89.         ori.w    #high_priority,art_tile(a0)
    90.  
    91.  
    92.  
    93.     .nothighpriority:
    94.  
    95.         lea    (Ani_FireShield).l,a1
    96.  
    97.         jsr    (Animate_Sprite).l
    98.  
    99.         move.w    #$80,priority(a0)        ; Layer shield over player sprite
    100.  
    101.         cmpi.b    #$F,mapping_frame(a0)        ; Are these the frames that display in front of the player?
    102.  
    103.         blo.s    .overplayer            ; If so, branch
    104.  
    105.         move.w    #$200,priority(a0)        ; If not, layer shield behind player sprite
    106.  
    107.  
    108.  
    109.     .overplayer:
    110.  
    111.         bsr.w    PLCLoad_Shields
    112.  
    113.         jmp    (Draw_Sprite).l
    114.  
    115. ; ---------------------------------------------------------------------------
    116.  
    117.  
    118.  
    119. locret_19690:
    120.  
    121.         rts
    122.  
    123. ; ---------------------------------------------------------------------------
    124.  
    125.  
    126.  
    127. Obj_Fire_Shield_DestroyUnderwater:
    128.  
    129.         andi.b    #$8E,status_secondary(a2)    ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0
    130.  
    131.         jsr    (Create_New_Sprite).l        ; Set up for a new object
    132.  
    133.         bne.w    Obj_Fire_Shield_Destroy        ; If that can't happen, branch
    134.  
    135.         move.l    #Obj_FireShield_Dissipate,(a1)    ; Create dissipate object
    136.  
    137.         move.w    x_pos(a0),x_pos(a1)        ; Put it at shields' x_pos
    138.  
    139.         move.w    y_pos(a0),y_pos(a1)        ; Put it at shields' y_pos
    140.  
    141.  
    142.  
    143. Obj_Fire_Shield_Destroy:
    144.  
    145.         andi.b    #$8E,status_secondary(a2)    ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0
    146.  
    147.         move.l    #Obj_Insta_Shield,(a0)        ; Replace the Fire Shield with the Insta-Shield
    148.  
    149.         rts





    And here’s after:



    Code (Text):
    1. FireShield_Obj:
    2.  
    3.         moveq    #0,d0
    4.  
    5.         move.b    obRoutine(a0),d0
    6.  
    7.         move.w    FireShield_Index(pc,d0.w),d1
    8.  
    9.         jmp    FireShield_Index(pc,d1.w)
    10.  
    11. ; ===========================================================================
    12.  
    13.  
    14.  
    15. FireShield_Index:
    16.  
    17.         dc.w FireShield_Init-FireShield_Index
    18.  
    19.         dc.w FireShield_Main-FireShield_Index
    20.  
    21. ; ===========================================================================
    22.  
    23.  
    24.  
    25. FireShield_Init:
    26.  
    27.         move.l    #Map_FireShield,mappings(a0)
    28.  
    29.         move.l    #DPLC_FireShield,shield_DPLC_Address(a0)    ; Used by PLCLoad_Shields
    30.  
    31.         move.l    #ArtUnc_FireShield,shield_Art_Address(a0)    ; Used by PLCLoad_Shields
    32.  
    33.         move.b    #4,obRender(a0)
    34.  
    35.         move.b    #1,obPriority(a0)
    36.  
    37.         move.b    #$18,obActWid(a0)
    38.  
    39.         move.w    #$541,obGfx(a0)
    40.  
    41.         move.w    #$A820,shield_vram_art(a0)    ; Used by PLCLoad_Shields
    42.  
    43.         btst    #7,(v_player+obGfx).w
    44.  
    45.         beq.s    loc_195F0
    46.  
    47.         bset    #7,obGfx(a0)
    48.  
    49.  
    50.  
    51. loc_195F0:
    52.  
    53.         move.w    #1,obAnim(a0)    ; Clear anim and set prev_anim to 1
    54.  
    55.         move.b    #-1,shield_LastLoadedDPLC(a0)    ; Reset LastLoadedDPLC (used by PLCLoad_Shields)
    56.  
    57.         addq.b    #2,obRoutine(a0) ; => FireShield_Main
    58.  
    59.  
    60.  
    61. FireShield_Main:
    62.  
    63.         lea    (v_player).w,a2
    64.  
    65.         tst.b    (v_invinc).w
    66.  
    67.         bne.w    locret_19690    ; If so, do not display and do not update variables
    68.  
    69.         cmpi.b    #$1C,obAnim(a2)    ; Is player in their 'blank' animation?
    70.  
    71.         beq.s    locret_19690    ; If so, do not display and do not update variables
    72.  
    73.         tst.b    (v_shield).w     ; Should the player still have a shield?
    74.  
    75.         beq.w    Obj_Fire_Shield_Destroy    ; If not, change to Insta-Shield
    76.  
    77.         btst    #6,obStatus(a2)    ; Is player underwater?
    78.  
    79.         bne.s    Obj_Fire_Shield_DestroyUnderwater    ; If so, branch
    80.  
    81.         move.w    obX(a2),obX(a0)
    82.  
    83.         move.w    obY(a2),obY(a0)
    84.  
    85.         tst.b    obAnim(a0)    ; Is shield in its 'dashing' state?
    86.  
    87.         bne.s    @nothighpriority    ; If so, do not update orientation or allow changing of the priority art_tile bit
    88.  
    89.         move.b    obStatus(a2),obStatus(a0)    ; Inherit status
    90.  
    91.         andi.b    #1,obStatus(a0)    ; Limit inheritance to 'orientation' bit
    92.  
    93.         andi.w    #$7FFF,obGFX(a0)
    94.  
    95.         tst.w    obGFX(a2)
    96.  
    97.         bpl.s    @nothighpriority
    98.  
    99.         ori.w    #$8000,obGFX(a0)
    100.  
    101.  
    102.  
    103.     @nothighpriority:
    104.  
    105.         lea    (Ani_19A02).l,a1
    106.  
    107.         jsr    (AnimateSprite).l
    108.  
    109.         move.b    #1,obPriority(a0)    ; Layer shield over player sprite
    110.  
    111.         cmpi.b    #$F,mapping_frame(a0)    ; Are these the frames that display in front of the player?
    112.  
    113.         blo.s    @overplayer    ; If so, branch
    114.  
    115.         move.b    #3,obPriority(a0)    ; If not, layer shield behind player sprite
    116.  
    117.  
    118.  
    119.     @overplayer:
    120.  
    121.         jsr    PLCLoad_Shields
    122.  
    123.         jmp    (DisplaySprite).l
    124.  
    125. ; ---------------------------------------------------------------------------
    126.  
    127. locret_19690:
    128.  
    129.         rts
    130.  
    131. ; ---------------------------------------------------------------------------
    132.  
    133.  
    134.  
    135. Obj_Fire_Shield_DestroyUnderwater:
    136.  
    137.         clr.b    ($FFFFFE2C).w        ; remove shield
    138.  
    139.         jsr    (FindFreeObj).l        ; Make Smoke Puff Sprite when fire shield in water
    140.  
    141.         bne.w    Obj_Fire_Shield_Destroy
    142.  
    143.         move.b    #id_MissileDissolve,(a1)
    144.  
    145.         move.b    #4,obRoutine(a1)
    146.  
    147.         move.l    #Map_MisDissolve,obMap(a1) ; (Map_Obj24 for Hivebrain users)
    148.  
    149.         move.w    #$5A0,obGFX(a1)
    150.  
    151.         move.b    #4,obRender(a1)
    152.  
    153.         move.b    #1,obPriority(a1)
    154.  
    155.         move.b    #0,obColType(a1)
    156.  
    157.         move.b    #$C,obActWid(a1)
    158.  
    159.         move.b    #7,obTimeFrame(a1)            ; set frame duration to    7 frames
    160.  
    161.         move.b    #0,obFrame(a1)
    162.  
    163.         move.w    obX(a0),obX(a1)    ; Put it at shields' x_pos
    164.  
    165.         move.w    obY(a0),obY(a1)    ; Put it at shields' y_pos
    166.  
    167.  
    168.  
    169. Obj_Fire_Shield_Destroy:
    170.  
    171.     ;    move.b    #$XX,(a0)        ;Revert shield sprite to Insta-Shield
    172.  
    173.         ;move.b    #$0,$24(a0)    ;Reset the routine counter to trigger Insta-Shield Init
    174.  
    175.         clr.b    (v_shield).w        ; remove shield
    176.  
    177.         rts
    178.  
    179. ; ===========================================================================



    If you haven't noticed, I've shortened the art and mappings names by removing _Obj just to clean out everything, you can name it anything you like but be careful when naming things otherwise it will lead to errors. Now we need to include the art. I will quote the steps from the first post but modify them to work with the sparks. So take it away, quote me!





    First of all, make sure you got your SK disassembly ready. We're going to the bubble shield for example but it should be straightforward to do the other shields. The easiest thing to do is to go to:



    “skdisasm-master\General\Sprites\Shields” and look for DPLC - Bubble Shield.asm. Since PLCLoad_Shield retains using the same S3K format when it comes to DPLC mappings, we don’t need to change anything, so copy that file and paste it in the disassembly in your hack. As for the mappings, we need to convert to format to Sonic 1. You can use one of the three tools to do this:



    One tool is called SonMapEd. Open it up and set the Game Format setting to "Sonic 3 & Knuckles", load the mappings file, change the Game Format setting to "Sonic 1", and save the mappings as “Map - Bubble Shield.asm” in the “_maps” folder of your disassembly.



    Another tool is MappingsConverter. Open it up and load the mappings with input set to Game: "Sonic 3 & Knuckles", Format: ASM and output set to Game: "Sonic 1", Format: ASM, and save the mappings as “Map - Bubble Shield.asm” in the “_maps” folder of your disassembly.



    Another tool is Flex2. Open it up and insert the mappings and make sure the format is set as S3K. Load it, convert it to Sonic 1 and save it. The tool should overwrite the original mapping, and you should go back to the shields folder of the SK disassembly, copy the mappings and place it in “_maps” folder of your disassembly.



    As for the art, you’ll need to load PaletteConverter as the player palettes between Sonic 3K and 1 are vastly different. Open it up and you will see this:



    Capture (1).PNG





    We are going to do this according to the numbers I added.



    First of all, click on load BIN and open up Sonic’s S3K palette file. It should be under:


    “skdisasm-master\General\Sprites\Sonic\Palettes\SonicandTails.bin”



    2. Secondly, open up Sonic’s S1 palette file. It should be under:



    “yourdisassembly\pallet\Sonic.bin”



    You need to match the palette bytes by dragging Sonic 3K’s byte to match S1’s; and the outcome should be this:



    Capture (1).PNG





    3. Open up convert uncompressed file and open up the Insta-Shield art; It should be located at:



    Code:

    “skdisasm-master\General\Sprites\Shields\Bubble Shield.bin”



    It will give you a pop-up asking if you want to overwrite the source with the converted artwork. Say no and save the file in your disassembly in artunc and name it: “Bubble Shield.bin”



    I’ve linked a .rar file with everything converted here if you struggled to get that set up. Then, just drag and drop the files to its respective folder.



    Open up sonic1.asm, and under the fire shield object, insert this:





    Code (Text):
    1. Map_LightningShield:
    2.  
    3.         include "_maps\Lightning Shield.asm"
    4.  
    5.         even
    6.  
    7.  
    8.  
    9. DPLC_LightningShield:
    10.  
    11.         include "_map\Lightning Shield.asm"
    12.  
    13.         even
    14.  
    15.  
    16.  
    17.  
    18.  
    19. ArtUnc_LightningShield:
    20.  
    21.         incbin artunc\ArtUnc_Electroshield.bin
    22.  
    23.         even
    24.  
    25. Ani_199EA:
    26.  
    27.         dc.w byte_199EE-Ani_199EA
    28.  
    29.         dc.w byte_199F1-Ani_199EA
    30.  
    31. byte_199EE:    dc.b  $1F,   6,    $FF
    32.  
    33. byte_199F1:    dc.b    0,   0,      1,   2,   3,     4,   5,   6,    6,   6,      6,   6,   6,     6,   7, $FD,    0
    34.  
    35. Ani_19A02:
    36.  
    37.         dc.w byte_19A06-Ani_19A02
    38.  
    39.         dc.w byte_19A1A-Ani_19A02
    40.  
    41. byte_19A06:    dc.b    1,   0,     $F,   1, $10,     2, $11,   3, $12,   4,    $13,   5, $14,     6, $15,   7, $16,   8,    $17, $FF
    42.  
    43. byte_19A1A:    dc.b    1,   9,     $A,  $B,  $C,    $D,  $E,   9,  $A,  $B,     $C,  $D,  $E, $FD,   0,   0
    44.  
    45. Ani_19A2A:
    46.  
    47.         dc.w Anibyte_19A30-Ani_19A2A
    48.  
    49.         dc.w byte_19A5C-Ani_19A2A
    50.  
    51.         dc.w byte_19A73-Ani_19A2A
    52.  
    53. Anibyte_19A30:
    54.  
    55.         dc.b    1,   0,      0,   1,   1,     2,   2,   3,    3,   4,      4,   5,   5,     6,   6,   7,    7,   8,      8,   9,  $A,    $B, $16, $16, $15, $15,    $14, $14, $13, $13, $12, $12
    56.  
    57.         dc.b  $11, $11,    $10, $10,  $F,    $F,  $E,  $E,    9,  $A,     $B, $FF
    58.  
    59. byte_19A5C:
    60.  
    61.         dc.b    0,  $C,     $D, $17,  $C,    $D, $17,  $C,  $D, $17,     $C,  $D, $17,    $C,  $D, $17,  $C,  $D,    $17,  $C,  $D, $FC, $FF
    62.  
    63. byte_19A73:
    64.  
    65.         dc.b    3,   0,      1,   2, $FC, $FF,   0
    66.  
    67. Ani_19A7A:
    68.  
    69.         dc.w byte_19A80-Ani_19A7A
    70.  
    71.         dc.w byte_19AB8-Ani_19A7A
    72.  
    73.         dc.w byte_19ABF-Ani_19A7A
    74.  
    75. byte_19A80:
    76.  
    77.         dc.b    1,   0,      9,   0,   9,     0,   9,   1,  $A,   1,     $A,   1,  $A,     2,   9,   2,    9,   2,      9,   3,  $A,     3,  $A,   3,  $A,   4,      9,   4,   9,     4,   9,   5
    78.  
    79.         dc.b   $A,   5,     $A,   5,  $A,     6,   9,   6,    9,   6,      9,   7,  $A,     7,  $A,   7,  $A,   8,      9,   8,   9,     8,   9, $FF
    80.  
    81. byte_19AB8:    dc.b    5,   9,     $B,  $B,  $B, $FD,   0
    82.  
    83. byte_19ABF:    dc.b    5,  $C,     $C,  $B, $FD,     0,   0
    84.  
    85.     even
    86.  
    87. ArtUnc_LightningSparks:
    88.  
    89.         incbin artunc\Lightning Sparks.bin
    90.  
    91.         even
    92.  
    93.  
    94.  
    95.  
    96.  
    97. Map_FireShield:
    98.  
    99.         include "_maps\Fire Shield.asm"
    100.  
    101.         even
    102.  
    103.  
    104.  
    105. DPLC_FireShield:
    106.  
    107.         include "_maps\Fire Shield DPLC.asm"
    108.  
    109.         even
    110.  
    111.  
    112.  
    113.  
    114.  
    115. ArtUnc_FireShield:
    116.  
    117.         incbin "artunc\FireShield.bin"
    118.  
    119.         even
    120.  
    121. Map_BubbleShield:
    122.  
    123.         include "_maps\Bubble Shield.asm"
    124.  
    125.         even
    126.  
    127.  
    128.  
    129. DPLC_BubbleShield:
    130.  
    131.         include "_maps\Bubble Shield DPLC.asm"
    132.  
    133.         even
    134.  
    135.  
    136.  
    137.  
    138.  
    139. ArtUnc_BubbleShield:
    140.  
    141.          incbin "artunc\BubbleShield.bin"
    142.  
    143.          even



    OK, let’s load the object in Object Pointers.asm (it’s in the _inc folder). We need to hunt for a free object. The best choice is to find something like this:



    Code (Text):
    1. ptr_Obj05:        dc.l NullObject


    This is a dummy object. It sounds simple enough, but we can replace it with the name of our object to load the lightning shield.



    Code (Text):
    1. id_Obj05:        equ ((ptr_Obj05-Obj_Index)/4)+1


    You also need to follow the same procedure with the sparks, bubble and fire shields and find free objects. I’m going to reference id_Obj05 as id_LightningShield for the remainder of the tutorial. Now to load it! I’m going to replace the contents of the shield monitor with the lightning shield, in this case, so open up 2E Monitor Content Power-Up.asm and look for Pow_ChkShield and replace this:



    Code (Text):
    1.         move.b    #1,(v_shield).w    ; give Sonic a shield
    2.  
    3.         move.b    #id_ShieldItem,(v_objspace+$180).w ; load shield object ($38)


    With this:





    Code (Text):
    1.         move.b    #$4,(v_shield).w   ;give Sonic a shield with lightning attribute
    2.  
    3.         move.b    #id_LightningShield,(v_eshield).w ; load lightning shield object
    4.  
    5.         move.b    #id_LightningShieldSpark,(v_eshield+$1F).w ; load lightning shield spark object
    6.  
    7.         clr.b    (v_eshield+obRoutine).w


    You may have noticed a new variable called v_eshield. From what I can remember, this will be used when using the elemental shields to change objects and reset routines on the fly. If you want to load the other two shields, you can place them in free monitor content. The Eggman, S or goggles could be replaced if you haven’t modified anything inside of them. Be sure to change the attribute ($8 for bubble and 2 for fire as we will need it when inserting the moves) and ID of the shields.



    Hopefully, the objects load successfully and act as normal shields. If so, well done! You have done the most challenging part of the tutorial. Next time, I will be showing you how to load the moves and install the ring attraction code for the lightning shield. If it’s alright, please credit me for writing and sharing this with everyone, as I put so much effort into writing and getting this down.

    Also, kudos to AngelKOR64 for initially improving the code I originally had intact.



    Until then...
     

    Attached Files:

    Last edited: Nov 28, 2021 at 7:11 PM