PlayerSetup 

The first subroutine (PlayerSetup) defines the background color, border color, and the sprite multicolors. Then the sprite to character position is initialized. The variable (PARAM1) stores the X register. Then we set zero in the X register to point to Sprite 0. The variable (PARAM2) manages the Y position. Finally the subroutine (SpriteToCharPos) is called that sets up this position, saving the X/Y newly defined values, and stores them in the Sprite(s)’s position.

After this another similar set of parameters is defined for Sprite 1 which manages the X/Y positions. Both of these calls together created the top/bottom of the Sprite (head down to the legs). Here’s a description:

Sprite 0:
PARAM1 = X Position
X Register = Sprite (X)
PARAM2 = Y Position

Next the variable (PLAYER_STATE_IDLE) is loaded and a call is made to the subroutine (ChangePlayerState). This is used to call the main routine “PlayerStateIdle”, which monitors the Sprite’s joystick movement, idle animation, and monitors the Player’s gravity.

The last part actives the Sprite by setting bit 1 of variable (SPRITE_IS_ACTIVE) and (SPRITE_IS_ACTIVE + 1). Finally the variable (PLAYER_FALLCOUNT) resets a count that manages the Player falling in midair.

;------------------------------------------------------------------------------ 
; PLAYER SETUP 
;------------------------------------------------------------------------------ 
; PLAYER has a strange setup as it's ALWAYS going to be using sprites 0 and 1 
; As well as always being 'active' (used) 
;------------------------------------------------------------------------------ 
PlayerSetup
lda #COLOR_BLACK 
sta VIC_BACKGROUND_COLOR 
sta VIC_BORDER_COLOR 
lda #%00000011 ; Turn on multicolor for sprites 0 and 1 
sta VIC_SPRITE_MULTICOLOR ; also turn all others to single color 
lda #COLOR_BLACK 
sta VIC_SPRITE_MULTICOLOR_1 ; Set sprite shared multicolor 1 to brown 
lda #COLOR_LTRED 
sta VIC_SPRITE_MULTICOLOR_2 ; set sprite shared multicolor 2 to 'pink' 
lda #COLOR_YELLOW 
sta VIC_SPRITE_COLOR ; set sprite 0 color to yellow 
lda #COLOR_BLUE 
sta VIC_SPRITE_COLOR + 1 ; set sprite 1 color to orange (bkground sprite) 
;------------------------------------------------------------------------------ 
; We now use a system that tracks the sprite position in character coords on 
; the screen, so to avoid costly calculations every frame, we set the sprite 
; to a character border initially and track all movement from there. That way 
; we need only do this set of calculations once in the lifetime of the Player. 
; 
; To initially place the sprite, we use 'SpriteToCharPos' 
;------------------------------------------------------------------------------ 
; Sprite X position 
lda #19 
sta PARAM1 ; Sprite Y0 Head 
ldx #0 
lda #3 
sta PARAM2 
jsr SpriteToCharPos ; Sprite Y1 Legs 

ldx #1
lda #5
sta PARAM2
jsr
SpriteToCharPos

lda #PLAYER_STATE_IDLE ; Set initial state (idle)
jsr ChangePlayerState
;——————————– This will be relevant later
; Trim for better collisions

lda #0 
sta SPRITE_DELTA_TRIM_X ; Not used 
;-------------------------------- Set Player Mob active 
lda #1 
sta SPRITE_IS_ACTIVE ; Set sprite 0 to active 
sta SPRITE_IS_ACTIVE + 1 ; Set sprite 1 to active 
lda #0 ; reset the player fallcount 
sta PLAYER_FALLCOUNT 
rts

UpdatePlayer

The subroutine (UpdatePlayer) manages all of the Player (Sprite’s) boundary limits when moving close to the map’s edge. The constants are listed and defined as follows before getting into the subroutine.

PLAYER_RIGHT_CAP = Defines how far the Sprite walks to the right before scrolling the screen..
PLAYER_LEFT_CAP = Defines how far the Sprite walks to the left before scrolling the screen.
PLAYER_UP_CAP = Defines how far the Sprite can walk up before scrolling the screen.
PLAYER_DOWN_CAP = Defines how far the Sprite can walk down before scrolling the screen.

The variable (SPRITE_IS_ACTIVE) checks if the Sprite is still alive. If the Sprite is not dead yet, then a call is made to animate the Sprite using (AnimateSprite) and a call is made to (UpdatePlayerState).

;=================================================================================================== 
; UPDATE PLAYER 
;--------------------------------------------------------------------------------------------------- 
; Update the player. Joystick controls are updated via interrupt so we read the values from JOY_X 
; and JOY_Y 
;--------------------------------------------------------------------------------------------------- 
PLAYER_RIGHT_CAP = $
1c ; Sprite movement caps - at this point we don't 
PLAYER_LEFT_CAP = $09 ; Move the sprite, we scroll the screen 
PLAYER_UP_CAP = $04 
PLAYER_DOWN_CAP = $0F
UpdatePlayer 
; Only update the player if it's active 
lda SPRITE_IS_ACTIVE ; check against sprite #0 - is it active? 
bne @update 
rts
@update
ldx #0 
jsr AnimateSprite ; Animate sprite 
jsr UpdatePlayerState ; Update player by state 
rts

JoystickReady

;=================================================================================================
; JOYSTICK / PLAYER MOVE  ;=================================================================================================
; JOYSTICK READY
;—————————————————————————————————
; There are times atm when we have to ignore joystick input so the scrolling can ‘catch up’ after ; movement stops. Usually for a couple of frames.
;
; Returns A : 0 = ready 1 = not ready
;
; Modifies A ;—————————————————————————————————

In the subroutine (JoystickReady) scrolling functions are manages. A “1” is saved here if the joystick has not moved yet and zero means it has movement. The variables are defined below:

SCROLL_MOVING = Checks if the screen is still scrolling or not
SCROLL_DIRECTION = Checks for the direction the Player is moving in
SCROLL_STOP = The scrolling has stopped. Check if it needs to move again.

;=================================================================================================== 
; JOYSTICK / PLAYER MOVE 
;=================================================================================================== 
; The old system of joystick movement was going to become very unweildy very fast and not be very 
; good for expanding what the player can do. I'm trying a new system where the routines are broken 
; down and input is checked in individual states for what the player can do at any given time. 
; The movement routines will then be broken down and called as needed by the states. 
; Since the old system didn't actually read the joystick or scroll the screen (it read / set 
; variables by routines that do) - this SHOULD be fairly workable. 
;--------------------------------------------------------------------------------------------------- 
;=================================================================================================== 
; JOYSTICK READY 
;--------------------------------------------------------------------------------------------------- 
; There are times atm when we have to ignore joystick input so the scrolling can 'catch up' after 
; movement stops. Usually for a couple of frames. 
; 
; Returns A : 0 = ready 1 = not ready 
; 
; Modifies A 
;--------------------------------------------------------------------------------------------------- 
JoystickReady
lda SCROLL_MOVING ; if moving is 'stopped' we can test joystick 
beq @joyready ; if it's moving but direction is stopped, we're 'fixing' 
lda SCROLL_DIRECTION 
bne @joyready 
lda #1 ; Send code for joystick NOT ready for input 
rts

@joyready

lda #SCROLL_STOP ; reset scroll direction - if it needs to scroll 
sta SCROLL_DIRECTION ; it will be updated 
lda #0 ; send code for joystick ready 
rts

MovePlayerRight

For the (MovePlayerRight) subroutine the variable (SCROLL_FIX_SKIP) is cleared first. This handles a left pixel scroll issue that is used to skip a frame in the subroutine (UpdateScroll – found in the file scrolling.asm).

After this the variable (SPRITE_CHAR_POS_X) checks for a PLAYER_RIGHT_CAP for a right movement of the Player.

;=================================================================================================== 
; MOVE PLAYER RIGHT 
;--------------------------------------------------------------------------------------------------- 
; Move the player one pixel to the right if possible, taking into account scrolling, map limits 
; and collision detection against the screen ; ; Returns A: any blocking or special character to the right, or 0 if clear 
; 
;--------------------------------------------------------------------------------------------------- 
MovePlayerRight
lda #0 
sta SCROLL_FIX_SKIP 
;------------------------------------------ CHECK RIGHT MOVEMENT CAP 
clc ; clear carry flag because I'm paranoid 
lda SPRITE_CHAR_POS_X ; load the sprite char X position 
cmp #PLAYER_RIGHT_CAP ; check against the right edge of the screen 
bcc @rightMove ; if X char pos < cap - move the sprite, else scroll 
; Check against map edge

A check is made to the variable (MAP_X_POS) to see if the map needs to scroll to the right and it also checks the delta with the variable (MAP_X_DELTA).

If the variable (MAP_X_DELTA) is equal to zero then the variable (SCROLL_FIX_SKIP) is set.

lda MAP_X_POS ; load the current MAP X Position  
cmp #54 ; the map is 64 tiles wide, the screen is 10 tiles wide  
bne @scrollRight 
lda MAP_X_DELTA ; each tile is 4 characters wide (0-3)  
cmp #1 ; if we hit this limit we don't scroll (or move)  
bne @scrollRight ;at this point we will revert to move  
lda #1  
sta SCROLL_FIX_SKIP 
jmp @rightMove 
rts 

Since the variable (MAP_X_DELTA) is not at zero yet then we perform a call to (CheckMoveRight) to see if the Player can move to the right (are they blocked by a tile in the way?). A zero return here means a collision occurred. Then the variable (SCROLL_RIGHT) is cast into (SCROLL_DIRECTION) and (SCROLL_MOVING). Finally the subroutine returns a clear code in the accumulator.

;------------------------------------------ SCROLL RIGHT 
; Pre-scroll check
@scrollRight
ldx #0 
jsr CheckMoveRight ; Collision check against characters 
beq @scroll ; TODO - return the collision code here 
rts ; Setup for the scroll 

@scroll

lda #SCROLL_RIGHT ; Set the direction for scroll and post scroll checks 
sta SCROLL_DIRECTION 
sta SCROLL_MOVING 
lda #0 ; load 'clear code' 
rts ; TODO - ensure collision code is returned 
;----------------------------------------- MOVE SPRITE RIGHT

Check to see if the Player can move right using the subroutine (CheckMoveRight). If it’s not zero then a collision was found so we can’t move.

If no right collision was found then call a subroutine to (MoveSpriteRight – twice). Then set a clear code.

@rightMove

ldx #0 
jsr CheckMoveRight ; Check ahead for character collision  
bne @rightDone

@moveRight

ldx #0 
jsr MoveSpriteRight ; Move sprites one pixel right 
ldx #1 
jsr MoveSpriteRight 
lda #0 ; move code 'clear'

@rightDone

rts

MovePlayerLeft

For the (MovePlayerLeft) subroutine the variable (SCROLL_FIX_SKIP) is cleared first. This handles a left pixel scroll issue that is used to skip a frame in the subroutine (UpdateScroll – found in the file scrolling.asm).

After this the variable (SPRITE_CHAR_POS_X) checks for a PLAYER_LEFT_CAP for a left movement of the Player.

;=================================================================================================== 
; MOVE PLAYER LEFT 
;--------------------------------------------------------------------------------------------------- 
; Move the player one pixel to the left if possible, taking into account scrolling, map limits 
; and collision detection against the screen 
; 
; Returns A: any blocking or special character to the right, or 0 if clear 
;--------------------------------------------------------------------------------------------------- 
MovePlayerLeft
lda #0 ; Make sure scroll 'fix' is on 
sta SCROLL_FIX_SKIP ;---------------------------------------- CHECK MOVEMENT CAP ($07) 
lda SPRITE_CHAR_POS_X ; Check for left side movement cap 
cmp #PLAYER_LEFT_CAP 
bcs @leftMove ; if below cap, we move the sprite 

A check is made to the variable (MAP_X_POS) to see if the map needs to scroll to the left and it also checks the delta with the variable (MAP_X_DELTA).

If the variable (MAP_X_DELTA) is equal to zero then the variable (SCROLL_FIX_SKIP) is set. Then the variable (SPRITE_POS_X)) is check to see if the Sprite has moved to the left edge of the screen.

; Otherwise we prepare to scroll  
; Check for edge of map for scrolling 
lda MAP_X_POS ; Check for map pos X = 0 
bne @scrollLeft 
lda MAP_X_DELTA ; check for map delta = 0 
bne @scrollLeft ; We're at the maps left edge 
; So we revert to sprite movement once more 
lda #1 
sta SCROLL_FIX_SKIP 
lda SPRITE_POS_X,x ; Check for sprite pos > 0 (not sprite char pos) 
bpl @leftMove ; so we could walk to the edge of screen 
rts

Since the variable (MAP_X_DELTA) is not at zero yet then we perform a call to (CheckMoveLeft) to see if the Player can move to the left (are they blocked by a tile in the way?). A zero return here means a collision occurred. Then the variable (SCROLL_LEFT) is cast into (SCROLL_DIRECTION) and (SCROLL_MOVING). Finally the subroutine returns a clear code in the accumulator.

@scrollLeft 
;--------------------------------------- SCROLL SCREEN FOR LEFT MOVE 
ldx #0 
jsr CheckMoveLeft ; check for character collision to the left 
beq @scroll 
rts ; TODO - return collision code
@scroll 
lda #SCROLL_LEFT 
sta SCROLL_DIRECTION 
sta SCROLL_MOVING 
lda #0 ; return 'clear code' ; TODO - return clear collision code 
rts ;---------------------------------------- MOVE THE PLAYER LEFT ONE PIXEL

Check to see if the Player can move left using the subroutine (CheckMoveLeft). If it’s not zero then a collision was found so we can’t move.

If no left collision was found then call a subroutine to (MoveSpriteLeft – twice). Then set a clear code.

@leftMove
ldx #0 
jsr CheckMoveLeft ; check for collisions with characters 
bne @leftDone ; TODO return collision code
@moveLeft
ldx #0 
jsr MoveSpriteLeft 
ldx #1 
jsr MoveSpriteLeft 
lda #0 ; move code 'clear'
@leftDone 
rts

MovePlayerDown

Check the variable (SPRITE_CHAR_POS_Y) against the variable (PLAYER_DOWN_CAP) to see if the Player has not reached a boundary yet and can still move down.

;=================================================================================================== 
; MOVE PLAYER DOWN 
;--------------------------------------------------------------------------------------------------- 
; Move the player one pixel down if possible, taking into account scrolling, map limits 
; and collision detection against the screen 
; 
; Returns A: any blocking or special character below, or 0 if clear 
; 
; Modifies X 
;--------------------------------------------------------------------------------------------------- 
MovePlayerDown
clc 
lda SPRITE_CHAR_POS_Y 
cmp #PLAYER_DOWN_CAP 
bcc @downMove

A check is then made to the variable (MAP_Y_POS) to see if the screen is ready to scroll down after the Player has reached a boundary and it also checks the delta with the variable (MAP_Y_DELTA) to see if a delta was range was reached.

lda MAP_Y_POS 
cmp #$1B  
bne @downScroll 
lda MAP_Y_DELTA 
cmp #02  
bcc @downScroll 
rts

Since the variable (MAP_Y_DELTA) is not at zero yet then we perform a call to (CheckMoveDown) to see if the Player can move down (are they blocked by a tile in the way?) A zero here means a collision occurred. Then the variable (SCROLL_DOWN) is cast into (SCROLL_DIRECTION) and (SCROLL_MOVING). Finally a subroutine returns a clear code in the accumulator.

@downScroll 
ldx #0 ; Check Sprite #0 
jsr CheckMoveDown ; returns: 0 = can move : 1 = blocked 
beq @scroll ; We are not blocked = 0 
rts ; return with contents of collison routine
@scroll 
lda #SCROLL_DOWN 
sta SCROLL_DIRECTION 
sta SCROLL_MOVING 
lda #0 ; return a clear collision code 
rts

Check to see if the Player can move down using the subroutine (CheckMoveDown). If it’s not zero then a collision was found so we can’t move.
If no down collision was found then call a subroutine to (MoveSpriteDown – twice). Then set a clear code.

@downMove
ldx #0 ; Check Sprite #0 
jsr CheckMoveDown ; returns: 0 = can move : 1 = blocked 
bne @downDone ; retun with contents of collision code 
jsr MoveSpriteDown ; = 0 so we can move the Sprite Down 
ldx #1 
jsr MoveSpriteDown 
lda #0 ; return with clear code
@downDone 
rts

MovePlayerUp

Check the variable (SPRITE_CHAR_POS_Y) against the variable (PLAYER_UP_CAP) to see if the Player has not reached a boundary yet and can still move up.

;=================================================================================================== 
; MOVE PLAYER UP 
;--------------------------------------------------------------------------------------------------- 
; Move the player one pixel up if possible, taking into account scrolling, map limits 
; and collision detection against the screen 
; 
; Returns A: any blocking or special character below, or 0 if clear 
;--------------------------------------------------------------------------------------------------- 
MovePlayerUp
sec 
lda SPRITE_CHAR_POS_Y 
cmp #PLAYER_UP_CAP 
bcs @upMove

A check is then made to the variable (MAP_Y_POS) to see if the screen is ready to scroll down after the Player has reached a boundary and it also checks the delta with the variable (MAP_Y_DELTA) to see if a delta was range was reached.

lda MAP_Y_POS 
bne @upScroll 
clc 
lda MAP_Y_DELTA 
cmp #1 
bcs @upScroll 
rts

Since the variable (MAP_Y_DELTA) is not at zero yet then we perform a call to (CheckMoveUp) to see if the Player can move up (are they blocked by a tile in the way?) A zero here means a collision occurred. Then the variable (SCROLL_UP) is cast into (SCROLL_DIRECTION) and (SCROLL_MOVING). Finally a subroutine returns a clear code in the accumulator.

@upScroll
ldx #0 
jsr CheckMoveUp 
beq @scroll 
rts
@scroll 
lda #SCROLL_UP 
sta SCROLL_DIRECTION 
sta SCROLL_MOVING 
rts

Check to see if the Player can move up using the subroutine (CheckMoveUp). If it’s not zero then a collision was found so we can’t move.
If no up collision was found then call a subroutine to (MoveSpriteUp – twice). Then set a clear code.


@upMove
ldx #0 ; Check Sprite 0 (head/body) 
jsr CheckMoveUp 
bne @upDone
jsr MoveSpriteUp 
ldx #1 
jsr MoveSpriteUp
@upDone 
rts

ApplyGravity

Check to see if there is right movement and save a result in the variable (JOY_X). Otherwise we found right movement of the joystick.

;=================================================================================================== 
; APPLY GRAVITY 
;=================================================================================================== 
; Apply Gravity to the player - this system will be totally rewritten at some point to apply 
; a proper gravity to a player or any other sprite.. but for now it's just super basic 
; 
; A returns 0 if we moved down and a collision code if we didn't 
;--------------------------------------------------------------------------------------------------- 
ApplyGravity
lda #%00000100 ; Mask for bit 2 bit JOY_2 
bne @testRightMov ; Set Left state 
lda #$FF 
sta JOY_X 
jmp @doneMov
@testRightMov 
; Test for Right 
lda #%00001000 ; Mask for bit 3 bit JOY_2 
bne @doneMov

Make a call to subroutine (CheckBlockUnder) to see if there is a water collision (COLL_WATER) and execute that branch.
Else make a call to check for a rope collision (COLL_ROPE) and execute that branch.
Else make a call to check for a floor collision (COLL_FLOOR) and execute that branch.

@doneMov 
ldx #1 ; if we are on a rope, can we move down? 
jsr CheckBlockUnder ; first check we are on a rope 
lda COLLIDER_ATTR 
cmp #COLL_WATER 
beq @goFloat 
ldx #1 ; if we are on a rope, can we move down? 
jsr CheckBlockUnder ; first check we are on a rope 
lda COLLIDER_ATTR 
cmp #COLL_ROPE 
beq @ontheRope ; Player is on the rope 
cmp #COLL_FLOOR 
beq @noFalling ; Otherwise we have more checks.

Check the variable (SPRITE_POS_X_DELTA) to see if we are lined up to a tile and branch, otherwise we exit this routine.

lda SPRITE_POS_X_DELTA ; if not lined up on the rope we get a false positive  
cmp #4 ; for collisions around a 'rope hole'  
beq @moveDown ; If we are lined up, and blocked, its' solid ground  
bcc @moveDown ; if less than 4 - shift left one  
jmp @noFalling  

If the delta was aligned then we make a call to subroutine (CheckMoveDown) to see if a tile exists there, otherwise we exit.

No tile was found here so call the subroutine (MovePlayerDown – twice) to move the Sprite down the screen since they are airbound.

We found a rope collision, call the subroutine “PlayerStateRope”

We found a water collision, call the subroutine “PlayerStateFloating”

; Otherwise shift right one
@moveDown
ldx #0 
jsr CheckMoveDown ; If we are at the end, there will be solid ground under us 
beq @spriteMovesDown ; No tile exists under Player, he falls 
rts ; TILE FOUND: Stop gravity 
@spriteMovesDown
ldx #0 
jsr MovePlayerDown 
ldx #1 
jsr MovePlayerDown
@noFalling 
rts
@ontheRope
lda #PLAYER_STATE_ROPE ; change to climb rope state 
jmp ChangePlayerState
@goFloat 
lda #PLAYER_STATE_FLOATING ; change to climb rope state 
jmp ChangePlayerState
 ;=================================================================================================== 
; PLAYER STATES 
;=================================================================================================== 
; Player states are incremented by 2 as they are indexes to look up the address of the state 
; code on the PLAYER_STATE_JUMPTABLE. An address is 2 bytes (1 word) egro the index must increase 
; by 2 bytes. 
;---------------------------------------------------------------------------------------------------
PLAYER_STATE_IDLE = 0; standing still - awaiting input
PLAYER_STATE_WALK_R = 2 ; Walking right
PLAYER_STATE_WALK_L = 4 ; Walking left
PLAYER_STATE_FALL = 6 ; Falling
PLAYER_STATE_STAIRS_R = 8 ; stairs on the right
PLAYER_STATE_STAIRS_L = 10 ; stairs on the left
PLAYER_STATE_ROPE = 12 ; climb rope
PLAYER_STATE_JUMP = 14 ; Jumping
PLAYER_STATE_PUNCH_R = 16 ; punch right
PLAYER_STATE_PUNCH_L = 18 ; punch left
PLAYER_STATE_KICK_R = 20 ; kick right
PLAYER_STATE_KICK_L = 22 ; kick left
PLAYER_STATE_JUMP_R = 24 ; jump right
PLAYER_STATE_JUMP_L = 26 ; jump left
PLAYER_STATE_SWIM_R = 28 ; swim right
PLAYER_STATE_SWIM_L = 30 ; swim left
PLAYER_STATE_FLOATING = 32 ; floating 
PLAYER_SUBSTATE_ENTER =  0 ; we have just entered this state 
PLAYER_SUBSTATE_RUNNING = 1 ; This state is running normally 
;--------------------------------------------------------------------------------------------------- 
; PLAYER STATE JUMPTABLE 
;---------------------------------------------------------------------------------------------------
PLAYER_STATE_JUMPTABLE
word PlayerStateIdle 
word PlayerStateWalkR 
word PlayerStateWalkL 
word PlayerStateFall 
word PlayerStateStairsR 
word PlayerStateStairsL 
word PlayerStateRope 
word PlayerStateJump 
word PlayerStatePunchR 
word PlayerStatePunchL 
word PlayerStateKickR 
word PlayerStateKickL 
word PlayerStateJumpR 
word PlayerStateJumpL 
word PlayerStateSwimR 
word PlayerStateSwimL 
word PlayerStateFloating

ChangePlayerState

Save the variable (PLAYER_STATE) in the X register. Read the immediate value constant (PLAYER_SUBSTATE_ENTER). Save this in the variable (PLAYER_SUBSTATE). Set a “1” value in (SPRITE_ANIM_PLAY).
Next read from the table (PLAYER_STATE_JUMPTABLE. Save it in ZEROPAGE_POINTER_1 (low and high bytes). Execute the subroutine saved in the indirect address ZEROPAGE_POINTER_1.
This routine will point to individual subroutines based on whatever exists in the PLAYER_STATE_JUMPTABLE,x (which points to the specific line for that routine).

;=================================================================================================== 
; CHANGE PLAYER STATE 
;--------------------------------------------------------------------------------------------------- 
; Change a players state 
; 
; A = state to change to 
; 
; Modifies A,X,ZEROPAGE_POINTER_1 
;C64 Brain Notes: Player states recorded (animation, idle, running, etc.). Data is saved to PLAYER_SUBSTATE 
;--------------------------------------------------------------------------------------------------- 

ChangePlayerState

tax ; transfer A to X 
stx PLAYER_STATE ; store the new player state  
lda #PLAYER_SUBSTATE_ENTER ; Set substate to ENTER 
sta PLAYER_SUBSTATE 
lda #1 
sta SPRITE_ANIM_PLAY 
lda PLAYER_STATE_JUMPTABLE,x ; lookup state to change to 
sta ZEROPAGE_POINTER_1 ; and store it in ZEROPAGE_POINTER_1 
lda PLAYER_STATE_JUMPTABLE + 1,x 
sta ZEROPAGE_POINTER_1 + 1 
jmp (ZEROPAGE_POINTER_1) ; jump to state (to setup) 
; NOTE: This is NOT a jsr. 
; The state will act as an extension of 
; this routine then return. 
rts

UpdatePlayerState

Save the constant (PLAYER_STATE) in the X register. Load from the table (PLAYER_STATE_JUMPTABLE) and save in ZEROPAGE_POINTER_1 (low and high bytes). Go to the subroutine (indirect register) to the pointer address located in ZEROPAGE_POINTER_1 (low and high bytes).
This works similar to (ChangePlayerState) and can be used to update a specific pointer in memory.

;=================================================================================================== 
; UPDATE PLAYER STATE 
;--------------------------------------------------------------------------------------------------- 
; Update the player based on their state 
;--------------------------------------------------------------------------------------------------- 
UpdatePlayerState 
ldx PLAYER_STATE ; Load player state 
lda PLAYER_STATE_JUMPTABLE,x ; fetch the state address from the jump table 
sta ZEROPAGE_POINTER_1 ; store it in ZEROPAGE_POINTER_1 
lda PLAYER_STATE_JUMPTABLE + 1,x 
sta ZEROPAGE_POINTER_1 + 1 
jmp (ZEROPAGE_POINTER_1) ; jump to the right state (note - NOT a jsr) 
rts 

PlayerStateIdle

;=================================================================================================== 
; STATE IDLE 
;--------------------------------------------------------------------------------------------------- 
; The player is standing still and waiting input. 
; Possible optimizations we are doublechecking CheckBlockUnder and CheckDown, we can check once 
; and store those in a temp variable and look them up if needed. 
;--------------------------------------------------------------------------------------------------- 
PlayerStateIdle
lda PLAYER_SUBSTATE ; Check for first entry to state 
bne @running
ldx #0 ; load sprite number (0) in X 
lda #; load animation list in ZEROPAGE_POINTER_1  
sta ZEROPAGE_POINTER_1 ; byte %00000111 
lda #>ANIM_PLAYER_IDLE 
sta ZEROPAGE_POINTER_1 + 1 

jsr InitSpriteAnim ; setup the animation for Idle 
lda PLAYER_SUBSTATE_RUNNING ; set the substate to Running 
sta PLAYER_SUBSTATE 
rts
@running
jsr JoystickReady 
beq @input 
rts
@input 
;=============================================================================== 
; SPRITE CLIMBING THE POLE 
;=============================================================================== 
ldx #1 
jsr CheckBlockUnder 
lda COLLIDER_ATTR 
cmp #COLL_ROPE ; Check for rope under player  
beq @goRopeClimb ; found it, go to Rope climb state

@buttonCheck

lda #%00010000 ; Mask for bit 0 
bit JOY_2 ; check zero = jumping (button pressed) 
beq @butPress
;=============================================================================== 
; JOYSTICK: PUNCHING/JUMPING 
;=============================================================================== 
@checkdiagonals 
lda checkupright 
bit JOY_2 ; punch right 
beq @pressUpRight 
lda checkupleft ; Mask for bit 0 
bit JOY_2 ; check zero = jumping (button pressed) 
beq @pressUpLeft ; punch left 
;=============================================================================== 
; SPRITE FLOOR CHECK (ON GROUND) 
;=============================================================================== 
ldx #1 
jsr CheckBlockUnder 
lda COLLIDER_ATTR 
cmp #COLL_FLOOR ; Does floor exist under us? 
bne @stillFalling ; No, player keeps falling 
; Move Player Up if not falling. This fixes a big that positions the player 
; below the floor. 
ldx #0 
jsr MovePlayerUp 
ldx #1 
jsr MovePlayerUp 
jmp @horizCheck ; Player has landed on tile (can't fall)
;=============================================================================== 
; SPRITE FALLING (Gravity applied) 
;===============================================================================

@stillFalling

jsr ApplyGravity

@horizCheck

lda JOY_X ; horizontal movement 
beq @vertCheck ; check zero - ho horizontal input 
bmi @left ; negative = left 
;--------------------------------------------- JOYSTICK RIGHT

@right

lda #PLAYER_STATE_WALK_R ; go to walk state right 
jmp ChangePlayerState 
;--------------------------------------------- JOYSTICK LEFT

@left

lda #PLAYER_STATE_WALK_L ; go to walk state left 
jmp ChangePlayerState

@goFloating

lda #PLAYER_STATE_FLOATING 
jmp ChangePlayerState
@vertCheck 
;=============================================================================== 
; SPRITE SWIMMING IN WATER 
;=============================================================================== 
ldx #1 ; if we are on a rope, can we move down? 
jsr CheckBlockUnder ; first check we are on a rope 
lda COLLIDER_ATTR 
cmp #COLL_WATER 
beq @goFloating 
lda JOY_Y ; check vertical joystick input 
beq @end ; zero means no input 
bmi @up ; negative means up 
bpl @down ; already checked for 0 - so this is positive 
rts

@goRopeClimb

lda #PLAYER_STATE_ROPE 
jmp ChangePlayerState

@inLakeTest

lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState 
rts

@pressUpRight

lda #PLAYER_STATE_PUNCH_R ; go to jump state 
jmp ChangePlayerState

@pressUpLeft

lda #PLAYER_STATE_PUNCH_L ; go to jump state 
jmp ChangePlayerState

@butPress

lda #PLAYER_STATE_JUMP ; go to jump state 
jmp ChangePlayerState
;--------------------------------------------- JOYSTICK UP

@up

ldx #1 
jsr CheckBlockUnder 
lda COLLIDER_ATTR 
cmp #COLL_ROPE ; Check for rope under player  
bne @end
lda #PLAYER_STATE_ROPE ; change to climb rope state 
jmp ChangePlayerState ;-------------------------------------------- JOYSTICK DOWN

@down

ldx #1 ; if we are on a rope, can we move down? 
jsr CheckBlockUnder ; first check we are on a rope 
lda COLLIDER_ATTR 
cmp #COLL_ROPE 
bne @noRope 
jsr CheckMoveDown ; If we are at the end, there will be solid ground under us 
beq @goRopeClimb ; No blocking and on rope? We change to climbing 
; Otherwise we have more checks. 
lda SPRITE_POS_X_DELTA ; if not lined up on the rope we get a false positive 
cmp #4 ; for collisions around a 'rope hole' 
beq @end ; If we are lined up, and blocked, its' solid ground 
bcc @deltaLess ; if less than 4 - shift left one 
ldx #0 
jsr MovePlayerLeft 
rts

@deltaLess

; Otherwise shift right one 
ldx #0 
jsr MovePlayerRight  
rts 

@goWaterFloatRight

lda #PLAYER_STATE_SWIM_R 
jmp ChangePlayerState 
@goWaterFloatLeft 
lda #PLAYER_STATE_SWIM_L 
jmp ChangePlayerState

@noRope
@end

rts

IDLE_VAR

byte $00
 

PlayerStateWalkR

;=================================================================================================== 
; STATE WALKING RIGHT 
;--------------------------------------------------------------------------------------------------- 
PlayerStateWalkR
ldx #0 
jsr CheckMoveRight 
lda COLLIDER_ATTR 
cmp #COLL_WATER 
beq @goSwimRight
lda PLAYER_SUBSTATE
bne @running
;------------------------------------------------------- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_WALK_R
sta ZEROPAGE_POINTER_1 + 1
jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts 
; wait till next frame to start 
;-----------------------------------------------------------------------------
@running
jsr JoystickReady 
beq @input ; Check creates the 'fix' pause for scroll resetting

lda #PLAYER_STATE_IDLE
jmp ChangePlayerState
rts

@input
ldx #1 ; Rope Check 
jsr CheckBlockUnder 
lda COLLIDER_ATTR 
cmp #COLL_ROPE 
beq @joyCheck 
jsr ApplyGravity ; Apply Gravity - if we are not falling 
bne @joyCheck ; check the joystick input 
; Enables player to move right while falling 
inc PLAYER_FALLCOUNT 
lda PLAYER_FALLCOUNT 
cmp #10 
bcs @joyCheck 
rts
@goSwimRight
lda #PLAYER_STATE_SWIM_R 
jmp ChangePlayerState
@joyCheck
lda #0 
sta PLAYER_FALLCOUNT ; Check for JUMP to RIGHT
@buttonCheck
lda JOY_X 
bmi @idle ; if negative we are idling 
beq @idle ; Pressed joystick to right
@right
jsr JoystickReady 
lda #%00010000 ; Mask for bit 0 
bit JOY_2 ; check zero = button pressed 
beq @jumping ; Player can jump right  
ldx #0 
jsr MovePlayerRight ; Move player one pixel across - A = move? 0 or 1 
ldx #1 
jsr MovePlayerRight 
jsr ApplyGravity 
rts
@foundWaterTile
lda #1 
sta 53281 
rts 
@idle
jsr ApplyGravity ; Apply Gravity - if we are not falling 
lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState
@jumping
jsr ApplyGravity ; Apply Gravity - if we are not falling 
lda #PLAYER_STATE_JUMP 
jmp ChangePlayerState
@doneJoy
rts 

PlayerStateWalkL

;=================================================================================================== 
; STATE WALKING LEFT 
;--------------------------------------------------------------------------------------------------- 
PlayerStateWalkL
ldx #0 
jsr CheckMoveLeft 
lda COLLIDER_ATTR 
cmp #COLL_WATER 
beq @goSwimLeft
lda PLAYER_SUBSTATE 
bne @running 
;------------------------------------------------------- SETUP CODE GOES HERE 
ldx #0 ; Use sprite number 0 
lda #; load animation in ZEROPAGE_POINTER_1 
sta ZEROPAGE_POINTER_1 
lda #>ANIM_PLAYER_WALK_L 
sta ZEROPAGE_POINTER_1 + 1 
jsr InitSpriteAnim ; initialize the animation 
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING 
sta PLAYER_SUBSTATE 
rts ; wait till next frame to start 
@running
jsr JoystickReady 
beq @input ; Check creates the 'fix' pause for scroll resetting 

lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState 
rts
@input 
ldx #1 ; Rope Check 
jsr CheckBlockUnder 
lda COLLIDER_ATTR 
cmp #COLL_ROPE 
beq @joyCheck 
 
jsr ApplyGravity ; we are falling 
bne @joyCheck ; Enables player to move left while falling 
inc PLAYER_FALLCOUNT ; if we fall > 8 pixels we are truly falling 
lda PLAYER_FALLCOUNT 
cmp #10 ; Player continues to fall for 10 frames 
bcs @joyCheck 
rts
@goSwimLeft
lda #PLAYER_STATE_SWIM_L 
jmp ChangePlayerState
@joyCheck
lda #0 
sta PLAYER_FALLCOUNT ; Button pressed for Left
@buttonCheck
lda JOY_X 
bpl @idle ; if negative we are idling 
beq @idle 
@left
jsr JoystickReady 
lda #%00010000 ; Mask for bit 0 
bit JOY_2 ; check zero = button pressed 
beq @jumping ; Player can jump left 
ldx #0 
jsr MovePlayerLeft ; Move player one pixel across - A = move? 0 or 1 
ldx #1 
jsr MovePlayerLeft 

jsr ApplyGravity 
rts
@foundWaterTile
lda #1 
sta 53281 
rts 
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState 
@idle
jsr ApplyGravity ; Apply Gravity - if we are not falling

lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

@jumping
jsr ApplyGravity ; Apply Gravity - if we are not falling 
lda #PLAYER_STATE_JUMP
jmp ChangePlayerState 
@foundObject
lda #7 
sta 53280 
lda #1 
sta 53281 
rts 
@doneJoy
rts

PlayerStateRope

;=================================================================================================== 
; STATE ROPE UP 
;--------------------------------------------------------------------------------------------------- 
; Climbing a rope up 
;--------------------------------------------------------------------------------------------------- 
PlayerStateRope

lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_CLIMB_ROPE_UP
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; change takes effect next frame ;—————————————————————————–

@running
;———————————————————- JOYSTICK INPUT

 jsr JoystickReady
beq @input ; not ready for input
rts

;————————————————————————– ; Process valid joystick input
@input

ldx #1
jsr CheckBlockUnder ; Check tile under Top sprite (Sprite)

lda COLLIDER_ATTR
cmp #COLL_ROPE ; Does pole exist here?

bne @exitRopeClimb ; No pole found, exit routine

; Still climbing the rope here

lda JOY_X
beq @vertCheck
bmi @left
bpl @right
rts

@exitRopeClimb
; Move Player Up if not falling. This fixes a big that positions the player ; below the floor.
ldx #0
jsr MovePlayerUp
ldx #1
jsr MovePlayerUp ; Also scroll the screen upward


lda #SCROLL_UP
sta SCROLL_DIRECTION
sta SCROLL_MOVING
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

@right
ldx #1
jsr CheckMoveRight
beq @goRight ; Not blocked, right routine
rts

@goRight
lda #PLAYER_STATE_WALK_R
jmp ChangePlayerState

@left
ldx #1
jsr CheckMoveLeft
beq @goLeft ; Not blocked, left routine
rts

@goLeft
lda #PLAYER_STATE_WALK_L
jmp ChangePlayerState ; Align Player on rope so he can pass through ‘holes’ ; Check UP and DOWN on the ladder

@vertCheck
ldx #1
lda SPRITE_POS_X_DELTA,x
cmp #4 ; they pass through if delta is 4
beq @check ; We have passed completely through the tile
bcc @less ; if less than 4, shift right one pixel
jsr MovePlayerLeft ; not equal, not less, must be more – shift left one
jmp @check

@less
ldx #0
jsr MovePlayerRight
ldx #1
jsr MovePlayerRight

@check
; No tile was found underneath sprite, he falls
lda JOY_Y
beq @end
bmi @up
bpl @down
rts

@up
sec
lda SPRITE_CHAR_POS_Y
cmp #PLAYER_UP_CAP
bcs @goingUp

lda MAP_Y_POS
bne @upScroll
clc
lda MAP_Y_DELTA
cmp #1
bcs @upScroll
rts

@upScroll
ldx #0
jsr CheckMoveUp
beq @scroll
rts

@scroll
lda #SCROLL_UP
sta SCROLL_DIRECTION
sta SCROLL_MOVING
rts

@goingUp
lda #1
sta SPRITE_ANIM_PLAY ; play our animation
ldx #0
jsr MovePlayerUp
ldx #1
jsr MovePlayerUp

@exitMoveUp
rts

@down

lda #1
sta SPRITE_ANIM_PLAY ; play our animation
ldx #0
jsr MovePlayerDown ; Otherwise, move the Sprite Down

ldx #1
jsr MovePlayerDown
bne @endClimb ; We are not blocked continue on

rts

@finishDown
lda #1
sta SPRITE_ANIM_PLAY
jsr MovePlayerDown
bne @endClimb ; We are not blocked continue on

rts ; We are blocked=1 by a tile

@endClimb
lda SPRITE_POS_X_DELTA ; Check if Sprite is passing
cmp #4 ; completely through the tile
beq @stopClimb ; Yes, they passed through
rts

@stopClimb
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

@end
lda #0
sta SPRITE_ANIM_PLAY
; pause our animation

;————————————————————————–
rts

@playerFalls
jsr ApplyGravity

@exitRope
rts

PlayerStateJump

;=================================================================================================== 
; STATE JUMP UP 
;--------------------------------------------------------------------------------------------------- 
; Player is jumping 
;--------------------------------------------------------------------------------------------------- 
; This routine is entered when a Joystick button is pressed.
PlayerStateJump 
@running

@checkJump

@jumping

 ; First check if ledge is above Sprite 
ldx #0 
jsr CheckMoveUp ; Check for tile above our Sprite 
beq @contJump ; No tile exit stage 

lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState
@contJump
inc PLAYER_JUMP_POS ; Counter to track table loop 
lda PLAYER_JUMP_POS ; for PLAYER_JUMP_TABLE,x to read until 
cmp #28 ; it finds a "0" value. 28 bytes 
bcc @jumpOn
lda #0 
sta PLAYER_JUMP_POS 
jmp @jumpComplete ; Still Jumping

; PLAYER_JUMP_POS Counter is still less than 28

@jumpOn
ldx PLAYER_JUMP_POS ; check x for jump table (x = current state 
; of increment PLAYER_JUMP_POS) 
lda PLAYER_JUMP_TABLE,x ; check if at end of jump table = 0 
beq @jumpComplete
@jumpContinue
jsr JoystickReady 
lda JOY_X 
beq @moveUp 
bmi @leftJump ; Check for joystick to Left = 255 
bpl @rightJump ; Check for joystick to Right = 1 
jmp @moveUp ; Slow down the jumping Sprite  
@rightJump
ldx #0 
jsr MovePlayerRight 
ldx #1 
jsr MovePlayerRight 
jmp @moveUp ; Check for Jump to the Left
@leftJump 
ldx #0 
jsr MovePlayerLeft 
ldx #1 
jsr MovePlayerLeft
@moveUp
ldx #1 
jsr CheckBlockUnder 
bne @onGround ; No tile exists under Sprite 
jsr ApplyGravity
@onGround 
ldx #0 
jsr MovePlayerUp 
ldx #1 
jsr MovePlayerUp 
lda PLAYER_TIMER 
bne @skipJump 
lda #1 
sta PLAYER_SPEED 
lda #0 
sta PLAYER_TIMER
@skipJump 
jmp @jumping ; Table jump for PLAYER_JUMP_TABLE,x is done. 
; Value of "0" was found.
@jumpComplete
jsr ApplyGravity 
lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState
;---------------------------------------JOYSTICK RIGHT 
@right 
lda PLAYER_SUBSTATE 
bne @animJumpRightEnd 
ldx #0 
lda #sta ZEROPAGE_POINTER_1 
lda #>ANIM_PLAYER_WALK_R 
sta ZEROPAGE_POINTER_1 + 1 
jsr InitSpriteAnim ; initialize the animation 
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING 
sta PLAYER_SUBSTATE 
rts 
lda #PLAYER_STATE_WALK_R ; go to walk state right 
jmp ChangePlayerState 
;---------------------------------------JOYSTICK LEFT 
@left 
lda PLAYER_SUBSTATE 
bne @animJumpLeftEnd 
ldx #0 
lda #<ANIM_PLAYER_WALK_L 
sta ZEROPAGE_POINTER_1 
lda #>ANIM_PLAYER_WALK_L 
sta ZEROPAGE_POINTER_1 + 1 
jsr InitSpriteAnim ; initialize the animation 
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING 
sta PLAYER_SUBSTATE 
rts
@animJumpLeftEnd
lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState 
lda #PLAYER_STATE_WALK_L ; go to walk state left 
jmp ChangePlayerState
@animJumpRightEnd
lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState 
lda #PLAYER_STATE_WALK_R ; go to walk state left 
jmp ChangePlayerState
@idle 
lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState

PlayerStatePunchR

;===================================================================================================
; STATE PUNCH RIGHT
;—————————————————————————————————
; IMPORTANT: Checks when the Player can Move LEFT
or RIGHT. No other state or subroutine does this.
; The player is standing still
and waiting input.
; Possible optimizations we are doublechecking CheckBlockUnder
and CheckDown, we can check once
;
and store those in a temp variable and look them up if needed.
;—————————————————————————————————

PlayerStatePunchR 
lda PLAYER_SUBSTATE ; Check for first entry to state 
bne @running 
ldx #0 
lda #<ANIM_PLAYER_PUNCH_R ; load animation list in ZEROPAGE_POINTER_1  
sta ZEROPAGE_POINTER_1 
lda #>ANIM_PLAYER_PUNCH_R 
sta ZEROPAGE_POINTER_1 + 1 
jsr InitSpriteAnim ; setup the animation for Idle 
lda PLAYER_SUBSTATE_RUNNING ; set the substate to Running 
sta PLAYER_SUBSTATE 
rts ; wait till next frame to start 
@running
;------------------------------------------------------------ JOYSTICK INPUT

jsr JoystickReady
beq @input
rts ; not ready for input, we return 
@input ; process valid joystick input

@gravity

beq @joyCheck
jsr ApplyGravity ; Apply Gravity – if we are not falling

bne @joyCheck ; check the joystick input

@joyCheck
lda JOY_X
beq @idle ; if JOY_X is 0 we are idling and need to change states
bmi @idle ; if negative we are idling 
@doneJoy
rts
@idle
lda #0
sta SPRITE_ANIM_PLAY ; pause our animation

lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

PlayerStatePunchL

;=================================================================================================== 
; STATE PUNCH RIGHT 
;--------------------------------------------------------------------------------------------------- 
; IMPORTANT: Checks when the Player can Move LEFT or RIGHT. No other state or subroutine does this. 
; The player is standing still and waiting input. 
; Possible optimizations we are doublechecking CheckBlockUnder and CheckDown, we can check once 
; and store those in a temp variable and look them up if needed. 
;--------------------------------------------------------------------------------------------------- 
PlayerStatePunchL
lda PLAYER_SUBSTATE ; Check for first entry to state 
bne @running 
ldx #0 
lda #<ANIM_PLAYER_PUNCH_L ; load animation list in ZEROPAGE_POINTER_1  
sta ZEROPAGE_POINTER_1 
lda #>ANIM_PLAYER_PUNCH_L 
sta ZEROPAGE_POINTER_1 + 1 
jsr InitSpriteAnim ; setup the animation for Idle 
lda PLAYER_SUBSTATE_RUNNING ; set the substate to Running 
sta PLAYER_SUBSTATE 
rts ; wait till next frame to start
@running
;------------------------------------------------------------ JOYSTICK INPUT 
lda #1 
sta SPRITE_ANIM_PLAY ; begin our animation when set to one 
jsr JoystickReady 
beq @input 
rts ; not ready for input, we return
@input
; process valid joystick input 
@gravity
@joyCheck
lda JOY_X 
beq @idle ; if JOY_X is 0 we are idling and need to change states 
bmi @idle ; if negative we are idling
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState
rts 
@idle
; lda #PLAYER_STATE_IDLE 
; jmp ChangePlayerState

PlayerStateKickR

;=================================================================================================== 
; STATE STAIRS RIGHT 
;--------------------------------------------------------------------------------------------------- 
; Player state for climbing stairs 
;--------------------------------------------------------------------------------------------------- 
PlayerStateKickR
lda PLAYER_SUBSTATE ; test for first run 
bne @running 
;------------------------------------------------------- SETUP CODE GOES HERE 
; TODO - some check to change to walking right animation 
; if it's currently different 

ldx #0 ; Use sprite number 0
lda #; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_KICK_R
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; state change goes into effect next frame

;—————————————————————————– @running
;———————————————————- JOYSTICK INPUT

jsr JoystickReady
beq @input ; not ready for input

rts

@input 
lda JOY_X 
beq @vert_check ; X axis in 0 - check for up  
bmi @idle ; if it's -1 (left) return to idle 
; so it has to be 1 (right) - climb the stair
@vert_check 
; TO DO : check for an up press  
;@climb  
 
lda #PLAYER_STATE_IDLE ; return to idle (which will likely go to fall) 
jmp ChangePlayerState 

@idle

 
; lda #PLAYER_STATE_IDLE ; return to idle (which will likely go to fall) ; 
jmp ChangePlayerState 
rts

PlayerStateKickL

;=====================================================
; STATE STAIRS RIGHT
;---------------------------------------------------------------------------------------------------
; Player state for climbing stairs
;--------------------------------------------------------------------------------------------------- 

PlayerStateKickL
lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE

; TODO – some check to change to walking right animation
; if it’s currently different
ldx #0 ; Use sprite number 0
lda #<ANIM_PLAYER_KICK_L ; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_KICK_L
sta ZEROPAGE_POINTER_1 + 1
jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; state change goes into effect next frame
;—————————————————————————–

@running
;———————————————————- JOYSTICK INPUT
jsr JoystickReady
beq @input ; not ready for input

rts

@input
lda JOY_X
beq @vert_check ; X axis in 0 – check for up

bmi @idle ; if it’s –1 (left) return to idle
; so it has to be 1 (right) – climb the stair

@vert_check ; TO DO : check for an up press
;@climb
;
; check for stair collider
;
jsr MovePlayerRight ; attempt to move right
;
lda COLLIDER_ATTR ; we can’t – because of a stair
;
cmp #COLL_STAIR
;
beq @go_up ; so go up instead
;
;
lda #PLAYER_STATE_WALK_R ; else go back to walking (top of stair)
;
jmp ChangePlayerState
;
;@go_up
;
jsr MovePlayerUp

lda #PLAYER_STATE_IDLE ; return to idle (which will likely go to fall)
jmp ChangePlayerState
;
rts @idle
;
lda #PLAYER_STATE_IDLE ; return to idle (which will likely go to fall) ;
jmp ChangePlayerState

rts

PlayerStateFloating

;=================================================================================================== 
; STATE FLOATING 
;--------------------------------------------------------------------------------------------------- 
PlayerStateFloating 
lda PLAYER_SUBSTATE
bne @running
;------------------------------------------------------- SETUP CODE GOES HERE  
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING 
sta PLAYER_SUBSTATE
rts ; wait till next frame to start 

;—————————————————————————–

@running

jsr JoystickReady
beq @input

; Check creates the ‘fix’ pause for scroll resetting
rts

@input

lda JOY_Y 
beq @end 
bmi @up 
bpl @down
lda JOY_X 
beq @end 
bmi @left

@right

lda #PLAYER_STATE_SWIM_R 
jmp ChangePlayerState
;--------------------------------------------- JOYSTICK LEFT

@left

lda #PLAYER_STATE_SWIM_L 
jmp ChangePlayerState

@up

ldx #0 
jsr MovePlayerUp ; Move player one pixel across - A = move? 0 or 1 
ldx #1 
jsr MovePlayerUp 
rts

@down

ldx #0 
jsr MovePlayerDown ; Move player one pixel across - A = move? 0 or 1 
ldx #1 
jsr MovePlayerDown 
rts

@end

lda #PLAYER_STATE_IDLE 
jmp ChangePlayerState

PlayerStateSwimR

;=================================================================================================== 
; STATE SWIM RIGHT 
;--------------------------------------------------------------------------------------------------- 

PlayerStateSwimR

lda PLAYER_SUBSTATE 
bne @running
;------------------------------------------------------- SETUP CODE GOES HERE 

ldx #0 ; Use sprite number 0
lda #<ANIM_PLAYER_SWIM_R ; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_SWIM_R
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts

; wait till next frame to start
;—————————————————————————–

@running

lda #1 
sta SPRITE_ANIM_PLAY ; begin our animation when set to one 
jsr JoystickReady 
beq @input ; Check creates the 'fix' pause for scroll resetting 
rts

@input

lda JOY_X 
beq @idle 
bpl @right 
jmp @idle

@right

ldx #0 
jsr MovePlayerRight ; Move player one pixel across - A = move? 0 or 1 
ldx #1 
jsr MovePlayerRight 
rts 
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState 

@idle
lda #PLAYER_STATE_IDLE
jmp
ChangePlayerState
@doneJoy
rts

PlayerStateSwimL

;=================================================================================================== 
; STATE SWIM LEFT 
;--------------------------------------------------------------------------------------------------- 

PlayerStateSwimL

lda PLAYER_SUBSTATE 
bne @running

 

;------------------------------------------------------- SETUP CODE GOES HERE 
ldx #0 ; Use sprite number 0 
lda #<ANIM_PLAYER_SWIM_L ; load animation in ZEROPAGE_POINTER_1 
sta ZEROPAGE_POINTER_1 
lda #>ANIM_PLAYER_SWIM_L 
sta ZEROPAGE_POINTER_1 + 1 
jsr InitSpriteAnim ; initialize the animation 
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING 
sta PLAYER_SUBSTATE 
rts 

; wait till next frame to start

;—————————————————————————–
@running
lda #1
sta SPRITE_ANIM_PLAY ; begin our animation when set to one
jsr JoystickReady
beq @input ; Check creates the ‘fix’ pause for scroll resetting
rts

@input

lda JOY_X 
beq @idle 
bmi @left 
jmp @idle

@left

ldx #0 
jsr MovePlayerLeft ; Move player one pixel across - A = move? 0 or 1 
ldx #1 
jsr MovePlayerLeft 
rts 

lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

@idle
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

@doneJoy
rts

PlayerState_Framework

;=================================================================================================== 
; STATE FRAMEWORK 
;--------------------------------------------------------------------------------------------------- 
; A blank state template to make adding new states easier 
;--------------------------------------------------------------------------------------------------- 

PlayerState_Framework

lda PLAYER_SUBSTATE ; test for first run 
bne @running

 

;------------------------------------------------------- SETUP CODE GOES HERE

;

ldx #0 ; Use sprite number 0 
; lda #<ANIM_TEST ; load animation in ZEROPAGE_POINTER_1 
; sta ZEROPAGE_POINTER_1 
; lda #>ANIM_TEST 
; sta ZEROPAGE_POINTER_1 + 1 

jsr InitSpriteAnim ; initialize the animation 
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING 
sta PLAYER_SUBSTATE 
rts ; change takes effect next frame 

;—————————————————————————–

@running

;---------------------------------------------------------- JOYSTICK INPUT 
jsr JoystickReady 
beq @input ; not ready for input 
rts 

;————————————————————————–
; Process valid joystick input

@input
;
lda JOY_X
;
beq @vertCheck
;
bmi @left
;
bpl @right
;
rts @right

@left @vertCheck
;
lda JOY_Y
;
beq @end
;
bmi @up
;
bpl @down
;
rts

@up
@down
@end
;————————————————————————–
rts

;=================================================================================================== 
; PLAYER DATA 
;---------------------------------------------------------------------------------------------------
 
PLAYER_DATA 
PLAYER_STATE 
; Current state - walking, standing, dying, climbing 
byte 0

PLAYER_SUBSTATE

byte 0 ; Current substate - jumping up, falling down, start dying 
PLAYER_DIRECTION_X byte 0 
PLAYER_DIRECTION_Y byte 0 
PLAYER_FALLCOUNT byte 0 ; Counting frames to actual fall 
PLAYER_JUMPUPRIGHT byte 0 
PLAYER_JUMPUPLEFT byte 0 
PLAYER_FALLFLAG byte 0 ; Jump table from Endurion's code sample: ;gamedeve.net/blog/949/entry-2250107-a-c64-game-step-7'

PLAYER_JUMP_POS

byte 0 
PLAYER_JUMP_TABLE 
;byte 8,7,5,3,2,1,1,1,0,0 
byte 18,18,17,17,15,13,12,12,11,11,11,10,10 
byte 8,8,7,7,5,5,3,3,2,2,1,1,1,0,0
 
JUMP_TABLE_SIZE byte 10 
PLAYER_FALL_POS byte 0 
FALL_SPEED_TABLE byte 1,1,2,2,3,3,3,3,3,3 
PLAYER_TIMER byte 0 
PLAYER_TIMER2 byte 0 
PLAYER_SPEED byte 0