; ; gameSegments.s ; BuGS ; ; Created by Jeremy Rand on 2020-07-30. ;Copyright © 2020 Jeremy Rand. All rights reserved. ; case on mcopy gameSegments.macros keep gameSegments gameSegments start using globalData using tileData SEGMENT_MAX_NUM equ 12 SEGMENT_MAX_OFFSET equ SEGMENT_MAX_NUM*2-2 SEGMENT_STATE_NONE equ 0 SEGMENT_STATE_EXPLODING equ 1 SEGMENT_STATE_HEAD equ 2 SEGMENT_STATE_POISONED_HEAD equ 3 SEGMENT_STATE_BODY equ 4 SEGMENT_DIR_DOWN equ 0 SEGMENT_DIR_UP equ 1 ; You would think I would need "facing up" variants here also. When the centipede gets to the bottom of ; the screen, it starts traveling up again and when it turns, you would think it would face up. Turns out ; the real arcade system uses "facing down" sprites even when the centipede is moving up. So, I guess I ; will too. I suspect that the players tend not to notice because by the time the centipede is traveling ; up, they are pretty occupied by the fact that they are now surrounded by segments. SEGMENT_FACING_LEFT equ 0 SEGMENT_FACING_DOWN_LEFT equ 32 SEGMENT_FACING_DOWN equ 64 SEGMENT_FACING_DOWN_RIGHT equ 96 SEGMENT_FACING_RIGHT equ 128 SEGMENT_MAX_POSITION_OFFSET equ TILE_PIXEL_WIDTH*SEGMENT_MAX_NUM*2-2 segmentsInitLevel entry stz numSegments ldx #SEGMENT_MAX_OFFSET lda #SEGMENT_STATE_NONE segmentsInitLevel_loop anop sta segmentStates,x dex dex bpl segmentsInitLevel_loop rtl drawSegments entry ldx #SEGMENT_MAX_OFFSET drawSegments_nextSegment anop lda segmentStates,x bne drawSegments_cont jmp drawSegments_skipSegment drawSegments_cont anop phx cmp #SEGMENT_STATE_EXPLODING beq drawSegments_exploding cmp #SEGMENT_STATE_BODY bne drawSegments_head jsl segmentBodyJump bra drawSegments_handleTiles drawSegments_exploding anop jsl segmentExplodingJump bra drawSegments_handleTiles drawSegments_head anop jsl segmentHeadJump ; By the time we come back from segment*Jump, the X register no longer has the original 0-22 ; offset. Instead, we have a position offset which goes from 0-190. But that is good because ; we need to use that offset to update our dirty tiles. drawSegments_handleTiles anop ; We do have to handle non-game tiles. When centipede reaches the bottom, new segements are ; added randomly in from the side. That segment needs to be clipped so we need to use non-game ; tiles to do that clipping for us. _dirtyGameOrNonGameTileX segmentTileOffsetsUL _dirtyGameOrNonGameTileX segmentTileOffsetsUR _dirtyGameOrNonGameTileX segmentTileOffsetsLL _dirtyGameOrNonGameTileX segmentTileOffsetsLR ; Get our original 0-22 offset we are iterating through that we pushed earlier because we know ; it was going to get trashed. plx drawSegments_skipSegment anop dex dex bmi drawSegments_done jmp drawSegments_nextSegment drawSegments_done anop rtl segmentExplodingJump entry lda segmentPosOffset,x tax ldy segmentFacing,x lda explosionJumpTable,y sta segmentExplodingJump_jumpInst+1 lda explosionJumpTable+2,y sta segmentExplodingJump_jumpInst+3 ldy segmentScreenOffsets,x segmentExplodingJump_jumpInst anop jmp >leftHead1 nop segmentHeadJump entry lda segmentPixelOffset and segmentSpeed,x tay lda segmentPosOffset,x tax lda segmentFacing,x clc adc segmentSpriteOffset cpy #0 beq segmentHeadJump_noShift tay lda headShiftJumpTable,y sta segmentHeadJump_jumpInst+1 lda headShiftJumpTable+2,y sta segmentHeadJump_jumpInst+3 ldy segmentScreenOffsets,x bra segmentHeadJump_jumpInst segmentHeadJump_noShift anop tay lda headJumpTable,y sta segmentHeadJump_jumpInst+1 lda headJumpTable+2,y sta segmentHeadJump_jumpInst+3 ldy segmentScreenOffsets,x segmentHeadJump_jumpInst anop jmp >leftHead1 nop segmentBodyJump entry lda segmentPixelOffset and segmentSpeed,x tay lda segmentPosOffset,x tax lda segmentFacing,x clc adc segmentSpriteOffset cpy #0 beq segmentBodyJump_noShift tay lda bodyShiftJumpTable,y sta segmentBodyJump_jumpInst+1 lda bodyShiftJumpTable+2,y sta segmentBodyJump_jumpInst+3 ldy segmentScreenOffsets,x bra segmentBodyJump_jumpInst segmentBodyJump_noShift anop tay lda bodyJumpTable,y sta segmentBodyJump_jumpInst+1 lda bodyJumpTable+2,y sta segmentBodyJump_jumpInst+3 ldy segmentScreenOffsets,x segmentBodyJump_jumpInst anop jmp >leftHead1 nop updateSegments entry lda playerState cmp #PLAYER_STATE_ONSCREEN beq updateSegments_playerOnscreen ; Even when the player is offscreen, we need to update any segments which are exploding ldx #SEGMENT_MAX_OFFSET updateSegments_explodeOnlyLoop anop lda segmentStates,x cmp #SEGMENT_STATE_EXPLODING bne updateSegments_explodeOnlyNext ldy segmentPosOffset,x lda segmentFacing,y beq updateSegments_explodeOnlyDone sec sbc #$4 sta segmentFacing,y bra updateSegments_explodeOnlyNext updateSegments_explodeOnlyDone anop lda #SEGMENT_STATE_NONE sta segmentStates,x updateSegments_explodeOnlyNext anop dex dex bpl updateSegments_explodeOnlyLoop rtl updateSegments_playerOnscreen anop ; Clear the segment mask to start. stz segmentTileMask+0 stz segmentTileMask+2 stz segmentTileMask+4 stz segmentTileMask+6 stz segmentTileMask+8 stz segmentTileMask+10 stz segmentTileMask+12 stz segmentTileMask+14 stz segmentTileMask+16 stz segmentTileMask+18 stz segmentTileMask+20 stz segmentTileMask+22 stz segmentTileMask+24 stz segmentTileMask+26 stz segmentTileMask+28 stz segmentTileMask+30 stz segmentTileMask+32 stz segmentTileMask+34 stz segmentTileMask+36 stz segmentTileMask+38 stz segmentTileMask+40 stz segmentTileMask+42 stz segmentTileMask+44 stz segmentTileMask+46 stz segmentTileMask+48 stz segmentTileMask+50 stz segmentTileMask+52 stz segmentTileMask+54 stz segmentTileMask+56 stz segmentTileMask+58 stz segmentTileMask+60 stz segmentTileMask+62 stz segmentTileMask+64 stz segmentTileMask+66 stz segmentTileMask+68 stz segmentTileMask+70 stz segmentTileMask+72 stz segmentTileMask+74 stz segmentTileMask+76 stz segmentTileMask+78 lda segmentPixelOffset inc a and #TILE_PIXEL_WIDTH-1 sta segmentPixelOffset and #1 beq updateSegments_skipSpriteOffset lda segmentSpriteOffset beq updateSegments_resetSpriteOffset sec sbc #$4 bra updateSegments_spriteOffsetCont updateSegments_resetSpriteOffset anop lda #SEGMENT_SPRITE_LAST_OFFSET updateSegments_spriteOffsetCont anop sta segmentSpriteOffset updateSegments_skipSpriteOffset anop lda #SEGMENT_MAX_OFFSET+2 sta segmentEmptyOffset ldx #SEGMENT_MAX_OFFSET updateSegments_nextSegment anop lda segmentStates,x bne updateSegments_cont stx segmentEmptyOffset lda segmentPosOffset,x beq updateSegments_emptyWrapPos dec a dec a sta segmentPosOffset,x jmp updateSegments_skipSegment updateSegments_emptyWrapPos anop lda #SEGMENT_MAX_POSITION_OFFSET sta segmentPosOffset,x jmp updateSegments_skipSegment updateSegments_cont anop cmp #SEGMENT_STATE_BODY bne updateSegments_headOrExploding lda segmentPosOffset,x beq updateSegments_bodyWrapPos dec a dec a sta segmentPosOffset,x bra updateSegments_bodyMarkTile updateSegments_bodyWrapPos anop lda #SEGMENT_MAX_POSITION_OFFSET sta segmentPosOffset,x updateSegments_bodyMarkTile anop phx tay ldx segmentCurrentTile,y ldy tileBitOffset,x lda tileBitMask,x ora segmentTileMask,y sta segmentTileMask,y plx jmp updateSegments_skipSegment updateSegments_headOrExploding anop lda segmentPosOffset,x beq updateSegments_headWrapPos dec a dec a sta segmentPosOffset,x tay jmp updateSegments_headNoWrap updateSegments_headWrapPos anop lda #SEGMENT_MAX_POSITION_OFFSET sta segmentPosOffset,x tay ; Copy the 0th position entry to the SEGMENT_POSITION_OFFSET_SPARE(th) entry. ; That way, Y points to the new entry for the head and Y+2 always points to ; the previous entry, even on a wrap around. lda segmentHorizontalDir sta segmentHorizontalDir+SEGMENT_MAX_POSITION_OFFSET lda segmentVerticalDir sta segmentVerticalDir+SEGMENT_MAX_POSITION_OFFSET lda segmentScreenOffsets sta segmentScreenOffsets+SEGMENT_MAX_POSITION_OFFSET lda segmentTileOffsetsUL sta segmentTileOffsetsUL+SEGMENT_MAX_POSITION_OFFSET lda segmentTileOffsetsUR sta segmentTileOffsetsUR+SEGMENT_MAX_POSITION_OFFSET lda segmentTileOffsetsLL sta segmentTileOffsetsLL+SEGMENT_MAX_POSITION_OFFSET lda segmentTileOffsetsLR sta segmentTileOffsetsLR+SEGMENT_MAX_POSITION_OFFSET lda segmentCurrentTile sta segmentCurrentTile+SEGMENT_MAX_POSITION_OFFSET ; We could be in exploding state and if so, we want to skip the rest of the ; code for update head segments. lda segmentStates,x cmp #SEGMENT_STATE_EXPLODING bne updateSegments_notExplodingWrap jsr updateSegmentExplodingWrap bra updateSegments_skipSegment updateSegments_notExplodingWrap anop ; Important - Do facing last because we use that to index into the jump ; table for update. lda segmentFacing sta segmentFacing+SEGMENT_MAX_POSITION_OFFSET bra updateSegments_headCont updateSegments_headNoWrap anop ; Assume no change in any of the data for now and just copy it into place. lda segmentHorizontalDir+2,y sta segmentHorizontalDir,y lda segmentVerticalDir+2,y sta segmentVerticalDir,y lda segmentScreenOffsets+2,y sta segmentScreenOffsets,y lda segmentTileOffsetsUL+2,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsUR+2,y sta segmentTileOffsetsUR,y lda segmentTileOffsetsLL+2,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsLR+2,y sta segmentTileOffsetsLR,y lda segmentCurrentTile+2,y sta segmentCurrentTile,y ; We could be in exploding state and if so, we want to skip the rest of the ; code for update head segments. lda segmentStates,x cmp #SEGMENT_STATE_EXPLODING bne updateSegments_notExplodingNoWrap jsr updateSegmentExplodingNoWrap bra updateSegments_skipSegment updateSegments_notExplodingNoWrap anop ; Important - Do facing last because we use that to index into the jump ; table for update. lda segmentFacing+2,y sta segmentFacing,y updateSegments_headCont anop ; This is funky. Each "facing" value is in increments of 32 to make figuring out where ; the sprite callback is easy but it makes this update callback harder. We divide by ; 16 to get values incrementing by 2's. Then, we or in the speed. The speed is 0 or 1 ; so now we have values incrementing by 1 each with a unique pair of facing and speed. ; Then we multiply by two again so we have unique values incrementing by 2 and that is ; used as an index into the jump table. That way, we jump to a subroutine which is ; unique for each facing/speed pair. lsr a lsr a lsr a lsr a ora segmentSpeed,x asl a stx segmentBeingUpdated tax jsr (segmentUpdateJumpTable,x) ldx segmentCurrentTile,y ldy tileBitOffset,x lda tileBitMask,x ora segmentTileMask,y sta segmentTileMask,y ldx segmentBeingUpdated updateSegments_skipSegment anop dex dex bmi updateSegments_maybeAdd jmp updateSegments_nextSegment updateSegments_maybeAdd anop lda segmentsAddEnabled bne updateSegments_done lda segmentEmptyOffset cmp #SEGMENT_MAX_OFFSET+2 bge updateSegments_done lda numSegments beq updateSegments_done cmp #SEGMENT_MAX_NUM bge updateSegments_done lda segmentPixelOffset and #3 cmp #1 bne updateSegments_done jsl rand0_to_65534 and #31 bne updateSegments_done jmp updateSegmentAddSegment updateSegments_done anop rtl updateSegmentAddSegment entry inc numSegments ldx segmentEmptyOffset lda #SEGMENT_STATE_HEAD sta segmentStates,x lda #SEGMENT_SPEED_FAST sta segmentSpeed,x ldy segmentPosOffset,x lda #SEGMENT_DIR_DOWN sta segmentVerticalDir,y jsl rand0_to_65534 and #$0080 beq updateSegmentAddSegment_leftSide lda #SEGMENT_DIR_LEFT sta segmentHorizontalDir,y lda #SEGMENT_FACING_LEFT sta segmentFacing,y ldx #((GAME_NUM_TILES_WIDE*(GAME_NUM_TILES_TALL-6))-1)*SIZEOF_TILE_INFO lda tileScreenOffset,x sta segmentScreenOffsets,y txa sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y sta segmentCurrentTile,y lda tileRight,x sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y rtl updateSegmentAddSegment_leftSide anop lda #SEGMENT_DIR_RIGHT sta segmentHorizontalDir,y lda #SEGMENT_FACING_RIGHT sta segmentFacing,y ldx #(GAME_NUM_TILES_WIDE*(GAME_NUM_TILES_TALL-7))*SIZEOF_TILE_INFO lda tileScreenOffset,x sec sbc #6 sta segmentScreenOffsets,y txa sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y sta segmentCurrentTile,y lda tileLeft,x sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y rtl updateSegmentExplodingNoWrap entry lda segmentFacing+2,y bra updateSegmentExploding_common updateSegmentExplodingWrap entry lda segmentFacing updateSegmentExploding_common anop beq updateSegmentExploding_done sec sbc #$4 sta segmentFacing,y rts updateSegmentExploding_done anop lda #SEGMENT_STATE_NONE sta segmentStates,x rts updateSegmentLeftFast entry tyx dec segmentScreenOffsets,x lda segmentPixelOffset and #3 bne updateSegmentLeftFast_nextOffset lda segmentTileOffsetsUL,y sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y rts updateSegmentLeftFast_nextOffset anop cmp #1 bne updateSegmentLeftFast_nextOffset2 lda segmentTileOffsetsUL,y tax lda tileLeft,x sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y sta segmentCurrentTile,y rts updateSegmentLeftFast_nextOffset2 anop cmp #3 beq updateSegmentLeftFast_checkDir rts updateSegmentLeftFast_checkDir anop ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_POISONED_HEAD beq updateSegmentLeftFast_changeDir ; This is a important special case. When we add segments on the side, we don't want a mushroom ; right on the edge or near the edge to cause a turn and make the segment head off-screen. This ; special case is there only for fast head segments because we only add fast head segments on the ; right and left. ldx segmentTileOffsetsUR,y cpx #RHS_FIRST_TILE_OFFSET bge updateSegmentLeftFast_noChangeDir ldx segmentTileOffsetsUL,y cpx #LHS_FIRST_TILE_OFFSET bge updateSegmentLeftFast_changeDir lda tileType,x bne updateSegmentLeftFast_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentLeftFast_changeDir ldx segmentTileOffsetsUL,y lda tileLeft,x cmp #LHS_FIRST_TILE_OFFSET bge updateSegmentLeftFast_changeDir tax lda tileType,x bne updateSegmentLeftFast_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentLeftFast_changeDir updateSegmentLeftFast_noChangeDir anop rts updateSegmentLeftFast_checkPoison anop cmp #TILE_POISON_MUSHROOM1 blt updateSegmentLeftFast_changeDir ldx segmentBeingUpdated lda #SEGMENT_STATE_POISONED_HEAD sta segmentStates,x updateSegmentLeftFast_changeDir anop lda #SEGMENT_FACING_DOWN_LEFT sta segmentFacing,y lda #SEGMENT_DIR_RIGHT sta segmentHorizontalDir,y lda segmentVerticalDir,y beq updateSegmentLeftFast_dirDown lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO bge updateSegmentLeftFast_doUp lda #SEGMENT_DIR_DOWN sta segmentVerticalDir,y bra updateSegmentLeftFast_doDown updateSegmentLeftFast_doUp anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*2 sta segmentScreenOffsets,y lda segmentTileOffsetsLR,y tax lda tileAbove,x sta segmentTileOffsetsUR,y lda segmentTileOffsetsLL,y tax lda tileAbove,x sta segmentTileOffsetsUL,y sta segmentCurrentTile,y rts updateSegmentLeftFast_dirDown anop lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO blt updateSegmentLeftFast_doDown ; If the head segment was poisoned, it is no longer poisoned once the head is going up again. lda #SEGMENT_DIR_UP sta segmentVerticalDir,y ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_HEAD beq updateSegmentLeftFast_notPoisoned lda #SEGMENT_STATE_HEAD sta segmentStates,x bra updateSegmentLeftFast_doUp updateSegmentLeftFast_notPoisoned anop stz segmentsAddEnabled bra updateSegmentLeftFast_doUp updateSegmentLeftFast_doDown anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW*2 sta segmentScreenOffsets,y lda segmentTileOffsetsUR,y tax lda tileBelow,x sta segmentTileOffsetsLR,y lda segmentTileOffsetsUL,y tax lda tileBelow,x sta segmentTileOffsetsLL,y sta segmentCurrentTile,y rts updateSegmentLeftSlow entry lda #1 and segmentPixelOffset beq updateSegmentLeftSlow_skipDec tyx dec segmentScreenOffsets,x updateSegmentLeftSlow_skipDec anop lda segmentPixelOffset bne updateSegmentLeftSlow_nextOffset lda segmentTileOffsetsUL,y sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y lda numSegments cmp #1 beq updateSegmentsLeftSlow_lastSegment rts updateSegmentsLeftSlow_lastSegment anop ldx segmentBeingUpdated lda #SEGMENT_SPEED_FAST sta segmentSpeed,x rts updateSegmentLeftSlow_nextOffset anop cmp #1 bne updateSegmentLeftSlow_nextOffset2 lda segmentTileOffsetsUL,y tax lda tileLeft,x sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y sta segmentCurrentTile,y rts updateSegmentLeftSlow_nextOffset2 anop cmp #5 beq updateSegmentLeftSlow_checkDir rts updateSegmentLeftSlow_checkDir anop ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_POISONED_HEAD beq updateSegmentLeftSlow_changeDir ldx segmentTileOffsetsUL,y cpx #LHS_FIRST_TILE_OFFSET bge updateSegmentLeftSlow_changeDir lda tileType,x bne updateSegmentLeftSlow_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentLeftSlow_changeDir ldx segmentTileOffsetsUL,y lda tileLeft,x cmp #LHS_FIRST_TILE_OFFSET bge updateSegmentLeftSlow_changeDir tax lda tileType,x bne updateSegmentLeftSlow_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentLeftSlow_changeDir rts updateSegmentLeftSlow_checkPoison anop cmp #TILE_POISON_MUSHROOM1 blt updateSegmentLeftSlow_changeDir ldx segmentBeingUpdated lda #SEGMENT_STATE_POISONED_HEAD sta segmentStates,x updateSegmentLeftSlow_changeDir anop lda #SEGMENT_FACING_DOWN_LEFT sta segmentFacing,y lda #SEGMENT_DIR_RIGHT sta segmentHorizontalDir,y lda segmentVerticalDir,y beq updateSegmentLeftSlow_dirDown lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO bge updateSegmentLeftSlow_doUp lda #SEGMENT_DIR_DOWN sta segmentVerticalDir,y bra updateSegmentLeftSlow_doDown updateSegmentLeftSlow_doUp anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW sta segmentScreenOffsets,y lda segmentTileOffsetsLR,y tax lda tileAbove,x sta segmentTileOffsetsUR,y lda segmentTileOffsetsLL,y tax lda tileAbove,x sta segmentTileOffsetsUL,y sta segmentCurrentTile,y rts updateSegmentLeftSlow_dirDown anop lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO blt updateSegmentLeftSlow_doDown ; If the head segment was poisoned, it is no longer poisoned once the head is going up again. lda #SEGMENT_DIR_UP sta segmentVerticalDir,y ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_HEAD beq updateSegmentLeftSlow_notPoisoned lda #SEGMENT_STATE_HEAD sta segmentStates,x bra updateSegmentLeftSlow_doUp updateSegmentLeftSlow_notPoisoned anop stz segmentsAddEnabled bra updateSegmentLeftSlow_doUp updateSegmentLeftSlow_doDown anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW sta segmentScreenOffsets,y lda segmentTileOffsetsUR,y tax lda tileBelow,x sta segmentTileOffsetsLR,y lda segmentTileOffsetsUL,y tax lda tileBelow,x sta segmentTileOffsetsLL,y sta segmentCurrentTile,y rts updateSegmentDownLeftFast entry lda segmentScreenOffsets,y cmp #$2000-(7*SCREEN_BYTES_PER_ROW) bge updateSegmentDownLeftFast_notOffscreen lda segmentPixelOffset cmp #7 beq updateSegmentDownLeftFast_moveOnScreen rts updateSegmentDownLeftFast_moveOnScreen anop lda segmentScreenOffsets,y clc adc #2*SCREEN_BYTES_PER_ROW sta segmentScreenOffsets,y rts updateSegmentDownLeftFast_notOffscreen anop lda segmentVerticalDir,y beq updateSegmentDownLeftFast_down lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*2+1 bra updateSegmentDownLeftFast_cont updateSegmentDownLeftFast_down anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW*2-1 updateSegmentDownLeftFast_cont anop sta segmentScreenOffsets,y lda segmentPixelOffset and #3 beq updateSegmentDownLeftFast_nextOffset lda #SEGMENT_FACING_LEFT sta segmentFacing,y lda segmentVerticalDir,y beq updateSegmentDownLeftFast_tilesDown lda segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y rts updateSegmentDownLeftFast_tilesDown anop lda segmentTileOffsetsLL,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsLR,y sta segmentTileOffsetsUR,y rts updateSegmentDownLeftFast_nextOffset anop lda #SEGMENT_FACING_DOWN sta segmentFacing,y rts updateSegmentDownLeftSlow entry lda segmentScreenOffsets,y tax lda #1 and segmentPixelOffset beq updateSegmentDownLeftSlow_skipDec dex updateSegmentDownLeftSlow_skipDec anop lda segmentVerticalDir,y beq updateSegmentDownLeftSlow_down txa sec sbc #SCREEN_BYTES_PER_ROW bra updateSegmentDownLeftSlow_cont updateSegmentDownLeftSlow_down anop txa clc adc #SCREEN_BYTES_PER_ROW updateSegmentDownLeftSlow_cont anop sta segmentScreenOffsets,y lda segmentPixelOffset cmp #4 bne updateSegmentDownLeftSlow_nextOffset lda #SEGMENT_FACING_LEFT sta segmentFacing,y lda segmentVerticalDir,y beq updateSegmentDownLeftSlow_tilesDown lda segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y rts updateSegmentDownLeftSlow_tilesDown anop lda segmentTileOffsetsLL,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsLR,y sta segmentTileOffsetsUR,y rts updateSegmentDownLeftSlow_nextOffset anop cmp #7 bne updateSegmentDownLeftSlow_done lda #SEGMENT_FACING_DOWN sta segmentFacing,y updateSegmentDownLeftSlow_done anop rts updateSegmentDownFast entry lda segmentHorizontalDir,y beq updateSegmentDownFast_left lda #SEGMENT_FACING_DOWN_RIGHT sta segmentFacing,y lda segmentVerticalDir,y beq updateSegmentDownFast_downRight lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*2-1 bra updateSegmentDownFast_done updateSegmentDownFast_downRight anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW*2+1 bra updateSegmentDownFast_done updateSegmentDownFast_left anop lda #SEGMENT_FACING_DOWN_LEFT sta segmentFacing,y lda segmentVerticalDir,y beq updateSegmentDownFast_downLeft lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*2+1 bra updateSegmentDownFast_done updateSegmentDownFast_downLeft anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW*2-1 updateSegmentDownFast_done anop sta segmentScreenOffsets,y rts updateSegmentDownSlow entry lda segmentVerticalDir,y beq updateSegmentDownSlow_down lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW bra updateSegmentDownSlow_cont updateSegmentDownSlow_down anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW updateSegmentDownSlow_cont anop sta segmentScreenOffsets,y lda segmentPixelOffset cmp #2 bne updateSegmentDownSlow_done lda segmentHorizontalDir,y beq updateSegmentDownSlow_left lda #SEGMENT_FACING_DOWN_RIGHT sta segmentFacing,y tyx inc segmentScreenOffsets,x rts updateSegmentDownSlow_left anop lda #SEGMENT_FACING_DOWN_LEFT sta segmentFacing,y tyx dec segmentScreenOffsets,x updateSegmentDownSlow_done anop rts updateSegmentDownRightFast entry lda segmentScreenOffsets,y cmp #$2000-(7*SCREEN_BYTES_PER_ROW) bge updateSegmentDownRightFast_notOffscreen lda segmentPixelOffset cmp #7 beq updateSegmentDownRightFast_moveOnScreen rts updateSegmentDownRightFast_moveOnScreen anop lda segmentScreenOffsets,y clc adc #2*SCREEN_BYTES_PER_ROW sta segmentScreenOffsets,y rts updateSegmentDownRightFast_notOffscreen anop lda segmentVerticalDir,y beq updateSegmentDownRightFast_down lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*2-1 bra updateSegmentDownRightFast_cont updateSegmentDownRightFast_down anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW*2+1 updateSegmentDownRightFast_cont anop sta segmentScreenOffsets,y lda segmentPixelOffset and #3 beq updateSegmentDownRightFast_nextOffset lda #SEGMENT_FACING_RIGHT sta segmentFacing,y lda segmentVerticalDir,y beq updateSegmentDownRightFast_tilesDown lda segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y rts updateSegmentDownRightFast_tilesDown anop lda segmentTileOffsetsLL,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsLR,y sta segmentTileOffsetsUR,y rts updateSegmentDownRightFast_nextOffset anop lda #SEGMENT_FACING_DOWN sta segmentFacing,y rts updateSegmentDownRightSlow entry lda segmentScreenOffsets,y tax lda #1 and segmentPixelOffset beq updateSegmentDownRightSlow_skipInc inx updateSegmentDownRightSlow_skipInc anop lda segmentVerticalDir,y beq updateSegmentDownRightSlow_down txa sec sbc #SCREEN_BYTES_PER_ROW bra updateSegmentDownRightSlow_cont updateSegmentDownRightSlow_down anop txa clc adc #SCREEN_BYTES_PER_ROW updateSegmentDownRightSlow_cont anop sta segmentScreenOffsets,y lda segmentPixelOffset cmp #4 bne updateSegmentDownRightSlow_nextOffset lda #SEGMENT_FACING_RIGHT sta segmentFacing,y lda segmentVerticalDir,y beq updateSegmentDownRightSlow_tilesDown lda segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y rts updateSegmentDownRightSlow_tilesDown anop lda segmentTileOffsetsLL,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsLR,y sta segmentTileOffsetsUR,y rts updateSegmentDownRightSlow_nextOffset anop cmp #7 bne updateSegmentDownRightSlow_done lda #SEGMENT_FACING_DOWN sta segmentFacing,y updateSegmentDownRightSlow_done anop rts updateSegmentRightFast entry tyx inc segmentScreenOffsets,x lda segmentPixelOffset and #3 bne updateSegmentRightFast_nextOffset lda segmentTileOffsetsUR,y sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y rts updateSegmentRightFast_nextOffset anop cmp #1 bne updateSegmentRightFast_nextOffset2 lda segmentTileOffsetsUR,y tax lda tileRight,x sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y sta segmentCurrentTile,y updateSegmentRightFast_nextOffset2 anop cmp #3 beq updateSegmentRightFast_checkDir rts updateSegmentRightFast_checkDir anop ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_POISONED_HEAD beq updateSegmentRightFast_changeDir ; This is a important special case. When we add segments on the side, we don't want a mushroom ; right on the edge or near the edge to cause a turn and make the segment head off-screen. This ; special case is there only for fast head segments because we only add fast head segments on the ; right and left. ldx segmentTileOffsetsUL,y cpx #LHS_FIRST_TILE_OFFSET bge updateSegmentRightFast_noChangeDir ldx segmentTileOffsetsUR,y cpx #RHS_FIRST_TILE_OFFSET bge updateSegmentRightFast_changeDir lda tileType,x bne updateSegmentRightFast_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentRightFast_changeDir ldx segmentTileOffsetsUR,y lda tileRight,x cmp #RHS_FIRST_TILE_OFFSET bge updateSegmentRightFast_changeDir tax lda tileType,x bne updateSegmentRightFast_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentRightFast_changeDir updateSegmentRightFast_noChangeDir anop rts updateSegmentRightFast_checkPoison anop cmp #TILE_POISON_MUSHROOM1 blt updateSegmentRightFast_changeDir ldx segmentBeingUpdated lda #SEGMENT_STATE_POISONED_HEAD sta segmentStates,x updateSegmentRightFast_changeDir anop lda #SEGMENT_FACING_DOWN_RIGHT sta segmentFacing,y lda #SEGMENT_DIR_LEFT sta segmentHorizontalDir,y lda segmentVerticalDir,y beq updateSegmentRightFast_dirDown lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO bge updateSegmentRightFast_doUp lda #SEGMENT_DIR_DOWN sta segmentVerticalDir,y bra updateSegmentRightFast_doDown updateSegmentRightFast_doUp anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*2 sta segmentScreenOffsets,y lda segmentTileOffsetsLR,y tax lda tileAbove,x sta segmentTileOffsetsUR,y sta segmentCurrentTile,y lda segmentTileOffsetsLL,y tax lda tileAbove,x sta segmentTileOffsetsUL,y rts updateSegmentRightFast_dirDown anop lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO blt updateSegmentRightFast_doDown ; If the head segment was poisoned, it is no longer poisoned once the head is going up again. lda #SEGMENT_DIR_UP sta segmentVerticalDir,y ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_HEAD beq updateSegmentRightFast_notPoisoned lda #SEGMENT_STATE_HEAD sta segmentStates,x bra updateSegmentRightFast_doUp updateSegmentRightFast_notPoisoned anop stz segmentsAddEnabled bra updateSegmentRightFast_doUp updateSegmentRightFast_doDown anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW*2 sta segmentScreenOffsets,y lda segmentTileOffsetsUR,y tax lda tileBelow,x sta segmentTileOffsetsLR,y sta segmentCurrentTile,y lda segmentTileOffsetsUL,y tax lda tileBelow,x sta segmentTileOffsetsLL,y rts updateSegmentRightSlow entry lda #1 and segmentPixelOffset beq updateSegmentRightSlow_skipInc tyx inc segmentScreenOffsets,x updateSegmentRightSlow_skipInc anop lda segmentPixelOffset bne updateSegmentRightSlow_nextOffset lda segmentTileOffsetsUR,y sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y rts updateSegmentRightSlow_nextOffset anop cmp #1 bne updateSegmentRightSlow_nextOffset2 lda segmentTileOffsetsUR,y tax lda tileRight,x sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y sta segmentCurrentTile,y lda numSegments cmp #1 beq updateSegmentsRightSlow_lastSegment rts updateSegmentsRightSlow_lastSegment anop ldx segmentBeingUpdated lda #SEGMENT_SPEED_FAST sta segmentSpeed,x rts updateSegmentRightSlow_nextOffset2 anop cmp #5 beq updateSegmentRightSlow_checkDir rts updateSegmentRightSlow_checkDir anop ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_POISONED_HEAD beq updateSegmentRightSlow_changeDir ldx segmentTileOffsetsUR,y cpx #RHS_FIRST_TILE_OFFSET bge updateSegmentRightSlow_changeDir lda tileType,x bne updateSegmentRightSlow_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentRightSlow_changeDir ldx segmentTileOffsetsUR,y lda tileRight,x cmp #RHS_FIRST_TILE_OFFSET bge updateSegmentRightSlow_changeDir tax lda tileType,x bne updateSegmentRightSlow_checkPoison phy txy ldx tileBitOffset,y lda tileBitMask,y ply and segmentTileMask,x bne updateSegmentRightSlow_changeDir rts updateSegmentRightSlow_checkPoison anop cmp #TILE_POISON_MUSHROOM1 blt updateSegmentRightSlow_changeDir ldx segmentBeingUpdated lda #SEGMENT_STATE_POISONED_HEAD sta segmentStates,x updateSegmentRightSlow_changeDir anop lda #SEGMENT_FACING_DOWN_RIGHT sta segmentFacing,y lda #SEGMENT_DIR_LEFT sta segmentHorizontalDir,y lda segmentVerticalDir,y beq updateSegmentRightSlow_dirDown lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-5*GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO bge updateSegmentRightSlow_doUp lda #SEGMENT_DIR_DOWN sta segmentVerticalDir,y bra updateSegmentRightSlow_doDown updateSegmentRightSlow_doUp anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW sta segmentScreenOffsets,y lda segmentTileOffsetsLR,y tax lda tileAbove,x sta segmentTileOffsetsUR,y sta segmentCurrentTile,y lda segmentTileOffsetsLL,y tax lda tileAbove,x sta segmentTileOffsetsUL,y rts updateSegmentRightSlow_dirDown anop lda segmentTileOffsetsUR,y cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO blt updateSegmentRightSlow_doDown ; If the head segment was poisoned, it is no longer poisoned once the head is going up again. lda #SEGMENT_DIR_UP sta segmentVerticalDir,y ldx segmentBeingUpdated lda segmentStates,x cmp #SEGMENT_STATE_HEAD beq updateSegmentRightSlow_notPoisoned lda #SEGMENT_STATE_HEAD sta segmentStates,x bra updateSegmentRightSlow_doUp updateSegmentRightSlow_notPoisoned anop stz segmentsAddEnabled bra updateSegmentRightSlow_doUp updateSegmentRightSlow_doDown anop lda segmentScreenOffsets,y clc adc #SCREEN_BYTES_PER_ROW sta segmentScreenOffsets,y lda segmentTileOffsetsUR,y tax lda tileBelow,x sta segmentTileOffsetsLR,y sta segmentCurrentTile,y lda segmentTileOffsetsUL,y tax lda tileBelow,x sta segmentTileOffsetsLL,y rts addBodySegment entry lda numSegments asl a tax inc numSegments lda #SEGMENT_STATE_BODY sta segmentStates,x lda segmentSpeed-2,x sta segmentSpeed,x beq addBodySegment_fast jmp addBodySegment_slow addBodySegment_fast anop lda segmentPosOffset-2,x clc adc #8 sta segmentPosOffset,x tay lda segmentHorizontalDir-8,y sta segmentHorizontalDir-6,y sta segmentHorizontalDir-4,y sta segmentHorizontalDir-2,y sta segmentHorizontalDir,y lda segmentVerticalDir-8,y sta segmentVerticalDir-6,y sta segmentVerticalDir-4,y sta segmentVerticalDir-2,y sta segmentVerticalDir,y lda segmentFacing-8,y sta segmentFacing-6,y sta segmentFacing-4,y sta segmentFacing-2,y sta segmentFacing,y lda tileScreenOffset sec sbc #SCREEN_BYTES_PER_ROW*8 sta segmentScreenOffsets-6,y sta segmentScreenOffsets-4,y sta segmentScreenOffsets-2,y sta segmentScreenOffsets,y lda segmentTileOffsetsUL-8,y sta segmentTileOffsetsUL-6,y sta segmentTileOffsetsUL-4,y sta segmentTileOffsetsUL-2,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsUR-8,y sta segmentTileOffsetsUR-6,y sta segmentTileOffsetsUR-4,y sta segmentTileOffsetsUR-2,y sta segmentTileOffsetsUR,y sta segmentTileOffsetsUR,y lda segmentTileOffsetsLL-8,y sta segmentTileOffsetsLL-6,y sta segmentTileOffsetsLL-4,y sta segmentTileOffsetsLL-2,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsLR-8,y sta segmentTileOffsetsLR-6,y sta segmentTileOffsetsLR-4,y sta segmentTileOffsetsLR-2,y sta segmentTileOffsetsLR,y ; This is a bit weird but we pretend the body segment is in the bottom RHS tile while it is offscreen. ; We have to put in a valid game tile here and it doesn't matter if body segments "collide". But we ; don't want head segments to collide with a off-screen body segment. Since the head segments always ; appear at the top, it is safe to just tuck the body segments away at the top RHS. lda #RHS_FIRST_TILE_OFFSET sta segmentCurrentTile-6,y sta segmentCurrentTile-4,y sta segmentCurrentTile-2,y sta segmentCurrentTile,y rtl addBodySegment_slow anop lda segmentPosOffset-2,x clc adc #16 sta segmentPosOffset,x tay lda segmentHorizontalDir-16,y sta segmentHorizontalDir-14,y sta segmentHorizontalDir-12,y sta segmentHorizontalDir-10,y sta segmentHorizontalDir-8,y sta segmentHorizontalDir-6,y sta segmentHorizontalDir-4,y sta segmentHorizontalDir-2,y sta segmentHorizontalDir,y lda segmentVerticalDir-16,y sta segmentVerticalDir-14,y sta segmentVerticalDir-12,y sta segmentVerticalDir-10,y sta segmentVerticalDir-8,y sta segmentVerticalDir-6,y sta segmentVerticalDir-4,y sta segmentVerticalDir-2,y sta segmentVerticalDir,y lda segmentFacing-16,y sta segmentFacing-14,y sta segmentFacing-12,y sta segmentFacing-10,y sta segmentFacing-8,y sta segmentFacing-6,y sta segmentFacing-4,y sta segmentFacing-2,y sta segmentFacing,y lda tileScreenOffset sec sbc #SCREEN_BYTES_PER_ROW*8 sta segmentScreenOffsets-14,y sta segmentScreenOffsets-12,y sta segmentScreenOffsets-10,y sta segmentScreenOffsets-8,y sta segmentScreenOffsets-6,y sta segmentScreenOffsets-4,y sta segmentScreenOffsets-2,y sta segmentScreenOffsets,y lda segmentTileOffsetsUL-16,y sta segmentTileOffsetsUL-14,y sta segmentTileOffsetsUL-12,y sta segmentTileOffsetsUL-10,y sta segmentTileOffsetsUL-8,y sta segmentTileOffsetsUL-6,y sta segmentTileOffsetsUL-4,y sta segmentTileOffsetsUL-2,y sta segmentTileOffsetsUL,y lda segmentTileOffsetsUR-16,y sta segmentTileOffsetsUR-14,y sta segmentTileOffsetsUR-12,y sta segmentTileOffsetsUR-10,y sta segmentTileOffsetsUR-8,y sta segmentTileOffsetsUR-6,y sta segmentTileOffsetsUR-4,y sta segmentTileOffsetsUR-2,y sta segmentTileOffsetsUR,y sta segmentTileOffsetsUR,y lda segmentTileOffsetsLL-16,y sta segmentTileOffsetsLL-14,y sta segmentTileOffsetsLL-12,y sta segmentTileOffsetsLL-10,y sta segmentTileOffsetsLL-8,y sta segmentTileOffsetsLL-6,y sta segmentTileOffsetsLL-4,y sta segmentTileOffsetsLL-2,y sta segmentTileOffsetsLL,y lda segmentTileOffsetsLR-16,y sta segmentTileOffsetsLR-14,y sta segmentTileOffsetsLR-12,y sta segmentTileOffsetsLR-10,y sta segmentTileOffsetsLR-8,y sta segmentTileOffsetsLR-6,y sta segmentTileOffsetsLR-4,y sta segmentTileOffsetsLR-2,y sta segmentTileOffsetsLR,y sta segmentTileOffsetsLR,y ; See the comment above why we pretend the body segment is at the top RHS tile. lda #RHS_FIRST_TILE_OFFSET sta segmentCurrentTile-14,y sta segmentCurrentTile-12,y sta segmentCurrentTile-10,y sta segmentCurrentTile-8,y sta segmentCurrentTile-6,y sta segmentCurrentTile-4,y sta segmentCurrentTile-2,y sta segmentCurrentTile,y rtl ; This method is called with X register pointing to a structure which has the following info in it: ; - segment speed (2 bytes) ; - segment direction (2 bytes) ; - tile offset (2 bytes) ; - number of body segments (2 bytes) ; ; It reads this info and sets up the appropriate centipede segments. Also, it is expected to ; preserve the X register. addCentipede entry lda numSegments asl a tay lda #SEGMENT_STATE_HEAD sta segmentStates,y lda |$0,x sta segmentSpeed,y tya asl a asl a asl a sta segmentPosOffset,y tay lda #SEGMENT_DIR_DOWN sta segmentVerticalDir,y lda |$2,x sta segmentHorizontalDir,y phx lda |$4,x tax lda tileScreenOffset,x ; We will correct this later to adjust for speed and direction. sta segmentScreenOffsets,y lda segmentHorizontalDir,y beq addCentipede_left anop txa sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y sta segmentCurrentTile,y lda tileRight,x sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y plx lda |$0,x beq addCentipede_rightFast anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*7+2 bra addCentipede_rightFacing addCentipede_rightFast anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*8+2 addCentipede_rightFacing anop sta segmentScreenOffsets,y lda #SEGMENT_FACING_DOWN_LEFT bra addCentipede_facing addCentipede_left anop txa sta segmentTileOffsetsUR,y sta segmentTileOffsetsLR,y sta segmentCurrentTile,y lda tileLeft,x sta segmentTileOffsetsUL,y sta segmentTileOffsetsLL,y plx lda |$0,x beq addCentipede_leftFast anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*7+4 bra addCentipede_leftFacing addCentipede_leftFast anop lda segmentScreenOffsets,y sec sbc #SCREEN_BYTES_PER_ROW*8+4 addCentipede_leftFacing anop sta segmentScreenOffsets,y lda #SEGMENT_FACING_DOWN_RIGHT addCentipede_facing anop sta segmentFacing,y inc numSegments lda #5 sta segmentPixelOffset lda #1 sta segmentsAddEnabled ldy |$6,x beq addCentipede_done phx addCentipede_body anop phy jsl addBodySegment ply dey bne addCentipede_body plx addCentipede_done anop rtl ; The accummulator has the tile offset where the collision happened ; The y register has the address where the collision happened ; ; The result is a clear carry if there is no collision found or ; a set carry and the segment num * 2 in the X register isSegmentCollision entry ldy numSegments beq isSegmentCollision_returnFalse ldx #SEGMENT_MAX_OFFSET isSegmentCollision_loop anop ldy segmentStates,x beq isSegmentCollision_nextSegment cpy #SEGMENT_STATE_EXPLODING beq isSegmentCollision_nextSegment ldy segmentPosOffset,x cmp segmentTileOffsetsLL,y beq isSegmentCollision_returnTrue cmp segmentTileOffsetsLR,y beq isSegmentCollision_returnTrue cmp segmentTileOffsetsUL,y beq isSegmentCollision_returnTrue cmp segmentTileOffsetsUR,y beq isSegmentCollision_returnTrue isSegmentCollision_nextSegment anop dex dex bpl isSegmentCollision_loop isSegmentCollision_returnFalse anop clc rtl isSegmentCollision_returnTrue anop sec rtl ; Call this with the segment num * 2 in the X register shootSegment entry phx ldy segmentPosOffset,x ldx segmentCurrentTile,y jsl playKillSound plx phx lda segmentStates,x cmp #SEGMENT_STATE_BODY beq shootSegment_body jsl scoreAddOneHundred bra shootSegment_doneScore shootSegment_body anop jsl scoreAddTen shootSegment_doneScore anop plx jsl explodeSegment ldy segmentPosOffset,x lda segmentCurrentTile,y cmp #(NUM_GAME_TILES-GAME_NUM_TILES_WIDE)*SIZEOF_TILE_INFO bge shootSegment_done tay lda tileType,y beq shootSegment_noMushroom cmp #TILE_POISON_MUSHROOM1 blt shootSegment_normalMushroom lda #TILE_POISON_MUSHROOM4 bra shootSegment_dirtyTile shootSegment_noMushroom anop cpy #SPIDER_STARTING_TOP_ROW_OFFSET blt shootSegment_normalMushroom ldx playerNum inc numInfieldMushrooms,x shootSegment_normalMushroom anop lda #TILE_MUSHROOM4 shootSegment_dirtyTile anop sta tileType,y lda #TILE_STATE_DIRTY sta tileDirty,y shootSegment_done anop rtl ; Call this with the segment num * 2 in the X register explodeSegment entry dec numSegments lda #SEGMENT_STATE_EXPLODING sta segmentStates,x ldy segmentPosOffset,x ; We take over the segmentFacing value when exploding to be an explosion sprite offset lda #EXPLOSION_LAST_OFFSET sta segmentFacing,y ; If this is the last segment, then do not look for a following body segment cpx #22 bge shootSegment_done ; If the segment after this is a body segment, then it is now a head segment lda segmentStates+2,x cmp #SEGMENT_STATE_BODY bne explodeSegment_done lda #SEGMENT_STATE_HEAD sta segmentStates+2,x explodeSegment_done anop rtl segmentsAddEnabled dc i2'1' ; The method used to track a segments position and other details on the screen are a bit ; funky. In order to have body segments follow a head segment, we keep information from ; the position of the head segment. The segmentPosOffset gives an offset into the other ; larger arrays (96 words) which describes the position of the segment. When a head is ; updated, the segmentPosOffset is decremented. That way, the previous positions are ; still there and body segments after that can reference it. ; ; We need at least 96 of them because a slow moving head segment goes through 8 positions ; before it can be re-used by the next body segment. If there are 12 segments max, we ; need (8*12) positions to ensure all segments can know where there position was and will ; be next. segmentStates dc 12i2'SEGMENT_STATE_NONE' segmentSpeed dc 12i2'SEGMENT_SPEED_SLOW' segmentPosOffset dc 12i2'0' segmentHorizontalDir dc 96i2'SEGMENT_DIR_RIGHT' segmentVerticalDir dc 96i2'SEGMENT_DIR_DOWN' segmentFacing dc 96i2'SEGMENT_FACING_DOWN' segmentScreenOffsets dc 96i2'0' segmentTileOffsetsUL dc 96i2'0' segmentTileOffsetsUR dc 96i2'0' segmentTileOffsetsLL dc 96i2'0' segmentTileOffsetsLR dc 96i2'0' segmentCurrentTile dc 96i2'0' segmentTileMask dc 40i2'0' SEGMENT_SPRITE_LAST_OFFSET gequ 7*4 segmentSpriteOffset dc i2'0' segmentPixelOffset dc i2'0' segmentBeingUpdated dc i2'0' segmentEmptyOffset dc i2'0' headJumpTable anop ; leftHeadJumpTable dc i4'leftHead5' dc i4'leftHead4' dc i4'leftHead1' dc i4'leftHead2' dc i4'leftHead3' dc i4'leftHead2' dc i4'leftHead1' dc i4'leftHead4' ; leftDownHeadJumpTable dc i4'leftDownHead1' dc i4'leftDownHead2' dc i4'leftDownHead1' dc i4'leftDownHead2' dc i4'leftDownHead1' dc i4'leftDownHead2' dc i4'leftDownHead1' dc i4'leftDownHead2' ; downHeadJumpTable dc i4'downHead3' dc i4'downHead3' dc i4'downHead1' dc i4'downHead1' dc i4'downHead2' dc i4'downHead2' dc i4'downHead1' dc i4'downHead1' ; rightDownHeadJumpTable dc i4'rightDownHead1s' dc i4'rightDownHead2s' dc i4'rightDownHead1s' dc i4'rightDownHead2s' dc i4'rightDownHead1s' dc i4'rightDownHead2s' dc i4'rightDownHead1s' dc i4'rightDownHead2s' ; rightHeadJumpTable dc i4'rightHead5' dc i4'rightHead4' dc i4'rightHead1' dc i4'rightHead2' dc i4'rightHead3' dc i4'rightHead2' dc i4'rightHead1' dc i4'rightHead4' headShiftJumpTable anop ; leftHeadShiftJumpTable dc i4'leftHead5s' dc i4'leftHead4s' dc i4'leftHead1s' dc i4'leftHead2s' dc i4'leftHead3s' dc i4'leftHead2s' dc i4'leftHead1s' dc i4'leftHead4s' ; leftDownShiftHeadJumpTable dc i4'leftDownHead1s' dc i4'leftDownHead2s' ; Problem, spills into next tile... dc i4'leftDownHead1s' dc i4'leftDownHead2s' ; Problem, spills into next tile... dc i4'leftDownHead1s' dc i4'leftDownHead2s' ; Problem, spills into next tile... dc i4'leftDownHead1s' dc i4'leftDownHead2s' ; Problem, spills into next tile... ; downHeadJumpTable dc i4'downHead3' dc i4'downHead3' dc i4'downHead1' dc i4'downHead1' dc i4'downHead2' dc i4'downHead2' dc i4'downHead1' dc i4'downHead1' ; rightDownShiftHeadJumpTable dc i4'rightDownHead1' dc i4'rightDownHead2' dc i4'rightDownHead1' dc i4'rightDownHead2' dc i4'rightDownHead1' dc i4'rightDownHead2' dc i4'rightDownHead1' dc i4'rightDownHead2' ; rightHeadShiftJumpTable dc i4'rightHead5s' dc i4'rightHead4s' dc i4'rightHead1s' dc i4'rightHead2s' dc i4'rightHead3s' dc i4'rightHead2s' dc i4'rightHead1s' dc i4'rightHead4s' bodyJumpTable anop ; leftBodyJumpTable dc i4'leftBody5' dc i4'leftBody4' dc i4'leftBody1' dc i4'leftBody2' dc i4'leftBody3' dc i4'leftBody2' dc i4'leftBody1' dc i4'leftBody4' ; leftDownBodyJumpTable dc i4'leftDownBody1' dc i4'leftDownBody2' dc i4'leftDownBody1' dc i4'leftDownBody2' dc i4'leftDownBody1' dc i4'leftDownBody2' dc i4'leftDownBody1' dc i4'leftDownBody2' ; downBodyJumpTable dc i4'downBody3' dc i4'downBody3' dc i4'downBody1' dc i4'downBody1' dc i4'downBody2' dc i4'downBody2' dc i4'downBody1' dc i4'downBody1' ; rightDownBodyJumpTable dc i4'rightDownBody1s' dc i4'rightDownBody2s' dc i4'rightDownBody1s' dc i4'rightDownBody2s' dc i4'rightDownBody1s' dc i4'rightDownBody2s' dc i4'rightDownBody1s' dc i4'rightDownBody2s' ; rightBodyJumpTable dc i4'rightBody5' dc i4'rightBody4' dc i4'rightBody1' dc i4'rightBody2' dc i4'rightBody3' dc i4'rightBody2' dc i4'rightBody1' dc i4'rightBody4' bodyShiftJumpTable anop ; leftBodyShiftJumpTable dc i4'leftBody5s' dc i4'leftBody4s' dc i4'leftBody1s' dc i4'leftBody2s' dc i4'leftBody3s' dc i4'leftBody2s' dc i4'leftBody1s' dc i4'leftBody4s' ; leftDownShiftBodyJumpTable dc i4'leftDownBody1s' dc i4'leftDownBody2s' ; Problem, spills into next tile... dc i4'leftDownBody1s' dc i4'leftDownBody2s' ; Problem, spills into next tile... dc i4'leftDownBody1s' dc i4'leftDownBody2s' ; Problem, spills into next tile... dc i4'leftDownBody1s' dc i4'leftDownBody2s' ; Problem, spills into next tile... ; downBodyJumpTable dc i4'downBody3' dc i4'downBody3' dc i4'downBody1' dc i4'downBody1' dc i4'downBody2' dc i4'downBody2' dc i4'downBody1' dc i4'downBody1' ; rightDownShiftBodyJumpTable dc i4'rightDownBody1' dc i4'rightDownBody2' dc i4'rightDownBody1' dc i4'rightDownBody2' dc i4'rightDownBody1' dc i4'rightDownBody2' dc i4'rightDownBody1' dc i4'rightDownBody2' ; rightBodyShiftJumpTable dc i4'rightBody5s' dc i4'rightBody4s' dc i4'rightBody1s' dc i4'rightBody2s' dc i4'rightBody3s' dc i4'rightBody2s' dc i4'rightBody1s' dc i4'rightBody4s' segmentUpdateJumpTable dc i2'updateSegmentLeftFast' dc i2'updateSegmentLeftSlow' dc i2'updateSegmentDownLeftFast' dc i2'updateSegmentDownLeftSlow' dc i2'updateSegmentDownFast' dc i2'updateSegmentDownSlow' dc i2'updateSegmentDownRightFast' dc i2'updateSegmentDownRightSlow' dc i2'updateSegmentRightFast' dc i2'updateSegmentRightSlow' end