• entries
    104
  • comments
    101
  • views
    253628

A C64 Game - Step 53

Sign in to follow this  
Endurion

1810 views

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

step53.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 #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
Sign in to follow this  


2 Comments


Recommended Comments

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

Share this comment


Link to comment
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 ;)

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now