9. Guard AI#
Like the player, each guard has a column and row sprite location and a horizontal and vertical adjustment. Each guard also has an animation state and a facing direction.
Guards also maintain two timers: a gold timer and a resurrection timer. The resurrection timer comes into play when a guard is killed by a closing hole.
GUARD_LOCS_COL EQU $0C60 ; 8 bytes GUARD_LOCS_ROW EQU $0C68 ; 8 bytes GUARD_GOLD_TIMERS EQU $0C70 ; 8 bytes GUARD_X_ADJS EQU $0C78 ; 8 bytes GUARD_Y_ADJS EQU $0C80 ; 8 bytes GUARD_ANIM_STATES EQU $0C88 ; 8 bytes GUARD_FACING_DIRECTIONS EQU $0C90 ; 8 bytes GUARD_RESURRECTION_TIMERS EQU $0C98 ; 8 bytes GUARD_LOC_COL EQU $12 GUARD_LOC_ROW EQU $13 GUARD_ANIM_STATE EQU $14 GUARD_FACING_DIRECTION EQU $15 ; Hi bit set: facing left, otherwise facing right GUARD_GOLD_TIMER EQU $16 GUARD_X_ADJ EQU $17 GUARD_Y_ADJ EQU $18 GUARD_NUM EQU $19 GUARD_PATTERN EQU $63 GUARD_PHASE EQU $64 GUARD_RESURRECT_COL EQU $53 TMP_GUARD_COL EQU $55 TMP_GUARD_ROW EQU $56 GUARD_GOLD_TIMER_START_VALUE EQU $5F
GUARD_ANIM_STATE, GUARD_ANIM_STATES, GUARD_FACING_DIRECTION, GUARD_FACING_DIRECTIONS, GUARD_GOLD_TIMER, GUARD_GOLD_TIMERS, GUARD_GOLD_TIMER_START_VALUE, GUARD_LOCS_COL, GUARD_LOCS_ROW, GUARD_LOC_COL, GUARD_LOC_ROW, GUARD_NUM, GUARD_PATTERN, GUARD_PHASE, GUARD_X_ADJ, GUARD_X_ADJS, GUARD_Y_ADJ, GUARD_Y_ADJSORG $621D GUARD_GOLD_TIMER_START_VALUES: HEX 26 26 2E 44 47 49 4A 4B 4C 4D 4E 4F 50
ORG $7582 NUDGE_GUARD_TOWARDS_EXACT_COLUMN: SUBROUTINE LDA GUARD_X_ADJ CMP #$02 BCC .slightly_left BEQ .end .slightly_right: DEC GUARD_X_ADJ ; Nudge guard left JMP CHECK_FOR_GOLD_PICKED_UP_BY_GUARD .slightly_left: INC GUARD_X_ADJ ; Nudge guard right JMP CHECK_FOR_GOLD_PICKED_UP_BY_GUARD .end: RTS ORG $7595 NUDGE_GUARD_TOWARDS_EXACT_ROW: SUBROUTINE LDA GUARD_Y_ADJ CMP #$02 BCC .slightly_above BEQ .end .slightly_below: DEC GUARD_Y_ADJ ; Nudge guard up JMP CHECK_FOR_GOLD_PICKED_UP_BY_GUARD .slightly_above: INC GUARD_Y_ADJ ; Nudge guard down JMP CHECK_FOR_GOLD_PICKED_UP_BY_GUARD .end: RTS
NUDGE_GUARD_TOWARDS_EXACT_COLUMN, NUDGE_GUARD_TOWARDS_EXACT_ROWIf the guard is exactly on a sprite coordinate, and there's gold there, and
GUARD_GOLD_TIMER is zero or positive, then set GUARD_GOLD_TIMER to 0xFF - $53, and remove the gold.
ORG $74F7 CHECK_FOR_GOLD_PICKED_UP_BY_GUARD: SUBROUTINE LDA GUARD_X_ADJ CMP #$02 BNE .end LDA GUARD_Y_ADJ CMP #$02 BNE .end LDY GUARD_LOC_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_GOLD BNE .end LDA GUARD_GOLD_TIMER ; Does guard have gold already? BMI .end LDA #$FF SEC SBC $53 STA GUARD_GOLD_TIMER ; GUARD_GOLD_TIMER = 0xFF - $53 ; Remove gold from screen LDA #SPRITE_EMPTY STA (PTR2),Y LDY GUARD_LOC_ROW STY GAME_ROWNUM LDY GUARD_LOC_COL STY GAME_COLNUM JSR DRAW_SPRITE_PAGE2 LDY GAME_ROWNUM LDX GAME_COLNUM JSR GET_SCREEN_COORDS_FOR LDA #SPRITE_GOLD JMP ERASE_SPRITE_AT_PIXEL_COORDS ; tailcall .end: RTS
CHECK_FOR_GOLD_PICKED_UP_BY_GUARDORG $7574 INC_GUARD_ANIM_STATE: SUBROUTINE INC GUARD_ANIM_STATE CMP GUARD_ANIM_STATE BCC .check_upper_bound ; lower bound < GUARD_ANIM_STATE? ; otherwise PLAYER_ANIM_STATE <= lower bound: .write_lower_bound: STA GUARD_ANIM_STATE ; GUARD_ANIM_STATE = lower bound RTS .check_upper_bound: CPX GUARD_ANIM_STATE BCC .write_lower_bound ; GUARD_ANIM_STATE > upper bound? ; otherwise GUARD_ANIM_STATE <= upper bound: RTS
ORG $75A8 STORE_GUARD_DATA: SUBROUTINE LDX GUARD_NUM LDA GUARD_LOC_COL STA GUARD_LOCS_COL,X LDA GUARD_LOC_ROW STA GUARD_LOCS_ROW,X LDA GUARD_X_ADJ STA GUARD_X_ADJS,X LDA GUARD_Y_ADJ STA GUARD_Y_ADJS,X LDA GUARD_GOLD_TIMER STA GUARD_GOLD_TIMERS,X LDA GUARD_FACING_DIRECTION STA GUARD_FACING_DIRECTIONS,X LDA GUARD_ANIM_STATE STA GUARD_ANIM_STATES,X RTS LOAD_GUARD_DATA: SUBROUTINE LDX GUARD_NUM LDA GUARD_LOCS_COL,X STA GUARD_LOC_COL LDA GUARD_LOCS_ROW,X STA GUARD_LOC_ROW LDA GUARD_X_ADJS,X STA GUARD_X_ADJ LDA GUARD_Y_ADJS,X STA GUARD_Y_ADJ LDA GUARD_ANIM_STATES,X STA GUARD_ANIM_STATE LDA GUARD_FACING_DIRECTIONS,X STA GUARD_FACING_DIRECTION LDA GUARD_GOLD_TIMERS,X STA GUARD_GOLD_TIMER RTS
LOAD_GUARD_DATA, STORE_GUARD_DATAORG $6CCB GUARD_ANIM_SPRITES: HEX 08 2B 2C ; running left HEX 30 31 32 ; monkey-traversing left HEX 36 ; falling left HEX 28 29 2A ; running right HEX 2D 2E 2F ; monkey-traversing right HEX 35 ; falling right HEX 33 34 ; climbing
ORG $74DF GET_GUARD_SPRITE_AND_COORDS: SUBROUTINE LDX GUARD_LOC_COL LDY GUARD_X_ADJ JSR GET_HALF_SCREEN_COL_OFFSET_IN_Y_FOR STX SPRITE_NUM LDY GUARD_LOC_ROW LDX GUARD_Y_ADJ JSR GET_SCREEN_ROW_OFFSET_IN_X_FOR LDX GUARD_ANIM_STATE LDA GUARD_ANIM_SPRITES,X LDX SPRITE_NUM RTS
GET_GUARD_SPRITE_AND_COORDSThe GUARD_RESURRECTIONS routine handles guard resurrection. It checks each guard's
resurrection timer to see if it is nonzero. If so, we decrement the guard's timer and then
check the timer for specific values:
19: Draw the#SPRITE_GUARD_EGG0sprite at the guard's location.10: Draw the#SPRITE_GUARD_EGG1sprite at the guard's location.0: Increment the timer and check if the guard's location is empty on the active page. If so, put the#SPRITE_GUARDsprite at the guard's location, set its timers to zero, and play the guard resurrection sound.
ORG $7716 GUARD_RESURRECTIONS: SUBROUTINE LDX GUARD_COUNT BEQ HANDLE_TIMERS_COMMON_RETURN LDA GUARD_NUM PHA .loop: LDA GUARD_RESURRECTION_TIMERS,X BEQ .next STX GUARD_NUM JSR LOAD_GUARD_DATA LDA #$7F STA GUARD_GOLD_TIMERS,X LDA GUARD_LOCS_COL,X STA GAME_COLNUM LDA GUARD_LOCS_ROW,X STA GAME_ROWNUM DEC GUARD_RESURRECTION_TIMERS,X BEQ .resurrect LDA GUARD_RESURRECTION_TIMERS,X CMP #$13 ; 19 BNE .check_guard_flag_5_is_10 ; GUARD_RESURRECTION_TIMER is 19 LDA #SPRITE_GUARD_EGG0 JSR DRAW_SPRITE_PAGE2 JSR GET_GUARD_SPRITE_AND_COORDS LDA #SPRITE_GUARD_EGG0 JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP .next2 .check_guard_flag_5_is_10: CMP #$0A ; 10 BNE .next ; GUARD_RESURRECTION_TIMER is 10 LDA #SPRITE_GUARD_EGG1 JSR DRAW_SPRITE_PAGE2 JSR GET_GUARD_SPRITE_AND_COORDS LDA #SPRITE_GUARD_EGG1 JSR DRAW_SPRITE_AT_PIXEL_COORDS .next2: ; Restores the counter LDX GUARD_NUM .next: DEX BNE .loop PLA STA GUARD_NUM RTS .resurrect: LDY GAME_ROWNUM <<set active row pointer [[PTR1]] for [[Y]]>> LDX GUARD_NUM INC GUARD_RESURRECTION_TIMERS,X LDY GAME_COLNUM LDA (PTR1),Y BNE .next ; empty LDA #SPRITE_GUARD STA (PTR1),Y LDA #SPRITE_EMPTY JSR DRAW_SPRITE_PAGE2 LDA #$00 LDX GUARD_NUM STA GUARD_GOLD_TIMERS,X STA GUARD_RESURRECTION_TIMERS,X LDA #SPRITE_GUARD JSR DRAW_SPRITE_PAGE1 ; Play the "guard resurrection" sound JSR LOAD_SOUND_DATA HEX 02 7C 03 78 04 74 05 70 00 LDX GUARD_NUM JMP .next
; Init with: ; BYTE %10000110 ; BYTE %00111110 ; BYTE %10000101 GUARD_PATTERNS EQU $60 ; 3 bytes
ORG $6C82 MOVE_GUARDS: SUBROUTINE LDX GUARD_COUNT BEQ .end ; Increment GUARD_PHASE mod 3 INC GUARD_PHASE LDY GUARD_PHASE CPY #$03 BCC .incremented_phase LDY #$00 STY GUARD_PHASE .incremented_phase: LDA GUARD_PATTERNS,Y STA GUARD_PATTERN .loop: LSR GUARD_PATTERN ; Peel off the lsb BCC .bit_done JSR MOVE_GUARD ; Move a guard LDA ALIVE BEQ .end ; If player is dead, end. .bit_done: LDA GUARD_PATTERN BNE .loop .end: RTS
MOVE_GUARDSORG $6E7F GUARD_X_ADJ_TABLE: HEX 02 01 02 03 02 01
GUARD_ACTION EQU $58 ; Index into GUARD_FN_TABLE GUARD_ACTION_DO_NOTHING EQU #$00 GUARD_ACTION_MOVE_LEFT EQU #$01 GUARD_ACTION_MOVE_RIGHT EQU #$02 GUARD_ACTION_MOVE_UP EQU #$03 GUARD_ACTION_MOVE_DOWN EQU #$04
ORG $6E97 GUARD_FN_TABLE: WORD STORE_GUARD_DATA-1 WORD TRY_GUARD_MOVE_LEFT-1 WORD TRY_GUARD_MOVE_RIGHT-1 WORD TRY_GUARD_MOVE_UP-1 WORD TRY_GUARD_MOVE_DOWN-1
GUARD_FN_TABLEORG $6CDB MOVE_GUARD SUBROUTINE ; Increment GUARD_NUM mod GUARD_COUNT, except 1-based. INC GUARD_NUM LDX GUARD_COUNT CPX GUARD_NUM BCS .guard_num_incremented LDX #$01 STX GUARD_NUM .guard_num_incremented: JSR LOAD_GUARD_DATA LDA GUARD_GOLD_TIMER BMI .check_sprite_at_guard_pos BEQ .check_sprite_at_guard_pos ; GUARD_GOLD_TIMER > 0: DEC GUARD_GOLD_TIMER LDY GUARD_GOLD_TIMER CPY #$0D BCS .guard_gold_timer_expired ; GUARD_GOLD_TIMER < 12 JMP .check_gold_timer .guard_gold_timer_expired: LDX GUARD_NUM LDA GUARD_RESURRECTION_TIMERS,X BEQ .resurrect_guard_ JMP STORE_GUARD_DATA ; tailcall .resurrect_guard_: JMP .resurrect_guard .check_sprite_at_guard_pos: LDY GUARD_LOC_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_LADDER BEQ .ladder_ CMP #SPRITE_ROPE BNE .not_rope_or_ladder LDA GUARD_Y_ADJ CMP #$02 BEQ .ladder_ .not_rope_or_ladder: LDA GUARD_Y_ADJ CMP #$02 BCC .blank_or_player ; if GUARD_Y_ADJ < 2 LDY GUARD_LOC_ROW CPY #MAX_GAME_ROW BEQ .ladder_ ; Row == 15 <<set active and background row pointers [[PTR2]] and [[PTR1]] for [[Y+1]]>> LDY GUARD_LOC_COL LDA (PTR1),Y CMP #SPRITE_EMPTY BEQ .blank_or_player CMP #SPRITE_PLAYER BEQ .blank_or_player CMP #SPRITE_GUARD BEQ .ladder_ LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .ladder_ CMP #SPRITE_STONE BEQ .ladder_ CMP #SPRITE_LADDER BNE .blank_or_player .ladder_: JMP .ladder .blank_or_player: JSR GET_GUARD_SPRITE_AND_COORDS JSR ERASE_SPRITE_AT_PIXEL_COORDS JSR NUDGE_GUARD_TOWARDS_EXACT_COLUMN LDA #$06 LDY GUARD_FACING_DIRECTION BMI .set_guard_anim_state LDA #$0D .set_guard_anim_state STA GUARD_ANIM_STATE INC GUARD_Y_ADJ LDA GUARD_Y_ADJ CMP #$05 BCS $6dc0 ; If GUARD_Y_ADJ > 4 LDA GUARD_Y_ADJ CMP #$02 BNE .resurrect_guard JSR CHECK_FOR_GOLD_PICKED_UP_BY_GUARD LDY GUARD_LOC_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .resurrect_guard LDA GUARD_GOLD_TIMER BPL .reset_gold_timer DEC GOLD_COUNT .reset_gold_timer: LDA GUARD_GOLD_TIMER_START_VALUE STA GUARD_GOLD_TIMER LDY #$00 LDA #$75 JSR ADD_AND_UPDATE_SCORE ; SCORE += 75 ; Play the guard kill tune JSR LOAD_SOUND_DATA HEX 06 20 04 30 02 40 00 .resurrect_guard: JSR GET_GUARD_SPRITE_AND_COORDS JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP STORE_GUARD_DATA ; tailcall .6dc0: LDA #$00 STA GUARD_Y_ADJ ; set vertical adjust to -2 LDY GUARD_LOC_ROW <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .set_real_sprite LDA #SPRITE_EMPTY .set_real_sprite: STA (PTR1),Y INC GUARD_LOC_ROW ; move guard down LDY GUARD_LOC_ROW <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR1),Y CMP #SPRITE_PLAYER BNE .get_background_sprite LSR ALIVE ; set player to dead .get_background_sprite: LDA (PTR2),Y CMP #SPRITE_BRICK BNE .place_guard_at_loc LDA GUARD_GOLD_TIMER BPL .place_guard_at_loc ; What's above the guard? LDY GUARD_LOC_ROW DEY STY GAME_ROWNUM <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL STY GAME_COLNUM LDA (PTR2),Y CMP #SPRITE_EMPTY BEQ .drop_gold DEC GOLD_COUNT JMP .6e46 .drop_gold: LDA #SPRITE_GOLD STA (PTR1),Y STA (PTR2),Y JSR DRAW_SPRITE_PAGE2 LDY GAME_ROWNUM LDX GAME_COLNUM JSR GET_SCREEN_COORDS_FOR LDA #SPRITE_GOLD JSR DRAW_SPRITE_AT_PIXEL_COORDS .6e46 LDY GUARD_LOC_ROW <<set active row pointer [[PTR1]] for [[Y]]>> LDA #$00 STA GUARD_GOLD_TIMER LDY GUARD_LOC_COL .place_guard_at_loc LDA #SPRITE_GUARD STA (PTR1),Y JSR GET_GUARD_SPRITE_AND_COORDS JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP STORE_GUARD_DATA ; tailcall .check_gold_timer: CPY #$07 BCC .ladder JSR GET_GUARD_SPRITE_AND_COORDS JSR ERASE_SPRITE_AT_PIXEL_COORDS LDY GUARD_GOLD_TIMER LDA GUARD_X_ADJ_TABLE-7,Y ; GUARD_X_ADJ_TABLE[GUARD_GOLD_TIMER-7] STA GUARD_X_ADJ JSR GET_GUARD_SPRITE_AND_COORDS JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP STORE_GUARD_DATA ; tailcall ORG $6E85 .ladder LDX GUARD_LOC_COL LDY GUARD_LOC_ROW JSR DETERMINE_GUARD_MOVE ; Go to a guard movement function in the GUARD_FN_TABLE ASL TAY LDA GUARD_FN_TABLE+1,Y PHA LDA GUARD_FN_TABLE,Y PHA RTS
MOVE_GUARDADD_AND_UPDATE_SCORE, ALIVE, CHECK_FOR_GOLD_PICKED_UP_BY_GUARD, DETERMINE_GUARD_MOVE, DRAW_SPRITE_AT_PIXEL_COORDS, DRAW_SPRITE_PAGE2, ERASE_SPRITE_AT_PIXEL_COORDS, GAME_COLNUM, GAME_ROWNUM, GET_GUARD_SPRITE_AND_COORDS, GET_SCREEN_COORDS_FOR, GOLD_COUNT, GUARD_ANIM_STATE, GUARD_COUNT, GUARD_FACING_DIRECTION, GUARD_FN_TABLE, GUARD_GOLD_TIMER, GUARD_GOLD_TIMER_START_VALUE, GUARD_LOC_COL, GUARD_LOC_ROW, GUARD_NUM, GUARD_X_ADJ, GUARD_X_ADJ_TABLE, GUARD_Y_ADJ, LOAD_GUARD_DATA, LOAD_SOUND_DATA, NUDGE_GUARD_TOWARDS_EXACT_COLUMN, PTR1, PTR2, SCORE, STORE_GUARD_DATATo determine which direction a guard should move, all directions are checked for a candidate target, and the minimum distance from the candidate target to the player determines the move.
BEST_GUARD_DIST EQU $59
ORG $70D8 DETERMINE_GUARD_MOVE: SUBROUTINE STX TMP_GUARD_COL STY TMP_GUARD_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY TMP_GUARD_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .end_if_row_is_not_player_row LDA GUARD_GOLD_TIMER BEQ .end_if_row_is_not_player_row BMI .end_if_row_is_not_player_row LDA #GUARD_ACTION_MOVE_UP RTS .end_if_row_is_not_player_row: LDY TMP_GUARD_ROW CPY PLAYER_ROW BEQ .7100 JMP .end .7100: LDY TMP_GUARD_COL STY $57 CPY PLAYER_COL BCS .loop2 ; If TMP_GUARD_COL < PLAYER_COL: .loop: INC $57 LDY TMP_GUARD_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY $57 LDA (PTR2),Y CMP #SPRITE_LADDER BEQ .is_ladder_or_rope CMP #SPRITE_ROPE BEQ .is_ladder_or_rope LDY TMP_GUARD_ROW CPY #MAX_GAME_ROW BEQ .is_ladder_or_rope <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY $57 LDA (PTR2),Y CMP #SPRITE_EMPTY BEQ .end CMP #SPRITE_TRAP BEQ .end .is_ladder_or_rope: LDY $57 CPY PLAYER_COL BNE .loop ; PLAYER_COL == $57: LDA #GUARD_ACTION_MOVE_RIGHT RTS .loop2: DEC $57 LDY TMP_GUARD_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY $57 LDA (PTR2),Y CMP #SPRITE_LADDER BEQ .is_ladder_or_rope2 CMP #SPRITE_ROPE BEQ .is_ladder_or_rope2 LDY TMP_GUARD_ROW CPY #MAX_GAME_ROW BEQ .is_ladder_or_rope2 <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY $57 LDA (PTR2),Y CMP #SPRITE_EMPTY BEQ .end CMP #SPRITE_TRAP BEQ .end .is_ladder_or_rope2: LDY $57 CPY PLAYER_COL BNE .loop2 ; PLAYER_COL == $57: LDA #GUARD_ACTION_MOVE_LEFT RTS .end: LDA #GUARD_ACTION_DO_NOTHING STA GUARD_ACTION LDA #$FF STA BEST_GUARD_DIST LDX TMP_GUARD_COL LDY TMP_GUARD_ROW JSR DETERMINE_GUARD_LEFT_RIGHT_LIMITS JSR SHOULD_GUARD_MOVE_UP_OR_DOWN JSR SHOULD_GUARD_MOVE_LEFT JSR SHOULD_GUARD_MOVE_RIGHT LDA GUARD_ACTION RTS
DETERMINE_GUARD_MOVEGUARD_LEFT_COL_LIMIT EQU $5A GUARD_RIGHT_COL_LIMIT EQU $5B
ORG $743E DETERMINE_GUARD_LEFT_RIGHT_LIMITS: SUBROUTINE STX GUARD_LEFT_COL_LIMIT STX GUARD_RIGHT_COL_LIMIT STY ROWNUM .loop: LDA GUARD_LEFT_COL_LIMIT BEQ .loop2 LDY ROWNUM <<set active row pointer [[PTR1]] for [[Y]]>> LDY GUARD_LEFT_COL_LIMIT DEY LDA (PTR1),Y CMP #SPRITE_BRICK BEQ .loop2 CMP #SPRITE_STONE BEQ .loop2 CMP #SPRITE_LADDER BEQ .next CMP #SPRITE_ROPE BEQ .next LDY ROWNUM CPY #MAX_GAME_ROW BEQ .next <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY GUARD_LEFT_COL_LIMIT DEY LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .next CMP #SPRITE_STONE BEQ .next CMP #SPRITE_LADDER BNE .end_loop .next: DEC GUARD_LEFT_COL_LIMIT BPL .loop .end_loop: DEC GUARD_LEFT_COL_LIMIT .loop2: LDA GUARD_RIGHT_COL_LIMIT CMP #MAX_GAME_COL BEQ .end LDY ROWNUM <<set active row pointer [[PTR1]] for [[Y]]>> LDY GUARD_RIGHT_COL_LIMIT INY LDA (PTR1),Y CMP #SPRITE_BRICK BEQ .end CMP #SPRITE_STONE BEQ .end CMP #SPRITE_LADDER BEQ .next_loop2 CMP #SPRITE_ROPE BEQ .next_loop2 LDY ROWNUM CPY #MAX_GAME_ROW BEQ .next_loop2 <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY GUARD_RIGHT_COL_LIMIT INY LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .next_loop2 CMP #SPRITE_STONE BEQ .next_loop2 CMP #SPRITE_LADDER BNE .end_loop2 .next_loop2: INC GUARD_RIGHT_COL_LIMIT BPL .loop2 .end_loop2: INC GUARD_RIGHT_COL_LIMIT .end: RTS
DETERMINE_GUARD_LEFT_RIGHT_LIMITSORG $6FBC TRY_GUARD_MOVE_LEFT: SUBROUTINE LDY GUARD_LOC_ROW <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> LDX GUARD_X_ADJ CPX #$03 BCS .move_left ; horizontal adjustment > 0 ; horizontal adjustment <= 0 LDY GUARD_LOC_COL BEQ .store_guard_data ; Can't go any more left DEY LDA (PTR1),Y CMP #SPRITE_GUARD BEQ .store_guard_data CMP #SPRITE_STONE BEQ .store_guard_data CMP #SPRITE_BRICK BEQ .store_guard_data LDA (PTR2),Y CMP #SPRITE_TRAP BNE .move_left .store_guard_data: JMP STORE_GUARD_DATA ; tailcall .move_left: JSR GET_GUARD_SPRITE_AND_COORDS JSR ERASE_SPRITE_AT_PIXEL_COORDS JSR NUDGE_GUARD_TOWARDS_EXACT_ROW LDA #$FF STA GUARD_FACING_DIRECTION ; face left DEC GUARD_X_ADJ BPL .check_for_gold_pickup ; horizontal adjustment underflow JSR GUARD_DROP_GOLD LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .store_sprite LDA #SPRITE_EMPTY .store_sprite: STA (PTR1),Y DEC GUARD_LOC_COL DEY LDA (PTR1),Y CMP #SPRITE_PLAYER BNE .place_guard_sprite ; kill player LSR ALIVE .place_guard_sprite: LDA #SPRITE_GUARD STA (PTR1),Y LDA #$04 STA GUARD_X_ADJ ; horizontal adjustment = +2 BNE .determine_anim_set ; unconditional .check_for_gold_pickup: JSR CHECK_FOR_GOLD_PICKED_UP_BY_GUARD .determine_anim_set: LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_ROPE BEQ .rope LDA #$00 LDX #$02 BNE .inc_anim_state .rope: LDA #$03 LDX #$05 .inc_anim_state: JSR INC_GUARD_ANIM_STATE JSR GET_GUARD_SPRITE_AND_COORDS JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP STORE_GUARD_DATA ; tailcall
TRY_GUARD_MOVE_LEFTORG $7047 TRY_GUARD_MOVE_RIGHT: SUBROUTINE LDY GUARD_LOC_ROW <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> LDX GUARD_X_ADJ CPX #$02 BCC .move_right ; horizontal adjustment < 0 ; horizontal adjustment >= 0 LDY GUARD_LOC_COL CPY #MAX_GAME_COL BEQ .store_guard_data ; Can't go any more right INY LDA (PTR1),Y CMP #SPRITE_GUARD BEQ .store_guard_data CMP #SPRITE_STONE BEQ .store_guard_data CMP #SPRITE_BRICK BEQ .store_guard_data LDA (PTR2),Y CMP #SPRITE_TRAP BNE .move_right .store_guard_data: JMP STORE_GUARD_DATA ; tailcall .move_right: JSR GET_GUARD_SPRITE_AND_COORDS JSR ERASE_SPRITE_AT_PIXEL_COORDS JSR NUDGE_GUARD_TOWARDS_EXACT_ROW LDA #$01 STA GUARD_FACING_DIRECTION ; face right INC GUARD_X_ADJ LDA GUARD_X_ADJ CMP #$05 BCC .check_for_gold_pickup ; horizontal adjustment overflow JSR GUARD_DROP_GOLD LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .store_sprite LDA #SPRITE_EMPTY .store_sprite: STA (PTR1),Y INC GUARD_LOC_COL INY LDA (PTR1),Y CMP #SPRITE_PLAYER BNE .place_guard_sprite ; kill player LSR ALIVE .place_guard_sprite: LDA #SPRITE_GUARD STA (PTR1),Y LDA #$00 STA GUARD_X_ADJ ; horizontal adjustment = -2 BEQ .determine_anim_set ; unconditional .check_for_gold_pickup: JSR CHECK_FOR_GOLD_PICKED_UP_BY_GUARD .determine_anim_set: LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_ROPE BEQ .rope LDA #$07 LDX #$09 BNE .inc_anim_state .rope: LDA #$0A LDX #$0C .inc_anim_state: JSR INC_GUARD_ANIM_STATE JSR GET_GUARD_SPRITE_AND_COORDS JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP STORE_GUARD_DATA ; tailcall
TRY_GUARD_MOVE_RIGHTORG $6EA1 GUARD_DO_NOTHING: SUBROUTINE ; if GUARD_GOLD_TIMER > 0, GUARD_GOLD_TIMER++ LDA GUARD_GOLD_TIMER BEQ .store_guard_data BMI .store_guard_data INC GUARD_GOLD_TIMER .store_guard_data: JMP STORE_GUARD_DATA ORG $6EAC TRY_GUARD_MOVE_UP: SUBROUTINE LDY GUARD_Y_ADJ CPY #$03 BCS .move_up ; vertical adjustment > 0 LDY GUARD_LOC_ROW BEQ GUARD_DO_NOTHING DEY <<set active row pointer [[PTR1]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR1),Y CMP #SPRITE_BRICK BEQ GUARD_DO_NOTHING CMP #SPRITE_STONE BEQ GUARD_DO_NOTHING CMP #SPRITE_TRAP BEQ GUARD_DO_NOTHING CMP #SPRITE_GUARD BEQ GUARD_DO_NOTHING .move_up: JSR GET_GUARD_SPRITE_AND_COORDS JSR ERASE_SPRITE_AT_PIXEL_COORDS JSR NUDGE_GUARD_TOWARDS_EXACT_COLUMN LDY GUARD_LOC_ROW <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> DEC GUARD_Y_ADJ BPL TRY_GUARD_MOVE_UP_check_for_gold ; vertical adjustment underflow JSR GUARD_DROP_GOLD LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .set_active_sprite LDA #SPRITE_EMPTY .set_active_sprite: STA (PTR1),Y DEC GUARD_LOC_ROW LDY GUARD_LOC_ROW <<set active row pointer [[PTR1]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR1),Y CMP #SPRITE_PLAYER BNE .set_guard_sprite ; kill player LSR ALIVE .set_guard_sprite: LDA #SPRITE_GUARD STA (PTR1),Y LDA #$04 STA GUARD_Y_ADJ ; vertical adjust = +2 BNE TRY_GUARD_MOVE_UP_inc_anim_state ; unconditional TRY_GUARD_MOVE_UP_check_for_gold: JSR CHECK_FOR_GOLD_PICKED_UP_BY_GUARD TRY_GUARD_MOVE_UP_inc_anim_state: LDA #$0E LDX #$0F JSR INC_GUARD_ANIM_STATE JSR GET_GUARD_SPRITE_AND_COORDS JSR DRAW_SPRITE_AT_PIXEL_COORDS JMP STORE_GUARD_DATA
GUARD_DO_NOTHING, TRY_GUARD_MOVE_UPORG $6F39 TRY_GUARD_MOVE_DOWN: SUBROUTINE LDY GUARD_Y_ADJ CPY #$02 BCC .move_down ; vertical adjustment < 0 LDY GUARD_LOC_ROW CPY #MAX_GAME_ROW BCS .store_guard_data INY <<set active row pointer [[PTR1]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR1),Y CMP #SPRITE_STONE BEQ .store_guard_data CMP #SPRITE_GUARD BEQ .store_guard_data CMP #SPRITE_BRICK BNE .move_down .store_guard_data: JMP STORE_GUARD_DATA .move_down: JSR GET_GUARD_SPRITE_AND_COORDS JSR ERASE_SPRITE_AT_PIXEL_COORDS JSR NUDGE_GUARD_TOWARDS_EXACT_COLUMN LDY GUARD_LOC_ROW <<set active and background row pointers [[PTR1]] and [[PTR2]] for [[Y]]>> INC GUARD_Y_ADJ LDA GUARD_Y_ADJ CMP #$05 BCC .check_for_gold ; vertical adjustment overflow JSR GUARD_DROP_GOLD LDY GUARD_LOC_COL LDA (PTR2),Y CMP #SPRITE_BRICK BNE .set_active_sprite LDA #SPRITE_EMPTY .set_active_sprite: STA (PTR1),Y INC GUARD_LOC_ROW LDY GUARD_LOC_ROW <<set active row pointer [[PTR1]] for [[Y]]>> LDY GUARD_LOC_COL LDA (PTR1),Y CMP #SPRITE_PLAYER BNE .set_guard_sprite ; kill player LSR ALIVE .set_guard_sprite: LDA #SPRITE_GUARD STA (PTR1),Y LDA #$00 STA GUARD_Y_ADJ ; vertical adjust = -2 JMP TRY_GUARD_MOVE_UP_inc_anim_state .check_for_gold: JMP TRY_GUARD_MOVE_UP_check_for_gold
TRY_GUARD_MOVE_DOWNThis routine is called whenever we move a guard and the horizontal or vertical
adjustment under- or overflows. If and only if GUARD_GOLD_TIMER is zero,
decrement GUARD_GOLD_TIMER, and if there is nothing at the guard location,
then drop gold at the location.
ORG $753E GUARD_DROP_GOLD: SUBROUTINE LDA GUARD_GOLD_TIMER BPL .end INC GUARD_GOLD_TIMER BNE .end ; GUARD_GOLD_TIMER == 0 LDY GUARD_LOC_ROW STY GAME_ROWNUM <<set background row pointer [[PTR2]] for [[Y]]>> LDY GUARD_LOC_COL STY GAME_COLNUM LDA (PTR2),Y CMP #SPRITE_EMPTY BNE .decrement_flag_0 ; Put gold at location LDA #SPRITE_GOLD STA (PTR2),Y JSR DRAW_SPRITE_PAGE2 LDY GAME_ROWNUM LDX GAME_COLNUM JSR GET_SCREEN_COORDS_FOR LDA #SPRITE_GOLD JMP DRAW_SPRITE_AT_PIXEL_COORDS .decrement_flag_0: DEC GUARD_GOLD_TIMER .end: RTS
The PSUEDO_DISTANCE returns a distance measure between the player and the given A, X
coordinate based on whether the point is above, below, or on the same row as the player row.
If the point is on the same row as the player, then the return value is the
horizontal distance between the current guard and the point. Otherwise, if the point is above
the player row, return 200 plus the vertical distance between the point and the player.
Otherwise, the point is below the player row, so return 100 plus the vertical distance between
the point and the player.
ORG $72D4 PSEUDO_DISTANCE: SUBROUTINE STA TMP CMP PLAYER_ROW BNE .tmp_not_player_row ; TMP == PLAYER_ROW ; return | X - GUARD_LOC_COL | CPX GUARD_LOC_COL BCC .x_lt_guard_col ; X >= GUARD_LOC_COL TXA ; A = X - GUARD_LOC_COL SEC SBC GUARD_LOC_COL RTS .x_lt_guard_col: STX TMP ; A = GUARD_LOC_COL - X LDA GUARD_LOC_COL SEC SBC TMP RTS .tmp_not_player_row: ; If TMP >= PLAYER_ROW, return 200 + | TMP - PLAYER_ROW | ; otherwise return 100 + | TMP - PLAYER_ROW | BCC .tmp_lt_player_row ; TMP >= PLAYER_ROW ; A = TMP - PLAYER_ROW + 200 SEC SBC PLAYER_ROW CLC ADC #200 RTS .tmp_lt_player_row ; A = PLAYER_ROW - TMP + 100 LDA PLAYER_ROW SEC SBC TMP CLC ADC #100 RTS
ORG $71A1 SUBROUTINE .return: RTS SHOULD_GUARD_MOVE_LEFT: LDY GUARD_LEFT_COL_LIMIT CPY TMP_GUARD_COL BEQ .return LDY TMP_GUARD_ROW CPY #MAX_GAME_ROW BEQ .check_here ; Check below: ; Get background sprite at TMP_GUARD_ROW + 1, col = GUARD_LEFT_COL_LIMIT <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY GUARD_LEFT_COL_LIMIT LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .check_here CMP #SPRITE_STONE BEQ .check_here LDX GUARD_LEFT_COL_LIMIT LDY TMP_GUARD_ROW JSR GUARD_FIND_CANDIDATE_ROW_BELOW LDX GUARD_LEFT_COL_LIMIT JSR PSEUDO_DISTANCE CMP BEST_GUARD_DIST BCS .check_here ; dist >= BEST_GUARD_DIST? ; dist < BEST_GUARD_DIST STA BEST_GUARD_DIST ; dist LDA #GUARD_ACTION_MOVE_LEFT STA GUARD_ACTION .check_here: LDY TMP_GUARD_ROW BEQ .next <<set background row pointer [[PTR2]] for [[Y]]>> LDY GUARD_LEFT_COL_LIMIT LDA (PTR2),Y CMP #SPRITE_LADDER BNE .next ; Ladder here LDY TMP_GUARD_ROW LDX GUARD_LEFT_COL_LIMIT JSR GUARD_FIND_CANDIDATE_ROW_ABOVE LDX GUARD_LEFT_COL_LIMIT JSR PSEUDO_DISTANCE CMP BEST_GUARD_DIST BCS .next ; dist >= BEST_GUARD_DIST? ; dist < BEST_GUARD_DIST STA BEST_GUARD_DIST ; dist LDA #GUARD_ACTION_MOVE_LEFT STA GUARD_ACTION .next: INC GUARD_LEFT_COL_LIMIT JMP SHOULD_GUARD_MOVE_LEFT
SHOULD_GUARD_MOVE_LEFTORG $720B SUBROUTINE .return: RTS SHOULD_GUARD_MOVE_RIGHT: LDY GUARD_RIGHT_COL_LIMIT CPY TMP_GUARD_COL BEQ .return LDY TMP_GUARD_ROW CPY #MAX_GAME_ROW BEQ .check_here ; Check below: ; Get background sprite at TMP_GUARD_ROW + 1, col = GUARD_RIGHT_COL_LIMIT <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY GUARD_RIGHT_COL_LIMIT LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .check_here CMP #SPRITE_STONE BEQ .check_here LDX GUARD_RIGHT_COL_LIMIT LDY TMP_GUARD_ROW JSR GUARD_FIND_CANDIDATE_ROW_BELOW LDX GUARD_RIGHT_COL_LIMIT JSR PSEUDO_DISTANCE CMP BEST_GUARD_DIST BCS .check_here ; dist >= BEST_GUARD_DIST? ; dist < BEST_GUARD_DIST STA BEST_GUARD_DIST ; dist LDA #GUARD_ACTION_MOVE_RIGHT STA GUARD_ACTION .check_here: LDY TMP_GUARD_ROW BEQ .next <<set background row pointer [[PTR2]] for [[Y]]>> LDY GUARD_RIGHT_COL_LIMIT LDA (PTR2),Y CMP #SPRITE_LADDER BNE .next ; Ladder here LDY TMP_GUARD_ROW LDX GUARD_RIGHT_COL_LIMIT JSR GUARD_FIND_CANDIDATE_ROW_ABOVE LDX GUARD_RIGHT_COL_LIMIT JSR PSEUDO_DISTANCE CMP BEST_GUARD_DIST BCS .next ; dist >= BEST_GUARD_DIST? ; dist < BEST_GUARD_DIST STA BEST_GUARD_DIST ; dist LDA #GUARD_ACTION_MOVE_RIGHT STA GUARD_ACTION .next: DEC GUARD_RIGHT_COL_LIMIT JMP SHOULD_GUARD_MOVE_RIGHT
SHOULD_GUARD_MOVE_RIGHTORG $7275 SHOULD_GUARD_MOVE_UP_OR_DOWN: SUBROUTINE LDY TMP_GUARD_ROW CPY #MAX_GAME_ROW BEQ .should_guard_move_up <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY TMP_GUARD_COL LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .should_guard_move_up CMP #SPRITE_STONE BEQ .should_guard_move_up LDX TMP_GUARD_COL LDY TMP_GUARD_ROW JSR GUARD_FIND_CANDIDATE_ROW_BELOW LDX TMP_GUARD_COL JSR PSEUDO_DISTANCE CMP BEST_GUARD_DIST BCS .should_guard_move_up STA BEST_GUARD_DIST LDA #GUARD_ACTION_MOVE_DOWN STA GUARD_ACTION .should_guard_move_up: LDY TMP_GUARD_ROW BEQ .end <<set background row pointer [[PTR2]] for [[Y]]>> LDY TMP_GUARD_COL LDA (PTR2),Y CMP #SPRITE_LADDER BNE .end ; ladder LDX TMP_GUARD_COL LDY TMP_GUARD_ROW JSR GUARD_FIND_CANDIDATE_ROW_ABOVE LDX TMP_GUARD_COL JSR PSEUDO_DISTANCE CMP BEST_GUARD_DIST BCS .end STA BEST_GUARD_DIST LDA #GUARD_ACTION_MOVE_UP STA GUARD_ACTION .end: RTS
SHOULD_GUARD_MOVE_UP_OR_DOWNCHECK_CURR_TMP_ROW EQU $5C CHECK_TMP_COL EQU $5D CHECK_TMP_ROW EQU $5E
The GUARD_FIND_CANDIDATE_ROW_BELOW is called when determining if the guard should move down. It scans below
the guard for a candidate row. Upon entry, we store X and Y in CHECK_TMP_COL and CHECK_TMP_ROW.
Next, we scan from CHECK_TMP_ROW to the bottommost game row.
For each row:
- If the background sprite below the test coordinate is brick or stone, return
CHECK_TMP_ROW. - Otherwise, if the sprite below the test coordinate is not empty:
- If we're not all the way to the left:
- If there's a rope to the left, or if there's a brick, stone, or ladder below left then if this is below or on the same row as the
PLAYER_ROW, returnCHECK_TMP_ROW.
- If there's a rope to the left, or if there's a brick, stone, or ladder below left then if this is below or on the same row as the
- If we're not all the way to the right:
- If there's a rope to the right, or if there's a brick, stone, or ladder below right then if this is below or on the same row as the
PLAYER_ROW, returnCHECK_TMP_ROW.
- If there's a rope to the right, or if there's a brick, stone, or ladder below right then if this is below or on the same row as the
- If we're not all the way to the left:
And if we haven't returned in the loop, just return the MAX_GAME_ROW.
ORG $739A SUBROUTINE .return_tmp_row: LDA CHECK_TMP_ROW RTS GUARD_FIND_CANDIDATE_ROW_BELOW: STY CHECK_TMP_ROW STX CHECK_TMP_COL ; for CHECK_TMP_ROW = Y; CHECK_TMP_ROW <= MAX_GAME_ROW; CHECK_TMP_ROW++ .loop: ; if background sprite below tmp coords is brick or stone, return tmp row. <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY CHECK_TMP_COL LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .return_tmp_row CMP #SPRITE_STONE BEQ .return_tmp_row ; Not brick or stone below ; if background sprite at tmp coords is empty, then next tmp row. LDY CHECK_TMP_ROW <<set background row pointer [[PTR2]] for [[Y]]>> LDY CHECK_TMP_COL LDA (PTR2),Y CMP #SPRITE_EMPTY BEQ .next CPY #$00 BEQ .check_right ; cannot check to left ; if background sprite to left of tmp coords is rope, ; then tmp_row -> curr_tmp_row DEY LDA (PTR2),Y ; Check to left CMP #SPRITE_ROPE BEQ .store_as_curr_tmp_row ; if background sprite to left and below tmp coords is brick, stone, or ladder, ; then tmp_row -> curr_tmp_row LDY CHECK_TMP_ROW <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY CHECK_TMP_COL DEY LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .store_as_curr_tmp_row CMP #SPRITE_STONE BEQ .store_as_curr_tmp_row CMP #SPRITE_LADDER BNE .check_right ; Otherwise check right .store_as_curr_tmp_row: ; Store tmp row as curr tmp row, and if at or below player, return curr tmp row. LDY CHECK_TMP_ROW STY CHECK_CURR_TMP_ROW CPY PLAYER_ROW BCS .return_curr_tmp_row ; CHECK_TMP_ROW < PLAYER_ROW .check_right: LDY CHECK_TMP_COL CPY #MAX_GAME_COL BCS .next ; can't check right ; if background sprite to right is rope, ; then tmp_row -> curr_tmp_row INY LDA (PTR2),Y CMP #SPRITE_ROPE BEQ .store_as_curr_tmp_row_2 ; if background sprite to right and below tmp coords is brick, stone, or ladder, ; then tmp_row -> curr_tmp_row LDY CHECK_TMP_ROW <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY CHECK_TMP_COL INY LDA (PTR2),Y CMP #SPRITE_BRICK BEQ .store_as_curr_tmp_row_2 CMP #SPRITE_LADDER BEQ .store_as_curr_tmp_row_2 CMP #SPRITE_STONE BNE .next ; Brick, ladder, or stone. .store_as_curr_tmp_row_2: LDY CHECK_TMP_ROW STY CHECK_CURR_TMP_ROW CPY PLAYER_ROW BCS .return_curr_tmp_row ; CHECK_TMP_ROW < PLAYER_ROW .next: INC CHECK_TMP_ROW LDY CHECK_TMP_ROW CPY #MAX_GAME_ROW+1 BCS .return_max_game_row JMP .loop .return_max_game_row: LDA #MAX_GAME_ROW RTS .return_curr_tmp_row: LDA CHECK_CURR_TMP_ROW RTS
GUARD_FIND_CANDIDATE_ROW_BELOWORG $72FD SUBROUTINE .not_ladder: LDA CHECK_TMP_ROW ; row RTS GUARD_FIND_CANDIDATE_ROW_ABOVE: STY CHECK_TMP_ROW ; row STX CHECK_TMP_COL ; col .loop: <<set background row pointer [[PTR2]] for [[Y]]>> LDY CHECK_TMP_COL ; col LDA (PTR2),Y ; sprite on background CMP #SPRITE_LADDER BNE .not_ladder ; no ladder at row, col -> just return row. ; There is a ladder at row, col DEC CHECK_TMP_ROW ; row-- ; up one LDY CHECK_TMP_COL ; col BEQ .at_leftmost DEY ; to left (col-1) LDA (PTR2),Y ; To left of ladder is brick, stone, or ladder: .blocked_on_left CMP #SPRITE_BRICK BEQ .blocked_on_left CMP #SPRITE_STONE BEQ .blocked_on_left CMP #SPRITE_LADDER BEQ .blocked_on_left LDY CHECK_TMP_ROW ; row (that is now up one) <<set background row pointer [[PTR2]] for [[Y]]>> LDY CHECK_TMP_COL ; col DEY LDA (PTR2),Y ; sprite on background CMP #SPRITE_ROPE BNE .at_leftmost ; There is a rope above the ladder .blocked_on_left: ; If row <= PLAYER_ROW (on or above player row), return row LDY CHECK_TMP_ROW ; row STY SCRATCH_5C CPY PLAYER_ROW BCC .return_scratch_5C BEQ .return_scratch_5C .at_leftmost: LDY CHECK_TMP_COL ; col CPY #MAX_GAME_COL BEQ .at_rightmost ; Look at background sprite below and to the right LDY CHECK_TMP_ROW ; row <<set background row pointer [[PTR2]] for [[Y+1]]>> LDY CHECK_TMP_COL INY LDA (PTR2),Y ; get background sprite at row+1, col+1 ; Below and to the right of ladder is brick, stone, or ladder: .blocked_below CMP #SPRITE_BRICK BEQ .blocked_below CMP #SPRITE_STONE BEQ .blocked_below CMP #SPRITE_LADDER BEQ .blocked_below ; Look at background sprite to the right LDY CHECK_TMP_ROW ; row <<set background row pointer [[PTR2]] for [[Y]]>> LDY CHECK_TMP_COL ; col INY LDA (PTR2),Y ; get background sprite at row, col+1 CMP #SPRITE_ROPE BNE .at_rightmost ; There is a rope to the right of the ladder .blocked_below: ; If row <= PLAYER_ROW (on or above player row), return row LDY CHECK_TMP_ROW ; row STY SCRATCH_5C CPY PLAYER_ROW BCC .return_scratch_5C BEQ .return_scratch_5C .at_rightmost: ; If row < 1, return row, otherwise loop. LDY CHECK_TMP_ROW ; row CPY #$01 BCC .return_Y JMP .loop .return_Y: TYA RTS .return_scratch_5C: LDA SCRATCH_5C RTS
GUARD_FIND_CANDIDATE_ROW_ABOVE