And here's boss #3. Good luck!
A note to the latter code additions: If you wonder about the +(+) and -(-) labels, they are relative labels. They basically work like this: If a jmp or other reference is encountered, the compiler looks forward (+) or backward(-) for the first matching location. The +/- labels can be reused.
This avoids all annoying loop23, otherloop14b, etc. labels.
This boss has a vertical beam again, but this time only downwards. So here's a specialised check routine:
;------------------------------------------------------------
;check player vs. beam
; beam boss index in x
; player index in y
; PARAM5 = beam start Y
;------------------------------------------------------------
!zone CheckIsPlayerCollidingWithBeamV
CheckIsPlayerCollidingWithBeamV
lda SPRITE_ACTIVE,y
bne .PlayerIsActive
.PlayerNotActive
rts
.PlayerIsActivecmp #TYPE_PLAYER_DEANbeq +
cmp #TYPE_PLAYER_SAMbeq +
rts
+
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 .XMatch
clc
adc #1cmp SPRITE_CHAR_POS_X,y
beq .XMatch
sec
sbc #2cmp SPRITE_CHAR_POS_X,y
beq .XMatch
;not hit
rts
.XMatch;compare char positions in y
lda SPRITE_CHAR_POS_Y,x
cmp SPRITE_CHAR_POS_Y,y
bmi .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
Code reuse and refactoring from the first boss gives us a routine to draw a vertical beam:
!zone DrawBeamV
DrawBeamV
ldy PARAM4
ldx PARAM5
.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 + 1clcadc #( ( 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
And the rather complicated behaviour. The boss has two major states. One, where it flies horizontally and tries to home in on a player, and second, once a player has been seen, shoot downwards.
;------------------------------------------------------------
;boss 3
;state 1 = shoot
;state 128 = follow player
;state 129 = shoot, hitback active
;------------------------------------------------------------
!zone BehaviourBoss3
BehaviourBoss3
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 #129bne .NoHitBack
lda #1
sta SPRITE_STATE,x
.NoHitBack
lda SPRITE_STATE,x
and #$7f
beq .FollowPlayer
;shoot state
jmp .ShootDown
.FollowPlayer;above player?
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
bne +
;enter attack mode
lda #1
sta SPRITE_STATE,x
lda #0
sta SPRITE_MODE_POS,x
rts
+
lda DELAYED_GENERIC_COUNTER
and #$01beq +
rts
+
;y swing
inc SPRITE_MOVE_POS_Y,x
lda SPRITE_MOVE_POS_Y,x
and #15
sta SPRITE_MOVE_POS_Y,x
ldy SPRITE_MOVE_POS_Y,x
lda PATH_DY,y
beq .NoYMoveNeeded
sta PARAM1
and #$80beq .MoveDown
;move up
lda PARAM1
and #$7f
sta PARAM1
.MoveUp
jsr ObjectMoveUp
dec PARAM1
bne .MoveUp
jmp BossFollowPlayerX
.MoveDown
jsr ObjectMoveDown
dec PARAM1
bne .MoveDown
.NoYMoveNeeded
jmp BossFollowPlayerX
.ShootDown;Attack modes (more modes?)
inc SPRITE_ANNOYED,x
lda SPRITE_ANNOYED,x
cmp #4beq .NextAttackStep
rts
.NextAttackStep
lda #0
sta SPRITE_ANNOYED,x
lda SPRITE_CHAR_POS_X,x
sta PARAM4
lda SPRITE_CHAR_POS_Y,x
sta PARAM5
inc SPRITE_MODE_POS,x
lda SPRITE_MODE_POS,x
cmp #11bcc .BeamNotDangerous
cmp #29bcs .BeamNotDangerous
;does player hit beam?
ldy #0
jsr CheckIsPlayerCollidingWithBeamV
ldy #1
jsr CheckIsPlayerCollidingWithBeamV
.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
.HandleBeam
lda BEAM_CHAR_H,y
sta PARAM1
lda BEAM_CHAR_V,y
sta PARAM2
lda BEAM_COLOR,y
sta PARAM3
txa
pha
jsr DrawBeamV
pla
tax
rts
.BeamStep1;beam
ldy #BEAM_TYPE_DARK
jmp .HandleBeam
.BeamStep2;beam
ldy #BEAM_TYPE_MEDIUM
jmp .HandleBeam
.BeamStep3;beam
ldy #BEAM_TYPE_LIGHT
jmp .HandleBeam
.BeamStep4;beam
ldy #BEAM_TYPE_LIGHT2
jmp .HandleBeam
.BeamEnd
jsr RestoreBeamHV
lda #128
sta SPRITE_STATE,x
rts
Plus the usual table additions which I don't list here.
Have fun!
Keep up the great work!