A C64 Game - Step 53

posted in New Old Things
Published May 26, 2012
Advertisement

Here comes our first boss. Nothing too difficult, but different. Let's see what this works out to smile.png



The boss moves quite similar to the ghost however he's got a special attack. If nothing else happens the boss is homing in on you. If you shoot him two times he goes into attack mode (and you better step back).

For performance reason the beams are made of characters, a horizontal and vertical line at the boss position.

;------------------------------------------------------------
;boss
;------------------------------------------------------------
!zone BehaviourBoss
BehaviourBoss
  
BOSS_MOVE_SPEED = 1
          lda SPRITE_HITBACK,x
          beq .NoHitBack
          dec SPRITE_HITBACK,x

          ldy SPRITE_HITBACK,x
          lda BOSS_FLASH_TABLE,y
          sta VIC_SPRITE_COLOR,x

          cpy #0bne .NoHitBack

          ;make vulnerable again
          lda SPRITE_STATE,x
          cmp #128bne .NoHitBack

          lda #0
          sta SPRITE_STATE,x

.NoHitBack  
          lda DELAYED_GENERIC_COUNTER
          and #$03bne .NoAnimUpdate

          lda SPRITE_POINTER_BASE,x
          eor #$01
          sta SPRITE_POINTER_BASE,x

.NoAnimUpdate 
          lda SPRITE_STATE,x
          and #$7f
          bne .NotFollowPlayer
          jmp .FollowPlayer

.NotFollowPlayer  
          cmp #1beq .AttackMode
          rts

.AttackMode 
          ;Attack modes (more modes?)
          inc SPRITE_MOVE_POS,x
          lda SPRITE_MOVE_POS,x
          cmp #4beq .NextAttackStep
          rts

.NextAttackStep
          lda #0
          sta SPRITE_MOVE_POS,x
          inc SPRITE_MODE_POS,x

          lda SPRITE_MODE_POS,x
          cmp #11bcc .BeamNotDangerous
          cmp #29bcs .BeamNotDangerous
;does player hit beam?
          ldy #0
          jsr CheckIsPlayerCollidingWithBeam
          ldy #1
          jsr CheckIsPlayerCollidingWithBeam

.BeamNotDangerous 
          lda SPRITE_MODE_POS,x
          cmp #11beq .BeamStep1
          cmp #12beq .BeamStep2
          cmp #13beq .BeamStep3
          cmp #16beq .BeamStep4
          cmp #17beq .BeamStep3
          cmp #18beq .BeamStep4
          cmp #19beq .BeamStep3
          cmp #20beq .BeamStep4
          cmp #21beq .BeamStep3
          cmp #22beq .BeamStep4
          cmp #23beq .BeamStep3
          cmp #24beq .BeamStep4
          cmp #25beq .BeamStep3
          cmp #26beq .BeamStep4
          cmp #27beq .BeamStep3
          cmp #28beq .BeamStep4
          cmp #29beq .BeamStep3
          cmp #30beq .BeamEnd
          rts

.BeamStep1;beam
          lda #BEAM_TYPE_DARK
          jsr .DrawBeam
          rts

.BeamStep2;beam
          lda #BEAM_TYPE_MEDIUM
          jsr .DrawBeam
          rts

.BeamStep3;beam
          lda #BEAM_TYPE_LIGHT
          jsr .DrawBeam
          rts

.BeamStep4;beam
          lda #BEAM_TYPE_LIGHT2
          jsr .DrawBeam
          rts

.BeamEnd
          jsr .RestoreBeam

          lda #0
          sta SPRITE_STATE,x
          rts

.DrawBeam
          tay
          lda BEAM_CHAR_H,y
          sta PARAM1
          lda BEAM_CHAR_V,y
          sta PARAM2
          lda BEAM_COLOR,y
          sta PARAM3

          ldy SPRITE_CHAR_POS_Y,x

          lda SCREEN_LINE_OFFSET_TABLE_LO,y
          sta ZEROPAGE_POINTER_1
          sta ZEROPAGE_POINTER_2
          lda SCREEN_LINE_OFFSET_TABLE_HI,y
          sta ZEROPAGE_POINTER_1 + 1
          clc
          adc #( ( SCREEN_COLOR - SCREEN_CHAR ) >> 8 )
          sta ZEROPAGE_POINTER_2 + 1

          stx PARAM6

          ldy #1
.HLoop  
          lda PARAM1
          sta (ZEROPAGE_POINTER_1),y
          lda PARAM3
          sta (ZEROPAGE_POINTER_2),y
          iny
          cpy #39bne .HLoop
          ;vertical beam
          ldy SPRITE_CHAR_POS_X,x
          ldx #1

.NextLine 
          lda SCREEN_LINE_OFFSET_TABLE_LO,x
          sta ZEROPAGE_POINTER_1
          sta ZEROPAGE_POINTER_2
          lda SCREEN_LINE_OFFSET_TABLE_HI,x
          sta ZEROPAGE_POINTER_1 + 1
          clc
          adc #( ( SCREEN_COLOR - SCREEN_CHAR ) >> 8 )
          sta ZEROPAGE_POINTER_2 + 1

          lda PARAM2
          sta (ZEROPAGE_POINTER_1),y
          lda PARAM3
          sta (ZEROPAGE_POINTER_2),y

          inx
          cpx #22bne .NextLine
          ldx PARAM6
          rts

.RestoreBeam
          ldy SPRITE_CHAR_POS_Y,x

          lda SCREEN_LINE_OFFSET_TABLE_LO,y
          sta ZEROPAGE_POINTER_1
          sta ZEROPAGE_POINTER_2
          sta ZEROPAGE_POINTER_3
          sta ZEROPAGE_POINTER_4
          lda SCREEN_LINE_OFFSET_TABLE_HI,y
          sta ZEROPAGE_POINTER_1 + 1
          sec
          sbc #( ( SCREEN_CHAR - SCREEN_BACK_CHAR ) >> 8 )
          sta ZEROPAGE_POINTER_2 + 1
          clc
          adc #( ( SCREEN_COLOR - SCREEN_BACK_CHAR ) >> 8 )
          sta ZEROPAGE_POINTER_3 + 1
          sec
          sbc #( ( SCREEN_COLOR - SCREEN_BACK_COLOR ) >> 8 )
          sta ZEROPAGE_POINTER_4 + 1

          stx PARAM6

          ldy #1

-
          lda (ZEROPAGE_POINTER_2),y
          sta (ZEROPAGE_POINTER_1),y
          lda (ZEROPAGE_POINTER_4),y
          sta (ZEROPAGE_POINTER_3),y
          iny
          cpy #39bne -
          ;vertical beam
          ldy SPRITE_CHAR_POS_X,x
          ldx #1

.NextLineR  
          lda SCREEN_LINE_OFFSET_TABLE_LO,x
          sta ZEROPAGE_POINTER_1
          sta ZEROPAGE_POINTER_2
          sta ZEROPAGE_POINTER_3
          sta ZEROPAGE_POINTER_4
          lda SCREEN_LINE_OFFSET_TABLE_HI,x
          sta ZEROPAGE_POINTER_1 + 1
          clc
          adc #( ( SCREEN_BACK_CHAR - SCREEN_CHAR ) >> 8 )
          sta ZEROPAGE_POINTER_2 + 1
          clc
          adc #( ( SCREEN_COLOR - SCREEN_BACK_CHAR ) >> 8 )
          sta ZEROPAGE_POINTER_3 + 1
          sec
          sbc #( ( SCREEN_COLOR - SCREEN_BACK_COLOR ) >> 8 )
          sta ZEROPAGE_POINTER_4 + 1
          lda (ZEROPAGE_POINTER_2),y
          sta (ZEROPAGE_POINTER_1),y
          lda (ZEROPAGE_POINTER_4),y
          sta (ZEROPAGE_POINTER_3),y

          inx
          cpx #22bne .NextLineR
          ldx PARAM6
          rts

.FollowPlayer 
          inc SPRITE_ANIM_DELAY,x
          lda SPRITE_ANIM_DELAY,x
          cmp #10beq .DoCheckMove
          jmp .DoGhostMove
.DoCheckMove
          lda #0
          sta SPRITE_ANIM_DELAY,x

          txa
          and #$01
          tay
          lda SPRITE_ACTIVE,y
          cmp #TYPE_PLAYER_DEANbeq .FoundPlayer
          cmp #TYPE_PLAYER_SAMbeq .FoundPlayer

          ;check other player
          tya
          eor #1
          tay
          lda SPRITE_ACTIVE,y
          cmp #TYPE_PLAYER_DEANbeq .FoundPlayer
          cmp #TYPE_PLAYER_SAMbeq .FoundPlayer

          ;no player to hunt
          rts

.FoundPlayer;player index in y
          lda SPRITE_CHAR_POS_X,y
          cmp SPRITE_CHAR_POS_X,x
          bpl .MoveRight
;move left
          lda SPRITE_DIRECTION,x
          bne .AlreadyLookingLeft
          lda SPRITE_MOVE_POS,x
          beq .TurnLNow
          dec SPRITE_MOVE_POS,x
          bne .CheckYNow

.TurnLNow 
          ;turning now
          lda #1
          sta SPRITE_DIRECTION,x
          lda #SPRITE_BOSS_L_1
          sta SPRITE_POINTER_BASE,x
          jmp .CheckYNow

.AlreadyLookingLeft
          lda SPRITE_MOVE_POS,x
          cmp #BOSS_MOVE_SPEEDbeq .CheckYNow
          inc SPRITE_MOVE_POS,x
          jmp .CheckYNow

.MoveRight 
          lda SPRITE_DIRECTION,x
          beq .AlreadyLookingRight

          lda SPRITE_MOVE_POS,x
          beq .TurnRNow
          dec SPRITE_MOVE_POS,x
          bne .CheckYNow

          ;turning now
.TurnRNow 
          lda #0
          sta SPRITE_DIRECTION,x
          lda #SPRITE_BOSS_R_1
          sta SPRITE_POINTER_BASE,x
          jmp .CheckYNow

.AlreadyLookingRight  
          lda SPRITE_MOVE_POS,x
          cmp #BOSS_MOVE_SPEEDbeq .CheckYNow
          inc SPRITE_MOVE_POS,x
          jmp .CheckYNow

.CheckYNow;player index in y
          lda SPRITE_CHAR_POS_Y,y
          cmp SPRITE_CHAR_POS_Y,x
          bpl .MoveDown
;move left
          lda SPRITE_DIRECTION_Y,x
          bne .AlreadyLookingUp
          lda SPRITE_MOVE_POS_Y,x
          beq .TurnUNow
          dec SPRITE_MOVE_POS_Y,x
          bne .DoGhostMove

.TurnUNow 
          ;turning now
          lda #1
          sta SPRITE_DIRECTION_Y,x
          jmp .DoGhostMove

.AlreadyLookingUp
          lda SPRITE_MOVE_POS_Y,x
          cmp #BOSS_MOVE_SPEEDbeq .DoGhostMove
          inc SPRITE_MOVE_POS_Y,x
          jmp .DoGhostMove

.MoveDown
          lda SPRITE_DIRECTION_Y,x
          beq .AlreadyLookingDown

          lda SPRITE_MOVE_POS_Y,x
          beq .TurnDNow
          dec SPRITE_MOVE_POS_Y,x
          bne .DoGhostMove

          ;turning now
.TurnDNow 
          lda #0
          sta SPRITE_DIRECTION_Y,x
          jmp .DoGhostMove

.AlreadyLookingDown
          lda SPRITE_MOVE_POS_Y,x
          cmp #BOSS_MOVE_SPEEDbeq .DoGhostMove
          inc SPRITE_MOVE_POS_Y,x
          jmp .DoGhostMove
.DoGhostMove;move X times
          ldy SPRITE_MOVE_POS,x
          sty PARAM4
          beq .DoY

          lda SPRITE_DIRECTION,x
          beq .DoRight
.MoveLoopL
          jsr ObjectMoveLeftBlocking
          dec PARAM4
          bne .MoveLoopL
          jmp .DoY

.DoRight
.MoveLoopR
          jsr ObjectMoveRightBlocking
          dec PARAM4
          bne .MoveLoopR

.DoY  
          ;move X times
          ldy SPRITE_MOVE_POS_Y,x
          sty PARAM4
          beq .MoveDone

          lda SPRITE_DIRECTION_Y,x
          beq .DoDown
.MoveLoopU
          jsr ObjectMoveUpBlocking
          dec PARAM4
          bne .MoveLoopU
          jmp .MoveDone

.DoDown
.MoveLoopD
          jsr ObjectMoveDownBlockingNoPlatform
          dec PARAM4
          bne .MoveLoopD

.MoveDone 
          rts

Checking the player for collision with the beam is heavily simplified due to the beam being horizontal/vertical. It's a cheap comparison of position values:


;------------------------------------------------------------
;check player vs. beam
; beam boss index in x
; player index in y
;------------------------------------------------------------
!zone CheckIsPlayerCollidingWithBeam
CheckIsPlayerCollidingWithBeam
          lda SPRITE_ACTIVE,y
          bne .PlayerIsActive
.PlayerNotActive
          rts

.PlayerIsActive
          lda SPRITE_STATE,y
          cmp #128bcs .PlayerNotActive

          ;compare char positions in x
          lda SPRITE_CHAR_POS_X,x
          cmp SPRITE_CHAR_POS_X,y
          beq .PlayerHit

          clc
          adc #1cmp SPRITE_CHAR_POS_X,y
          beq .PlayerHit

          sec
          sbc #2cmp SPRITE_CHAR_POS_X,y
          beq .PlayerHit

          ;compare char positions in y
          lda SPRITE_CHAR_POS_Y,x
          cmp SPRITE_CHAR_POS_Y,y
          beq .PlayerHit

          clc
          adc #1cmp SPRITE_CHAR_POS_Y,y
          beq .PlayerHit

          sec
          sbc #2cmp SPRITE_CHAR_POS_Y,y
          beq .PlayerHit

          ;not hit
          rts

.PlayerHit;player killed
          lda #129
          sta SPRITE_STATE,y

          lda #SPRITE_PLAYER_DEAD
          sta SPRITE_POINTER_BASE,y

          lda #0
          sta SPRITE_MOVE_POS,y

          lda SPRITE_ACTIVE,y
          cmp #TYPE_PLAYER_SAMbne .PlayerWasDean

          ;reset Sam specific variables
          lda #0
          sta SPRITE_HELD

.PlayerWasDean
          rts

The boss only enters attack mode for every second hit, therefore he gets a special treatment in his hit function:


;------------------------------------------------------------
;hit behaviour for boss
;------------------------------------------------------------
!zone HitBehaviourBoss
HitBehaviourBoss
          lda #8
          sta SPRITE_HITBACK,x

          ;make invincible for a short while
          lda SPRITE_STATE,x
          ora #$80
          sta SPRITE_STATE,x

          ;boss switches tactic
          lda SPRITE_HP,x
          and #$01
          beq .SwitchToAttack
          rts

.SwitchToAttack
          lda #129
          sta SPRITE_STATE,x

          lda #0
          sta SPRITE_MODE_POS,x
          sta SPRITE_MOVE_POS,x
          sta SPRITE_MOVE_POS_Y,x
          rts

The less interesting parts are known, add new values for the boss object plus the color/character tables for the beam.

step53.zip

Previous Step Next Step

Previous Entry A C64 Game - Step 52
1 likes 2 comments

Comments

Sik_the_hedgehog
Wow, that's looking nice. That's a long way since last time I checked this project =O (you were just starting to implement enemies back then)

My only experience programming an 8-bit processor is with the Z80. Granted, the surrounding hardware was far from friendly (ugh!) and code was horribly time critical (processing events while also outputting PCM at 10650 Hz in the background without any sort of buffering is far from nice), but it certainly paid off.

I wonder how annoying is to program for the 6502, considering that it's add opcode always adds the carry... (no add without carry, seriously?)
May 26, 2012 07:54 AM
Endurion
Yeah, the adc thing is a bit annoying. It simply means automatically clearing the carry before calling add. They could've saved a byte per add if they added a add without carry (which at that time would've been quite useful)

It really helps having some talented people doing graphic and music ;)
June 02, 2012 05:40 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement