; autosized lemmings state based on compile-time MAX_LEMMINGS value ; currently 10 lemming_x: .res MAX_LEMMINGS,0 lemming_y: .res MAX_LEMMINGS,0 lemming_direction: .res MAX_LEMMINGS,0 lemming_out: .res MAX_LEMMINGS,0 lemming_frame: .res MAX_LEMMINGS,0 lemming_exploding_frame:.res MAX_LEMMINGS,0 lemming_status: .res MAX_LEMMINGS,0 lemming_exploding: .res MAX_LEMMINGS,0 lemming_fall_distance: .res MAX_LEMMINGS,0 lemming_attribute: .res MAX_LEMMINGS,0 ;========================== ; move the lemmings ;========================== move_lemmings: ldy #0 sty CURRENT_LEMMING ; reset loop move_lemming_loop: ldy CURRENT_LEMMING ; get current in Y lda lemming_out,Y beq skip_move_lemming ; if not out, don't move really_move_lemming: ; bump frame tya tax inc lemming_frame,X ; only can inc with X lda lemming_status,Y ; use jump table based on status tax lda move_lemming_jump_h,X pha lda move_lemming_jump_l,X pha rts ; jump to it done_move_lemming: ; sanity boundary check, remove if X<0 or X>40 or Y<0 or Y>180 ldy CURRENT_LEMMING lda lemming_x,Y ; bmi lemming_in_space cmp #40 bcs lemming_in_space ; if negative wraps around to 255 lda lemming_y,Y cmp #180 ; if negative, wraps around to 255 bcc lemming_in_bounds ; so we catch it with this check too lemming_in_space: clc jsr remove_lemming lemming_in_bounds: skip_move_lemming: inc CURRENT_LEMMING ; loop until done lda CURRENT_LEMMING cmp #MAX_LEMMINGS bne move_lemming_loop really_done_checking_lemming: rts move_lemming_jump_l: .byte <(do_lemming_falling-1) .byte <(do_lemming_walking-1) .byte <(do_lemming_digging-1) .byte <(do_lemming_exploding-1) .byte <(do_lemming_particles-1) .byte <(do_lemming_splatting-1) .byte <(do_lemming_floating-1) .byte <(do_lemming_climbing-1) .byte <(do_lemming_bashing-1) .byte <(do_lemming_stopping-1) .byte <(do_lemming_mining-1) .byte <(do_lemming_building-1) .byte <(do_lemming_shrugging-1) .byte <(do_lemming_pullup-1) move_lemming_jump_h: .byte >(do_lemming_falling-1) .byte >(do_lemming_walking-1) .byte >(do_lemming_digging-1) .byte >(do_lemming_exploding-1) .byte >(do_lemming_particles-1) .byte >(do_lemming_splatting-1) .byte >(do_lemming_floating-1) .byte >(do_lemming_climbing-1) .byte >(do_lemming_bashing-1) .byte >(do_lemming_stopping-1) .byte >(do_lemming_mining-1) .byte >(do_lemming_building-1) .byte >(do_lemming_shrugging-1) .byte >(do_lemming_pullup-1) ;========================= ;========================= ; falling ;========================= ;========================= do_lemming_falling: tya tax inc lemming_y,X ; fall speed inc lemming_y,X inc lemming_fall_distance,X ; how far lda lemming_fall_distance,X cmp #12 bcc not_fallen_enough ; blt lda lemming_attribute,X bpl not_fallen_enough ; see if high bit set ; we can switch to floating lda #LEMMING_FLOATING sta lemming_status,X lda #0 sta lemming_frame,X not_fallen_enough: jsr collision_check_ground jmp done_move_lemming ;========================= ;========================= ; floating ;========================= ;========================= do_lemming_floating: tya tax inc lemming_y,X ; fall speed lda #0 sta lemming_fall_distance,X jsr collision_check_ground jmp done_move_lemming ;========================= ;========================= ; walking ;========================= ;========================= do_lemming_walking: ; see if ground has risen up jsr collision_check_hill ; collision detect walls ldy CURRENT_LEMMING lda lemming_y,Y clc adc #3 ; waist-high? tax lda hposn_high,X clc adc #$20 sta GBASH lda hposn_low,X sta GBASL ; increment ; only do this every 4th frame? lda lemming_frame,Y and #$3 beq walking_increment bne walking_done walking_increment: ; actually incrememt clc ; increment/decrement X lda lemming_x,Y adc lemming_direction,Y ; A is now incremented version ; sta lemming_x,Y tay ; Y is now incremented version lda (GBASL),Y ; collision check cmp #$ff ; don't collide with bridge beq walking_no_wall and #$7f beq walking_no_wall walking_yes_wall: pha ; we hit a wall, reverse course, undo the increment ; Y is updated jsr check_at_exit_xiny ; check if climber, if so climb ldy CURRENT_LEMMING pla cmp #$10 beq not_climber ; HACK: special case so climbers ; don't climb over stoppers lda lemming_attribute,Y and #LEMMING_CLIMBER beq not_climber lda #LEMMING_CLIMBING sta lemming_status,Y jmp walking_done not_climber: ; reverse direction lda lemming_direction,Y eor #$ff clc adc #1 sta lemming_direction,Y jmp walking_no_increment walking_no_wall: ; y is incremented version tya ldy CURRENT_LEMMING sta lemming_x,Y walking_no_increment: jsr collision_check_ground walking_done: jmp done_move_lemming ;===================== ; digging ;===================== do_lemming_digging: ; ldy #CURRENT_LEMMING lda lemming_y,Y ; point to spot below us clc adc #9 tax lda hposn_high,X clc adc #$20 ; point to background sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y tay lda #$0 sta (GBASL),Y ; erase bg ldx CURRENT_LEMMING ; dig down inc lemming_y,X blurgh: jsr collision_check_ground jmp done_move_lemming ;===================== ; mining ;===================== do_lemming_mining: ldy CURRENT_LEMMING lda lemming_frame,Y and #$f bne no_mining_this_frame ; only move dirt on frame 0 ldx #0 ; erase background stx HGR_COLOR ; (X,A) to (X,A+Y) where X is xcoord/7 jsr hgr_box_page_toggle ; erase box page1 ldy CURRENT_LEMMING lda lemming_x,Y tax lda lemming_y,Y ldy #10 jsr hgr_box jsr hgr_box_page_toggle ; erase box page2 ldy CURRENT_LEMMING lda lemming_x,Y tax lda lemming_y,Y ldy #10 jsr hgr_box ldx CURRENT_LEMMING ; move 3 lines down inc lemming_y,X inc lemming_y,X inc lemming_y,X lda lemming_x,X ; move left or right clc adc lemming_direction,X sta lemming_x,X jsr collision_check_ground no_mining_this_frame: done_mining: jmp done_move_lemming ;===================== ; bashing ;===================== do_lemming_bashing: ldy CURRENT_LEMMING lda lemming_frame,Y and #$f bne no_bashing_this_frame ldx #0 stx HGR_COLOR ; (X,A) to (X,A+Y) where X is xcoord/7 jsr hgr_box_page_toggle ldy CURRENT_LEMMING lda lemming_x,Y clc adc lemming_direction,Y tax lda lemming_y,Y ldy #9 jsr hgr_box jsr hgr_box_page_toggle ldy CURRENT_LEMMING lda lemming_x,Y clc adc lemming_direction,Y tax lda lemming_y,Y ldy #9 jsr hgr_box ldy CURRENT_LEMMING ; FIXME: combine with earlier? lda lemming_x,Y clc adc lemming_direction,Y sta lemming_x,Y ; here? jsr collision_check_side no_bashing_this_frame: ; jsr collision_check_ground jmp done_move_lemming ; ; xxxxxxx ; xxxxxxx ; xxxxxxx ; xxxxxxx ; ;===================== ; building ;===================== do_lemming_building: ldy CURRENT_LEMMING lda lemming_frame,Y and #$7 beq yes_building_this_frame jmp no_building_this_frame ; only move dirt on frame 0 yes_building_this_frame: ; ldx #7 ; draw white block ; stx HGR_COLOR lda lemming_attribute,Y and #$1 bne building_odd building_even: ldy CURRENT_LEMMING lda lemming_y,Y clc adc #9 tax ; get row info for Y1 into GBASL/GBASH lda hposn_high,X sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y clc adc lemming_direction,Y tay lda #$ff sta (GBASL),Y lda GBASH clc adc #$20 sta GBASH lda #$ff sta (GBASL),Y jmp update_building building_odd: ldy CURRENT_LEMMING lda lemming_y,Y clc adc #8 tax ; get row info for Y1 into GBASL/GBASH lda hposn_high,X sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y ; build one block beyond clc adc lemming_direction,Y sta build_smc4+1 sta build_smc3+1 clc adc lemming_direction,Y sta build_smc1+1 sta build_smc2+1 lda #$ff ; put one block build_smc4: ldy #$dd sta (GBASL),Y build_smc1: ldy #$dd ; also put one beyond sta (GBASL),Y lda GBASH clc adc #$20 sta GBASH lda #$ff build_smc3: ldy #$dd sta (GBASL),Y build_smc2: ldy #$dd ; see if hit wall lda (GBASL),Y and #$7f beq coast_clear hit_something: ldy CURRENT_LEMMING lda #LEMMING_WALKING sta lemming_status,Y jmp keep_at_it coast_clear: lda #$ff sta (GBASL),Y keep_at_it: ldx CURRENT_LEMMING ; move 2 lines up dec lemming_y,X dec lemming_y,X ; dec lemming_y,X lda lemming_x,X ; move left or right clc adc lemming_direction,X sta lemming_x,X update_building: ldx CURRENT_LEMMING inc lemming_attribute,X lda lemming_attribute,X and #$f cmp #11 bne done_building cmp #8 bcc no_build_click jsr click_speaker no_build_click: ; hit the end! lda #LEMMING_SHRUGGING sta lemming_status,X lda #0 sta lemming_frame,X no_building_this_frame: done_building: jmp done_move_lemming ;======================== ; stopping ;======================== do_lemming_stopping: ; we do want to see if something knocks the ground out from ; under us ; in practice that is tricky as the barrier we draw causes ; the lemming to jump. hmmm ; jsr collision_check_ground jmp done_move_lemming ;======================== ; shrugging ;======================== do_lemming_shrugging: lda lemming_frame,Y cmp #8 bne not_done_shrugging lda #LEMMING_WALKING ; done shrug, start walking again sta lemming_status,Y not_done_shrugging: jmp done_move_lemming ;======================== ; climbing ;======================== do_lemming_climbing: lda lemming_frame,Y and #3 bne not_done_climbing ; climb tya tax dec lemming_y,X dec lemming_y,X not_done_climbing: jsr collision_check_ceiling bcs done_climbing ; carry set we hit ceiling ; don't check pullup then jsr collision_check_above done_climbing: jmp done_move_lemming ;======================== ; pulling up ;======================== do_lemming_pullup: lda lemming_frame,Y cmp #8 bne not_done_pullup lda lemming_x,Y clc adc lemming_direction,Y sta lemming_x,Y lda #LEMMING_WALKING sta lemming_status,Y jmp done_pullup not_done_pullup: ; climb tya tax dec lemming_y,X done_pullup: jmp done_move_lemming ;===================== ; do nothing ;===================== ; placeholders do_lemming_exploding: ; nothing special do_lemming_splatting: ; nothing special do_lemming_particles: ; work done in draw jmp done_move_lemming ;========================== ; remove lemming from game ;========================== ; Y points to CURRENT_LEMMING ; if C set it means they exited remove_lemming: bcc didnt_exit sed lda PERCENT_RESCUED_L clc adc PERCENT_ADD sta PERCENT_RESCUED_L bcc no_percent_oflo inc PERCENT_RESCUED_H no_percent_oflo: cld jsr update_percent_in didnt_exit: sed ; decrement BCD value lda LEMMINGS_OUT sec sbc #1 sta LEMMINGS_OUT cld jsr click_speaker lda #0 ldy CURRENT_LEMMING sta lemming_out,Y jsr update_lemmings_out ; if that was the last one, then level over lda LEMMINGS_OUT bne not_last_lemming lda #LEVEL_WIN sta LEVEL_OVER not_last_lemming: rts ;============================= ; collision check side ;============================= ; for bashing ; maybe can also be used when building? collision_check_side: ldy CURRENT_LEMMING lda lemming_y,Y clc adc #3 tax lda hposn_high,X clc adc #$20 ; check bg, not fg sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y clc adc lemming_direction,Y tay lda (GBASL),Y and #$7f bne gotta_keep_going broke_on_through_to_the_other_side: ldy CURRENT_LEMMING lda #LEMMING_WALKING sta lemming_status,Y gotta_keep_going: rts ;============================= ; collision check above ;============================= ; looking above right/left ; useful for climbers to see if at top collision_check_above: ldy CURRENT_LEMMING lda lemming_y,Y tax lda hposn_high,X clc adc #$20 ; check bg, not fg sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y clc adc lemming_direction,Y tay lda (GBASL),Y and #$7f bne gotta_keep_climbing at_the_top: ldy CURRENT_LEMMING lda #LEMMING_PULLUP sta lemming_status,Y lda #0 ; reset frame for pullup sta lemming_frame,Y gotta_keep_climbing: rts ;============================= ; collision check ceiling ;============================= collision_check_ceiling: ldy CURRENT_LEMMING lda lemming_y,Y tax lda hposn_high,X clc adc #$20 ; check bg, not fg sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y tay lda (GBASL),Y and #$7f beq done_check_ceiling ; if empty space above us, OK hit_head: ; o/~ I hit my head, I heard the phone ring... o/~ ; face other direction, fall ldy CURRENT_LEMMING lda lemming_direction,Y eor #$ff clc adc #1 sta lemming_direction,Y lda #LEMMING_FALLING sta lemming_status,Y sec rts done_check_ceiling: clc rts ;============================= ; collision check ground ;============================= collision_check_ground: ldy CURRENT_LEMMING lda lemming_y,Y clc adc #9 ; FIXME: should be 10? tax lda hposn_high,X clc adc #$20 ; check bg, not fg sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y tay lda (GBASL),Y and #$7f beq ground_falling ; if empty space below us, fall on_the_ground: ; if we get here we're on the ground ; if we were previously falling/floating we need to do something ; otherwise do nothing ldy CURRENT_LEMMING lda lemming_status,Y cmp #LEMMING_FALLING beq hit_ground cmp #LEMMING_FLOATING beq hit_ground bne done_check_ground ; could rts here? hit_ground: ; hit ground walking ldy CURRENT_LEMMING lda lemming_fall_distance,Y cmp #32 bcs lemming_goes_splat lda #0 sta lemming_fall_distance,Y lda #LEMMING_WALKING ; else, walk jmp update_status_check_ground ; nothing beneath us, falling ground_falling: ldy CURRENT_LEMMING lda lemming_status,Y ; if floating, don't go back to fall cmp #LEMMING_FLOATING beq done_check_ground lda #LEMMING_FALLING update_status_check_ground: ldy CURRENT_LEMMING sta lemming_status,Y done_check_ground: rts lemming_goes_splat: lda #LEMMING_SPLATTING sta lemming_status,Y lda #0 sta lemming_frame,Y ; clc ; jsr remove_lemming rts ;============================= ; collision check hill ;============================= collision_check_hill: lda lemming_y,Y clc adc #8 tax lda hposn_high,X clc adc #$20 ; check bg, not fg sta GBASH lda hposn_low,X sta GBASL lda lemming_x,Y tay lda (GBASL),Y and #$7f beq on_ground ; if empty space below us, good underground: ldx CURRENT_LEMMING dec lemming_y,X ; bump us up on_ground: rts ;============================= ; check at exit ;============================= check_at_exit_xiny: tya ldy CURRENT_LEMMING jmp check_at_exit_y check_at_exit: ; see if at exit ldy CURRENT_LEMMING ; check X lda lemming_x,Y check_at_exit_y: exit_x1_smc: cmp #31 bcc not_done_level exit_x2_smc: cmp #35 bcs not_done_level ; check Y lda lemming_y,Y exit_y1_smc: cmp #116 bcc not_done_level exit_y2_smc: cmp #127 bcs not_done_level ; done level sec jsr remove_lemming not_done_level: rts ;============================= ; check if hit hazzard ;============================= ; TODO: make configurable collision_check_hazzard: ldy #0 sty CURRENT_LEMMING check_hazzard_loop: ldy CURRENT_LEMMING lda lemming_out,Y beq continue_check_hazzard lda lemming_y,Y cmp #130 bcs in_hazzard bcc continue_check_hazzard in_hazzard: lda lemming_status,Y cmp #LEMMING_SPLATTING beq continue_check_hazzard ; don't re-splat lda #LEMMING_SPLATTING sta lemming_status,Y lda #0 sta lemming_frame,Y continue_check_hazzard: inc CURRENT_LEMMING lda CURRENT_LEMMING cmp #MAX_LEMMINGS bne check_hazzard_loop rts ;============================= ; check if hit hazzard2 ;============================= ; TODO: make configurable collision_check_hazzard2: ldy #0 sty CURRENT_LEMMING check_hazzard_loop2: ldy CURRENT_LEMMING lda lemming_out,Y beq continue_check_hazzard2 lda lemming_x,Y cmp #10 bcs continue_check_hazzard2 in_hazzard2: lda lemming_status,Y cmp #LEMMING_SPLATTING beq continue_check_hazzard2 ; don't re-splat lda #LEMMING_SPLATTING sta lemming_status,Y lda #0 sta lemming_frame,Y continue_check_hazzard2: inc CURRENT_LEMMING lda CURRENT_LEMMING cmp #MAX_LEMMINGS bne check_hazzard_loop2 rts