; --------------------------------------------------------------------------- ; Elemental shields from S3K to S1. ; Ported by TheBlad768 (2018). ; --------------------------------------------------------------------------- ; Object variables obSlot_Shields: equ $38 ; Set current object slot in object pointers obVRAM_Shields: = ($A820/$20) ; Set VRAM address obVRAM_LightningShields_Spark: = $AC00 ; Set VRAM address ; --------------------------------------------------------------------------- ; Universal object variables obRender: equ 1 ; .b obGfx: equ 2 ; .w obMap: equ 4 ; .l obX: equ 8 ; .w.l obScreenY: equ $A ; .w obY: equ $C ; .w.l obVelX: equ $10 ; .w obVelY: equ $12 ; .w obInertia: equ $14 ; .w obHeight: equ $16 ; .b obWidth: equ $17 ; .b obPriority: equ $18 ; .b obActWid: equ $19 ; .b obFrame: equ $1A ; .b obAniFrame: equ $1B ; .b obAnim: equ $1C ; .b obTimeFrame: equ $1E ; .b obColType: equ $20 ; .b obColProp: equ $21 ; .b obStatus: equ $22 ; .b obRoutine: equ $24 ; .b ob2ndRout: equ $25 ; .b obAngle: equ $26 ; .b.w obSubtype: equ $28 ; .b.w ; Dynamic object variables obDPLC_Address: equ $2A ; .l obArt_Address: equ $2E ; .l obVRAM_Art: equ $32 ; .w obLastLoadedDPLC: equ $34 ; .b obLoadShield: equ $35 ; .b obSaveShield: equ $36 ; .w ;obNull equ $38 ; .w ;obNull equ $3A ; .w ;obNull equ $3C ; .w ;obNull equ $3E ; .w ; --------------------------------------------------------------------------- ; Player Status Variables Status_Facing: equ 0 Status_InAir: equ 1 Status_Roll: equ 2 Status_OnObj: equ 3 Status_RollJump: equ 4 Status_Push: equ 5 Status_Underwater: equ 6 ; --------------------------------------------------------------------------- ; Player status_secondary variables Status_Shield: equ 0 Status_Invincible: equ 1 Status_BlueShield: equ 2 Status_FireShield: equ 4 Status_LtngShield: equ 5 Status_BublShield: equ 6 ; --------------------------------------------------------------------------- ; Sonic/objects variables status_secondary: equ $2B ; Sonic double_jump_flag: equ $2F ; Sonic shield_reaction: equ $2F ; Objects ; bit 3 = bounces off shield, bit 4 = negated by fire shield, bit 5 = negated by lightning shield, bit 6 = negated by bubble shield ; --------------------------------------------------------------------------- ; RAM v_Shield: equ $FFFFD180 ; pointer v_player: equ $FFFFD000 ; main character v_tracksonic: equ $FFFFCB00 v_trackpos: equ $FFFFF7A8 v_pal_water_dup: equ $FFFFFA00 v_pal_water: equ $FFFFFA80 H_scroll_frame_offset: equ $FFFFFF94 ; w ; --------------------------------------------------------------------------- ; Macros offsetTable macro * \* EQU * current_offset_table = \* endm offsetTableEntry macro ptr dc.\0 ptr-current_offset_table endm ; =============== S U B R O U T I N E ======================================= Obj_LoadShield: lea (v_player).w,a2 moveq #0,d0 move.b obRoutine(a0),d0 move.w LoadShield_Index(pc,d0.w),d0 jmp LoadShield_Index(pc,d0.w) ; --------------------------------------------------------------------------- LoadShield_Index: offsetTable offsetTableEntry.w LoadShield_Init ; 0 LoadInstaShield_Index offsetTableEntry.w Obj_InstaShield_Main ; 2 LoadInvincibility_Index offsetTableEntry.w Obj_Invincibility_Init ; 4 offsetTableEntry.w Obj_Invincibility_Main ; 6 LoadInvincibility2_Index offsetTableEntry.w Obj_Invincibility_Main2 ; 8 LoadBlueShield_Index offsetTableEntry.w Obj_BlueShield_Main ; A LoadFireShield_Index offsetTableEntry.w Obj_FireShield_Main ; C LoadFireShield_Dissipate_Index offsetTableEntry.w Obj_FireShield_Dissipate ; E offsetTableEntry.w Obj_FireShield_Dissipate_Main ; 10 LoadLightningShield_Index offsetTableEntry.w Obj_LightningShield_LoadArt ; 12 offsetTableEntry.w Obj_LightningShield_Main ; 14 offsetTableEntry.w Obj_LightningShield_DestroyUnderwater2 ; 16 LoadLightningShield_Spark_Index offsetTableEntry.w Obj_LightningShield_Spark ; 18 offsetTableEntry.w Obj_LightningShield_Spark_Delete ; 1A LoadBubbleShield_Index offsetTableEntry.w Obj_BubbleShield_ResetAir ; 1C offsetTableEntry.w Obj_BubbleShield_Main ; 1E ; --------------------------------------------------------------------------- LoadShield_DataIndex: offsetTable offsetTableEntry.w InstaShield_DataIndex ; 0 offsetTableEntry.w Invincibility_DataIndex ; 2 offsetTableEntry.w BlueShield_DataIndex ; 4 offsetTableEntry.w InstaShield_DataIndex ; 6 offsetTableEntry.w FireShield_DataIndex ; 8 offsetTableEntry.w LightningShield_DataIndex ; A offsetTableEntry.w BubbleShield_DataIndex ; C InstaShield_DataIndex: dc.l Map_InstaShield,DPLC_InstaShield,(LoadInstaShield_Index-LoadShield_Index)<<24|ArtUnc_InstaShield Invincibility_DataIndex: dc.l Map_Invincibility,0,(LoadInvincibility_Index-LoadShield_Index)<<24 BlueShield_DataIndex: dc.l Map_BlueShield,DPLC_BlueShield,(LoadBlueShield_Index-LoadShield_Index)<<24|ArtUnc_BlueShield FireShield_DataIndex: dc.l Map_FireShield,DPLC_FireShield,(LoadFireShield_Index-LoadShield_Index)<<24|ArtUnc_FireShield LightningShield_DataIndex: dc.l Map_LightningShield,DPLC_LightningShield,(LoadLightningShield_Index-LoadShield_Index)<<24|ArtUnc_LightningShield BubbleShield_DataIndex: dc.l Map_BubbleShield,DPLC_BubbleShield,(LoadBubbleShield_Index-LoadShield_Index)<<24|ArtUnc_BubbleShield ; --------------------------------------------------------------------------- LoadShield_Init: move.b obLoadShield(a0),d0 add.w d0,d0 move.w #obVRAM_Shields,obGfx(a0) ; Set VRAM move.w #obVRAM_Shields*$20,obVRAM_Art(a0) ; Set VRAM*20 lea LoadShield_DataIndex(pc),a1 adda.w (a1,d0.w),a1 ; Get address move.l (a1)+,obMap(a0) ; Set current mapping move.l (a1)+,obDPLC_Address(a0) ; Set current DPLC mapping move.b (a1),obRoutine(a0) ; Set current object routine move.l (a1)+,d0 ; Get uncompressed art address ; XXFFFFFF - XX routine, FFFFFF - Art address andi.l #$FFFFFF,d0 ; Fix XXFFFFFF to 00FFFFFF move.l d0,obArt_Address(a0) ; Set current uncompressed art address move.b #4,obRender(a0) move.b #1,obPriority(a0) move.b #$18,obWidth(a0) move.b #$18,obActWid(a0) move.b #$18,obHeight(a0) btst #7,(v_player+obGfx).w beq.s @3 bset #7,obGfx(a0) @3 move.w #1,obAnim(a0) move.b #-1,obLastLoadedDPLC(a0) LoadShield_Locret: rts ; =============== S U B R O U T I N E ======================================= Obj_InstaShield_Main: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? bne.s LoadShield_Locret ; If so, return move.w obX(a2),obX(a0) ; Inherit player's obX move.w obY(a2),obY(a0) ; Inherit player's obY move.b obStatus(a2),obStatus(a0) ; Inherit status andi.b #1,obStatus(a0) ; Limit inheritance to 'orientation' bit andi.w #$7FFF,obGfx(a0) tst.w obGfx(a2) bpl.s @1 ori.w #$8000,obGfx(a0) @1 lea (Ani_InstaShield).l,a1 jsr (AnimateSprite).l cmpi.b #7,obFrame(a0) ; Has it reached then end of its animation? bne.s @2 ; If not, branch tst.b double_jump_flag(a2) ; Is it in its attacking state? beq.s @2 ; If not, branch move.b #2,double_jump_flag(a2) ; Mark attack as over @2 tst.b obFrame(a0) ; Is this the first frame? beq.s @3 ; If so, branch and load the DPLC for this and the next few frames cmpi.b #3,obFrame(a0) ; Is this the third frame? bne.s @4 ; If not, branch as we don't need to load another DPLC yet @3 bsr.w PLCLoad_Shields @4 jmp (DisplaySprite).l ; =============== S U B R O U T I N E ======================================= Obj_Invincibility_Init: addq.b #2,obRoutine(a0) move.l #ArtUnc_Invincibility,d1 ; Load art source move.w #obVRAM_Shields*$20,d2 ; Load art destination move.w #(ArtUnc_Invincibility_end-ArtUnc_Invincibility)/2,d3 ; Size of art (in words) jsr (QueueDMATransfer).l movea.l a0,a1 moveq #0,d1 move.b d1,obAnim(a0) moveq #3-1,d0 @loop lea $40(a1),a1 move.b #obSlot_Shields,(a1) move.l obMap(a0),obMap(a1) move.w obGfx(a0),obGfx(a1) move.b obRender(a0),obRender(a1) move.b obPriority(a0),obPriority(a1) move.b obWidth(a0),obWidth(a1) move.b obActWid(a0),obActWid(a1) move.b obHeight(a0),obHeight(a1) move.b #(LoadInvincibility2_Index-LoadShield_Index),obRoutine(a1) addq.w #1,d1 move.b d1,obAnim(a1) dbf d0,@loop Obj_Invincibility_Main: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? bne.s Obj_Invincibility_CopyPos ; if yes, branch move.b obSaveShield(a0),obLoadShield(a0) bra.w Obj_Shield_Destroy3 ; --------------------------------------------------------------------------- Obj_Invincibility_Main2: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? beq.w Obj_LightningShield_Spark_Delete ; if not, branch Obj_Invincibility_CopyPos: move.w (v_trackpos).w,d0 move.b obAnim(a0),d1 lsl.b #3,d1 move.b d1,d2 add.b d1,d1 add.b d2,d1 addq.b #4,d1 sub.b d1,d0 move.b $37(a0),d1 sub.b d1,d0 addq.b #4,d1 cmpi.b #$18,d1 bcs.s @1 moveq #0,d1 @1 move.b d1,$37(a0) lea (v_tracksonic).w,a1 lea (a1,d0.w),a1 move.w (a1)+,obX(a0) move.w (a1)+,obY(a0) move.b (v_player+obStatus).w,obStatus(a0) lea (Ani_Invincibility).l,a1 jsr (AnimateSprite).l jmp (DisplaySprite).l ; =============== S U B R O U T I N E ======================================= Obj_BlueShield_Main: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? bne.w LoadShield_Locret ; If so, return btst #Status_Shield,status_secondary(a2) ; Should the player still have a shield? beq.w Obj_Shield_Destroy ; If not, change to Insta-Shield move.w obX(a2),obX(a0) move.w obY(a2),obY(a0) move.b obStatus(a2),obStatus(a0) ; Inherit status andi.b #1,obStatus(a0) ; Limit inheritance to 'orientation' bit andi.w #$7FFF,obGfx(a0) tst.w obGfx(a2) bpl.s @1 ori.w #$8000,obGfx(a0) @1 lea (Ani_BlueShield).l,a1 jsr (AnimateSprite).l bsr.w PLCLoad_Shields jmp (DisplaySprite).l ; =============== S U B R O U T I N E ======================================= Obj_FireShield_Main: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? bne.w LoadShield_Locret ; If so, return cmpi.b #$1C,obAnim(a2) ; Is player in their 'blank' animation? beq.w LoadShield_Locret ; If so, do not display and do not update variables btst #Status_Shield,status_secondary(a2) ; Should the player still have a shield? beq.s Obj_Shield_Destroy ; If not, change to Insta-Shield btst #Status_Underwater,obStatus(a2) ; Is player underwater? bne.s Obj_FireShield_DestroyUnderwater ; If so, branch move.w obX(a2),obX(a0) move.w obY(a2),obY(a0) tst.b obAnim(a0) ; Is shield in its 'dashing' state? bne.s @1 ; If so, do not update orientation or allow changing of the priority obGfx bit move.b obStatus(a2),obStatus(a0) ; Inherit status andi.b #1,obStatus(a0) ; Limit inheritance to 'orientation' bit andi.w #$7FFF,obGfx(a0) tst.w obGfx(a2) bpl.s @1 ori.w #$8000,obGfx(a0) @1 lea (Ani_FireShield).l,a1 jsr (AnimateSprite).l move.b #1,obPriority(a0) ; Layer shield over player sprite cmpi.b #$F,obFrame(a0) ; Are these the frames that display in front of the player? blo.s @2 ; If so, branch move.b #4,obPriority(a0) ; If not, layer shield behind player sprite @2 bsr.w PLCLoad_Shields jmp (DisplaySprite).l ; --------------------------------------------------------------------------- Obj_Shield_Destroy: andi.b #$8E,status_secondary(a2) ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0 Obj_Shield_Destroy2: clr.b obLoadShield(a0) Obj_Shield_Destroy3: clr.b obRoutine(a0) Obj_FireShield_Destroy_Locret: rts ; --------------------------------------------------------------------------- Obj_FireShield_DestroyUnderwater: andi.b #$8E,status_secondary(a2) ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0 jsr (SingleObjLoad).l ; Set up for a new object bne.s @1 ; If that can't happen, branch move.b #obSlot_Shields,(a1) ; Create dissipate object move.b #(LoadFireShield_Dissipate_Index-LoadShield_Index),obRoutine(a1) move.w obX(a0),obX(a1) ; Put it at shields' x_pos move.w obY(a0),obY(a1) ; Put it at shields' y_pos @1 bra.s Obj_Shield_Destroy ; =============== S U B R O U T I N E ======================================= Obj_LightningShield_LoadArt: addq.b #2,obRoutine(a0) move.l #ArtUnc_Obj_Lightning_Shield_Sparks,d1 ; Load art source move.w #obVRAM_LightningShields_Spark,d2 ; Load art destination move.w #(ArtUnc_Obj_Lightning_Shield_Sparks_end-ArtUnc_Obj_Lightning_Shield_Sparks)/2,d3 ; Size of art (in words) jsr (QueueDMATransfer).l Obj_LightningShield_Main: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? bne.s Obj_FireShield_Destroy_Locret ; If so, return cmpi.b #$1C,obAnim(a2) ; Is player in their 'blank' animation? beq.s Obj_FireShield_Destroy_Locret ; If so, do not display and do not update variables btst #Status_Shield,status_secondary(a2) ; Should the player still have a shield? beq.s Obj_Shield_Destroy ; If not, change to Insta-Shield btst #Status_Underwater,obStatus(a2) ; Is player underwater? bne.s Obj_LightningShield_DestroyUnderwater ; If so, branch move.w obX(a2),obX(a0) move.w obY(a2),obY(a0) move.b obStatus(a2),obStatus(a0) ; Inherit status andi.b #1,obStatus(a0) ; Limit inheritance to 'orientation' bit andi.w #$7FFF,obGfx(a0) tst.w obGfx(a2) bpl.s @1 ori.w #$8000,obGfx(a0) @1 tst.b obAnim(a0) ; Is shield in its 'double jump' state? beq.s @2 ; Is not, branch and display bsr.w Obj_LightningShield_Create_Spark ; Create sparks clr.b obAnim(a0) ; Once done, return to non-'double jump' state @2 lea (Ani_LightningShield).l,a1 jsr (AnimateSprite).l move.b #1,obPriority(a0) ; Layer shield over player sprite cmpi.b #$E,obFrame(a0) ; Are these the frames that display in front of the player? blo.s @3 ; If so, branch move.b #4,obPriority(a0) ; If not, layer shield behind player sprite @3 bsr.w PLCLoad_Shields jmp (DisplaySprite).l ; =============== S U B R O U T I N E ======================================= Obj_LightningShield_DestroyUnderwater: addq.b #2,obRoutine(a0) andi.b #$8E,status_secondary(a2) ; Sets Status_Shield, Status_FireShield, Status_LtngShield, and Status_BublShield to 0 ; Flashes the underwater palette white lea (v_pal_water).w,a1 lea (v_pal_water_dup).w,a2 move.w #($80/4)-1,d0 ; Size of Water_palette/4-1 @loop move.l (a1),(a2)+ ; Backup palette entries move.l #$0EEE0EEE,(a1)+ ; Overwrite palette entries with white dbf d0,@loop ; Loop until entire thing is overwritten move.w #0,-64(a1) ; Set the first colour in the third palette line to black move.b #3,obAniFrame(a0) rts ; --------------------------------------------------------------------------- Obj_LightningShield_DestroyUnderwater2: subq.b #1,obAniFrame(a0) ; Is it time to end the white flash? bpl.s locret_198BC ; If not, return bsr.w Obj_Shield_Destroy2 ; Replace the Lightning Shield with the Insta-Shield lea (v_pal_water_dup).w,a1 lea (v_pal_water).w,a2 move.w #($80/4)-1,d0 ; Size of Water_palette/4-1 @loop move.l (a1)+,(a2)+ ; Restore backed-up underwater palette dbf d0,@loop ; Loop until entire thing is restored locret_198BC: rts ; --------------------------------------------------------------------------- SparkVelocities: dc.w -$200, -$200 dc.w $200, -$200 dc.w -$200, $200 dc.w $200, $200 ; =============== S U B R O U T I N E ======================================= Obj_LightningShield_Create_Spark: lea SparkVelocities(pc),a2 moveq #4-1,d1 @loop jsr (SingleObjLoad).l ; Find free object slot bne.s Obj_LightningShield_Create_Spark_Locret ; If one can't be found, return move.b #obSlot_Shields,(a1) ; Make new object a Spark move.b #(LoadLightningShield_Spark_Index-LoadShield_Index),obRoutine(a1) move.w obX(a0),obX(a1) ; (Spark) Inherit obX from source object (Lightning Shield, Hyper Sonic Stars) move.w obY(a0),obY(a1) ; (Spark) Inherit obY from source object (Lightning Shield, Hyper Sonic Stars) move.l obMap(a0),obMap(a1) ; (Spark) Inherit obMap from source object (Lightning Shield, Hyper Sonic Stars) move.w obGfx(a0),obGfx(a1) ; (Spark) Inherit obGfx from source object (Lightning Shield, Hyper Sonic Stars) move.b #4,obRender(a1) move.b #1,obPriority(a1) move.b #8,obWidth(a1) move.b #8,obActWid(a1) move.b #8,obHeight(a1) move.b #1,obAnim(a1) move.l (a2)+,obVelX(a1) ; (Spark) Give xy_vel (unique to each of the four Sparks) dbf d1,@loop Obj_LightningShield_Create_Spark_Locret: rts ; =============== S U B R O U T I N E ======================================= Obj_LightningShield_Spark: jsr (SpeedToPos).l addi.w #$18,obVelY(a0) lea (Ani_LightningShield).l,a1 jsr (AnimateSprite).l jmp (DisplaySprite).l ; --------------------------------------------------------------------------- Obj_LightningShield_Spark_Delete: jmp (DeleteObject).l ; =============== S U B R O U T I N E ======================================= Obj_BubbleShield_ResetAir: addq.b #2,obRoutine(a0) jsr (ResumeMusic).l ; Reset air remaining Obj_BubbleShield_Main: btst #Status_Invincible,status_secondary(a2) ; first, does Sonic have invincibility? bne.s Obj_LightningShield_Create_Spark_Locret ; If so, return cmpi.b #$1C,obAnim(a2) ; Is player in their 'blank' animation? beq.s Obj_LightningShield_Create_Spark_Locret ; If so, do not display and do not update variables btst #Status_Shield,status_secondary(a2) ; Should the player still have a shield? beq.w Obj_Shield_Destroy ; If not, change to Insta-Shield move.w obX(a2),obX(a0) move.w obY(a2),obY(a0) move.b obStatus(a2),obStatus(a0) ; Inherit status andi.b #1,obStatus(a0) ; Limit inheritance to 'orientation' bit andi.w #$7FFF,obGfx(a0) tst.w obGfx(a2) bpl.s @1 ori.w #$8000,obGfx(a0) @1 lea (Ani_BubbleShield).l,a1 jsr (AnimateSprite).l bsr.s PLCLoad_Shields jmp (DisplaySprite).l ; =============== S U B R O U T I N E ======================================= Obj_FireShield_Dissipate: addq.b #2,obRoutine(a0) move.l #Map_obj27,obMap(a0) move.w #$5A0,obGfx(a0) move.b #4,obRender(a0) move.b #5,obPriority(a0) move.b #$C,obHeight(a0) move.b #$C,obWidth(a0) move.b #$C,obActWid(a0) move.b #3,obTimeFrame(a0) move.b #1,obFrame(a0) Obj_FireShield_Dissipate_Main: jsr (SpeedToPos).l subq.b #1,obTimeFrame(a0) bpl.s @1 move.b #3,obTimeFrame(a0) addq.b #1,obFrame(a0) cmpi.b #5,obFrame(a0) beq.w Obj_LightningShield_Spark_Delete @1 jmp (DisplaySprite).l ; =============== S U B R O U T I N E ======================================= PLCLoad_Shields: moveq #0,d0 move.b obFrame(a0),d0 cmp.b obLastLoadedDPLC(a0),d0 beq.s PLCLoad_Shields_Locret move.b d0,obLastLoadedDPLC(a0) movea.l obDPLC_Address(a0),a2 add.w d0,d0 adda.w (a2,d0.w),a2 move.w (a2)+,d5 subq.w #1,d5 bmi.s PLCLoad_Shields_Locret move.w obVRAM_Art(a0),d4 PLCLoad_Shields_ReadEntry: moveq #0,d1 move.w (a2)+,d1 move.w d1,d3 lsr.w #8,d3 andi.w #$F0,d3 addi.w #$10,d3 andi.w #$FFF,d1 lsl.l #5,d1 add.l obArt_Address(a0),d1 move.w d4,d2 add.w d3,d4 add.w d3,d4 jsr (QueueDMATransfer).l dbf d5,PLCLoad_Shields_ReadEntry PLCLoad_Shields_Locret: rts ; =============== S U B R O U T I N E ======================================= Reset_Player_Position_Array: lea (v_tracksonic).w,a1 move.w #$3F,d0 @1 move.w obX(a0),(a1)+ move.w obY(a0),(a1)+ dbf d0,@1 move.w #0,(v_trackpos).w rts