And here's the next boss. A striking similarity to the first boss, and yet a little bit different.
The main behaviour of this boss is the same as the previous, only the beam is diagonal this time.
Making a diagonal beam is somewhat more difficult, as you need to check for both playfield borders. The beams are started from the boss position and then all 4 directions are tackled after each other.
;------------------------------------------------------------
;boss
;------------------------------------------------------------
!zone BehaviourBoss2
BehaviourBoss2
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 BossFollowPlayer
.NotFollowPlayercmp #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 CheckIsPlayerCollidingWithDiagonalBeam
ldy #1
jsr CheckIsPlayerCollidingWithDiagonalBeam
.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 .DrawBeamDiagonal
rts
.BeamStep2;beam
lda #BEAM_TYPE_MEDIUM
jsr .DrawBeamDiagonal
rts
.BeamStep3;beam
lda #BEAM_TYPE_LIGHT
jsr .DrawBeamDiagonal
rts
.BeamStep4;beam
lda #BEAM_TYPE_LIGHT2
jsr .DrawBeamDiagonal
rts
.BeamEnd
jsr RestoreBeamDiagonal
lda #0
sta SPRITE_STATE,x
rts
.DrawBeamDiagonal
tay
lda BEAM_CHAR_NWSE,y
sta PARAM1
lda BEAM_CHAR_NESW,y
sta PARAM2
lda BEAM_COLOR,y
sta PARAM3
ldy SPRITE_CHAR_POS_Y,x
sty PARAM9
stx PARAM6
lda SPRITE_CHAR_POS_X,x
sta PARAM7
sta PARAM8
.NextUpperLine
ldy PARAM9
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
;upper part;left
ldy PARAM7
beq .NoLeftPart
lda PARAM1
sta (ZEROPAGE_POINTER_1),y
lda PARAM3
sta (ZEROPAGE_POINTER_2),y
.NoLeftPart;right
ldy PARAM8
beq .NoRightPart
lda PARAM2
sta (ZEROPAGE_POINTER_1),y
lda PARAM3
sta (ZEROPAGE_POINTER_2),y
.NoRightPart
dec PARAM9
beq .UpperPartDone
;left border reached?
lda PARAM7
beq .LeftDone
dec PARAM7
.LeftDone
lda PARAM8
beq .RightEndReached
cmp #38beq .RightEndReached
inc PARAM8
jmp .NextUpperLine
.RightEndReached
lda #0
sta PARAM8
jmp .NextUpperLine
.UpperPartDone
;lower part
ldy SPRITE_CHAR_POS_Y,x
sty PARAM9
stx PARAM6
lda SPRITE_CHAR_POS_X,x
sta PARAM7
sta PARAM8
.NextUpperLineBottom
ldy PARAM9
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
;upper part;left
ldy PARAM7
beq .NoLeftPartBottom
lda PARAM2
sta (ZEROPAGE_POINTER_1),y
lda PARAM3
sta (ZEROPAGE_POINTER_2),y
.NoLeftPartBottom;right
ldy PARAM8
beq .NoRightPartBottom
lda PARAM1
sta (ZEROPAGE_POINTER_1),y
lda PARAM3
sta (ZEROPAGE_POINTER_2),y
.NoRightPartBottom
inc PARAM9
lda PARAM9
cmp #22beq .LowerPartDone
;left border reached?
lda PARAM7
beq .LeftDoneBottom
dec PARAM7
.LeftDoneBottom
lda PARAM8
beq .RightEndReachedBottom
cmp #38beq .RightEndReachedBottom
inc PARAM8
jmp .NextUpperLineBottom
.RightEndReachedBottom
lda #0
sta PARAM8
jmp .NextUpperLineBottom
.LowerPartDone
ldx PARAM6
rts
Obviously all the same is repeated for the removal of the beam.
!zone RestoreBeamDiagonal
RestoreBeamDiagonal
ldy SPRITE_CHAR_POS_Y,x
stx PARAM6
ldy SPRITE_CHAR_POS_Y,x
sty PARAM9
stx PARAM6
lda SPRITE_CHAR_POS_X,x
sta PARAM7
sta PARAM8
.NextUpperLine
ldy PARAM9
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 + 1clcadc #( ( SCREEN_COLOR - SCREEN_BACK_CHAR ) >> 8 )
sta ZEROPAGE_POINTER_3 + 1
sec
sbc #( ( SCREEN_COLOR - SCREEN_BACK_COLOR ) >> 8 )
sta ZEROPAGE_POINTER_4 + 1
;upper part;left
ldy PARAM7
beq .NoLeftPart
lda (ZEROPAGE_POINTER_2),y
sta (ZEROPAGE_POINTER_1),y
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_3),y
.NoLeftPart;right
ldy PARAM8
beq .NoRightPart
lda (ZEROPAGE_POINTER_2),y
sta (ZEROPAGE_POINTER_1),y
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_3),y
.NoRightPartdec PARAM9
beq .UpperPartDone
;left border reached?
lda PARAM7
beq .LeftDone
dec PARAM7
.LeftDone
lda PARAM8
beq .RightEndReached
cmp #38
beq .RightEndReached
inc PARAM8
jmp .NextUpperLine
.RightEndReached
lda #0
sta PARAM8
jmp .NextUpperLine
.UpperPartDone
;lower part
ldy SPRITE_CHAR_POS_Y,x
sty PARAM9
stx PARAM6
lda SPRITE_CHAR_POS_X,x
sta PARAM7
sta PARAM8
.NextUpperLineBottom
ldy PARAM9
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 + 1clcadc #( ( SCREEN_COLOR - SCREEN_BACK_CHAR ) >> 8 )
sta ZEROPAGE_POINTER_3 + 1
sec
sbc #( ( SCREEN_COLOR - SCREEN_BACK_COLOR ) >> 8 )
sta ZEROPAGE_POINTER_4 + 1
;upper part;left
ldy PARAM7
beq .NoLeftPartBottom
lda (ZEROPAGE_POINTER_2),y
sta (ZEROPAGE_POINTER_1),y
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_3),y
.NoLeftPartBottom;right
ldy PARAM8
beq .NoRightPartBottom
lda (ZEROPAGE_POINTER_2),y
sta (ZEROPAGE_POINTER_1),y
lda (ZEROPAGE_POINTER_4),y
sta (ZEROPAGE_POINTER_3),y
.NoRightPartBottominc PARAM9
lda PARAM9
cmp #22
beq .LowerPartDone
;left border reached?
lda PARAM7
beq .LeftDoneBottom
dec PARAM7
.LeftDoneBottom
lda PARAM8
beq .RightEndReachedBottom
cmp #38
beq .RightEndReachedBottom
inc PARAM8
jmp .NextUpperLineBottom
.RightEndReachedBottom
lda #0
sta PARAM8
jmp .NextUpperLineBottom
.LowerPartDone
ldx PARAM6
rts
Checking if the player is hit by the beam as a bit different as well. The previous boss beam was checked simply by comparing X and Y pos. Now we take the distances between X and Y pos and compare them. They must be either equal or only differ by one to count as colliding.
;------------------------------------------------------------
;check player vs. diagonal beam
; beam boss index in x
; player index in y
;------------------------------------------------------------
!zone CheckIsPlayerCollidingWithDiagonalBeam
CheckIsPlayerCollidingWithDiagonalBeam
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
sec
sbc SPRITE_CHAR_POS_X,y
bpl .PositiveX
lda SPRITE_CHAR_POS_X,y
sec
sbc SPRITE_CHAR_POS_X,x
.PositiveX
sta PARAM1
lda SPRITE_CHAR_POS_Y,x
sec
sbc SPRITE_CHAR_POS_Y,y
bpl .PositiveY
lda SPRITE_CHAR_POS_Y,y
sec
sbc SPRITE_CHAR_POS_Y,x
.PositiveY
sta PARAM2
lda PARAM1
cmp PARAM2
beq .PlayerHit
lda PARAM1
sec
sbc PARAM2
and #$7f
cmp #1beq .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
Play the first boss, then stumble upon this one. It's not really difficult as well, but annoying to get the habit of moving out of the way for the first boss out of your mind
Have fun!