Jump to content

  • Log In with Google      Sign In   
  • Create Account






A C64 Game - Step 53

Posted by Endurion, in C64 26 May 2012 · 932 views

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

Attached Image

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 #0
		  bne .NoHitBack
		
		  ;make vulnerable again
		  lda SPRITE_STATE,x
		  cmp #128
		  bne .NoHitBack
		
		  lda #0
		  sta SPRITE_STATE,x
	  
.NoHitBack	  
		  lda DELAYED_GENERIC_COUNTER
		  and #$03
		  bne .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 #1
		  beq .AttackMode
		  rts
		
.AttackMode		
		  ;Attack modes (more modes?)
		  inc SPRITE_MOVE_POS,x
		  lda SPRITE_MOVE_POS,x
		  cmp #4
		  beq .NextAttackStep
		  rts
		
.NextAttackStep
		  lda #0
		  sta SPRITE_MOVE_POS,x
		  inc SPRITE_MODE_POS,x
		
		  lda SPRITE_MODE_POS,x
		  cmp #11
		  bcc .BeamNotDangerous
		  cmp #29
		  bcs .BeamNotDangerous
		
		  ;does player hit beam?
		  ldy #0
		  jsr CheckIsPlayerCollidingWithBeam
		  ldy #1
		  jsr CheckIsPlayerCollidingWithBeam
		
.BeamNotDangerous		
		  lda SPRITE_MODE_POS,x
		  cmp #11
		  beq .BeamStep1
		  cmp #12
		  beq .BeamStep2
		  cmp #13
		  beq .BeamStep3
		  cmp #16
		  beq .BeamStep4
		  cmp #17
		  beq .BeamStep3
		  cmp #18
		  beq .BeamStep4
		  cmp #19
		  beq .BeamStep3
		  cmp #20
		  beq .BeamStep4
		  cmp #21
		  beq .BeamStep3
		  cmp #22
		  beq .BeamStep4
		  cmp #23
		  beq .BeamStep3
		  cmp #24
		  beq .BeamStep4
		  cmp #25
		  beq .BeamStep3
		  cmp #26
		  beq .BeamStep4
		  cmp #27
		  beq .BeamStep3
		  cmp #28
		  beq .BeamStep4
		  cmp #29
		  beq .BeamStep3
		  cmp #30
		  beq .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 #39
		  bne .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 #22
		  bne .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 #39
		  bne -
		  ;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 #22
		  bne .NextLineR
		  ldx PARAM6
		  rts
		
.FollowPlayer		
		  inc SPRITE_ANIM_DELAY,x
		  lda SPRITE_ANIM_DELAY,x
		  cmp #10
		  beq .DoCheckMove
		  jmp .DoGhostMove
.DoCheckMove
		  lda #0
		  sta SPRITE_ANIM_DELAY,x
		
		  txa
		  and #$01
		  tay
		  lda SPRITE_ACTIVE,y
		  cmp #TYPE_PLAYER_DEAN
		  beq .FoundPlayer
		  cmp #TYPE_PLAYER_SAM
		  beq .FoundPlayer
		
		  ;check other player
		  tya
		  eor #1
		  tay
		  lda SPRITE_ACTIVE,y
		  cmp #TYPE_PLAYER_DEAN
		  beq .FoundPlayer
		  cmp #TYPE_PLAYER_SAM
		  beq .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_SPEED
		  beq .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_SPEED
		  beq .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_SPEED
		  beq .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_SPEED
		  beq .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 #128
bcs .PlayerNotActive

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

clc
adc #1
cmp SPRITE_CHAR_POS_X,y
beq .PlayerHit

sec
sbc #2
cmp 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 #1
cmp SPRITE_CHAR_POS_Y,y
beq .PlayerHit

sec
sbc #2
cmp 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_SAM
bne .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.

Previous Step Next Step

Attached Files






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?)
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 ;)

July 2014 »

S M T W T F S
  12345
678 9 101112
13141516171819
20212223242526
2728293031  

Recent Entries

Recent Comments

PARTNERS