diff --git a/games/duke/level1_data.inc b/games/duke/old/level1_data.inc similarity index 100% rename from games/duke/level1_data.inc rename to games/duke/old/level1_data.inc diff --git a/games/keen/Makefile b/games/keen/Makefile new file mode 100644 index 00000000..15bf6ece --- /dev/null +++ b/games/keen/Makefile @@ -0,0 +1,98 @@ +include ../../Makefile.inc + +DOS33 = ../../utils/dos33fs-utils/dos33 +DOS33_RAW = ../../utils/dos33fs-utils/dos33_raw +B2D = ../../utils/bmp2dhr/b2d +PNG2GR = ../../utils/gr-utils/png2gr +LZSA = ~/research/lzsa/lzsa/lzsa +TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft +EMPTY_DISK = ../../empty_disk/empty.dsk + + +all: keen.dsk + +keen.dsk: HELLO LOADER KEEN_TITLE KEEN_LEVEL1 +#KEEN_LEVEL2 + cp $(EMPTY_DISK) keen.dsk + $(DOS33) -y keen.dsk SAVE A HELLO + $(DOS33) -y keen.dsk BSAVE -a 0x1000 LOADER + $(DOS33) -y keen.dsk BSAVE -a 0x4000 KEEN_TITLE + $(DOS33) -y keen.dsk BSAVE -a 0x2000 KEEN_LEVEL1 +# $(DOS33) -y keen.dsk BSAVE -a 0x2000 KEEN_LEVEL2 + + +### + +HELLO: hello.bas + $(TOKENIZE) < hello.bas > HELLO + +#### + +LOADER: loader.o + ld65 -o LOADER loader.o -C ../../linker_scripts/apple2_1000.inc + +loader.o: loader.s + ca65 -o loader.o loader.s -l loader.lst + +#### + +KEEN_TITLE: keen_title.o + ld65 -o KEEN_TITLE keen_title.o -C ../../linker_scripts/apple2_4000.inc + +keen_title.o: keen_title.s zp.inc hardware.inc init_vars.s \ + zx02_optim.s \ + graphics/keen1_title.hgr.zx02 + ca65 -o keen_title.o keen_title.s -l keen_title.lst + +#### + +KEEN_LEVEL1: keen_level1.o + ld65 -o KEEN_LEVEL1 keen_level1.o -C ../../linker_scripts/apple2_2000.inc + +keen_level1.o: keen_level1.s zp.inc hardware.inc \ + print_help.s gr_fast_clear.s quit_yn.s text_drawbox.s level_end.s \ + enemies_level1.s actions_level1.s item_level1.s \ + graphics/keen_graphics.inc \ + maps/level1_map.lzsa \ + status_bar.s draw_keen.s move_keen.s gr_putsprite_crop.s \ + draw_tilemap.s \ + sound_effects.s speaker_tone.s \ + keyboard.s handle_laser.s + ca65 -o keen_level1.o keen_level1.s -l keen_level1.lst + +#### + +KEEN_LEVEL2: keen_level2.o + ld65 -o KEEN_LEVEL2 keen_level2.o -C ../../linker_scripts/apple2_2000.inc + +keen_level2.o: keen_level2.s zp.inc hardware.inc \ + print_help.s gr_fast_clear.s quit_yn.s text_drawbox.s level_end.s \ + enemies_level2.s actions_level2.s item_level2.s \ + graphics/keen_graphics.inc keen_sprites.inc \ + maps/level2_map.lzsa \ + status_bar.s draw_keen.s move_keen.s gr_putsprite_crop.s \ + draw_tilemap.s \ + sound_effects.s speaker_tone.s \ + keyboard.s handle_laser.s + ca65 -o keen_level2.o keen_level2.s -l keen_level2.lst + + +#### + +graphics/keen1_title.hgr.zx02: + cd graphics && make + +graphics/keen_graphics.inc: + cd graphics && make + +maps/level1_map.lzsa: + cd maps && make + +#### + +clean: + rm -f *~ *.o *.lst HELLO KEEN_TITLE KEEN_LEVEL1 + cd graphics && make clean + cd maps && make clean +# cd title && make clean + diff --git a/games/keen/actions_level1.s b/games/keen/actions_level1.s new file mode 100644 index 00000000..6b153c60 --- /dev/null +++ b/games/keen/actions_level1.s @@ -0,0 +1,204 @@ + + ; someone pressed UP + +up_action: + + + ; set X and Y value + ; convert tile values to X,Y + ; X=((KEEN_X/2)-1)+TILEX + + lda KEEN_X + lsr + sec + sbc #1 + clc + adc TILEMAP_X + sta XPOS + + ; Y = (KEENY/4)+TILEY + lda KEEN_Y + lsr + lsr + clc + adc TILEMAP_Y + sta YPOS + + ; $39,$22 = 57,34 + + ; check if it's a key slot +check_red_keyhole: + + + ; key slot is 280,148 + ; 280,148 (-80,-12) -> 200,136 -> (/4,/4) -> 50,34 + + lda XPOS + cmp #50 + beq redkey_x + cmp #51 + bne check_if_exit + +redkey_x: + + lda YPOS + cmp #34 + bne check_if_exit + + ; check that we have the key + lda INVENTORY + and #INV_RED_KEY + bne open_the_wall + +no_red_key: + jsr buzzer_noise + jmp done_up_action + + ; open the red wall + ; there has to be a more efficient way of doing this +open_the_wall: + ; reset smc + lda #>BIG_TILEMAP + sta rwr_smc1+2 + sta rwr_smc2+2 + +remove_red_wall_outer: + ldx #0 +remove_red_wall_loop: +rwr_smc1: + lda BIG_TILEMAP,X + cmp #49 ; red key tile + bne not_red_tile + lda #2 ; lblue bg tile +rwr_smc2: + sta BIG_TILEMAP,X +not_red_tile: + inx + bne remove_red_wall_loop + + inc rwr_smc1+2 + inc rwr_smc2+2 + + lda rwr_smc1+2 + cmp #(>BIG_TILEMAP)+40 + bne remove_red_wall_outer + + ; refresh local tilemap + + jsr copy_tilemap_subset + + jsr rumble_noise + + jmp done_up_action + + + ; check if it's the exit +check_if_exit: + + ; exit is 296,148 + ; 296,148 (-80,-12) -> 216,136 -> (/4,/4) -> 54,34 + + lda XPOS + cmp #54 + beq exit_x + + cmp #55 + bne done_up_action + +exit_x: + lda YPOS + cmp #34 + bne done_up_action + + ; check that we have the key + lda INVENTORY + and #INV_RED_KEY + beq done_up_action + + lda #1 + sta DOOR_ACTIVATED + + sta FRAMEL ; restart timer + +done_up_action: + + rts + + + ;========================== + ; open the door, end level + ;========================== +check_open_door: + lda DOOR_ACTIVATED + beq done_open_door + + asl + tay + lda door_opening,Y + sta INL + lda door_opening+1,Y + sta INH + + ; need to find actual door location + ; it's at 54,34 + ; Y is going to be at 20 unless something weird is going on + ; X is going to be ((54-TILE_X)+2)*2 + + lda #56 + sec + sbc TILEMAP_X + asl + sta XPOS + + lda #20 + sta YPOS + + jsr put_sprite_crop + + lda FRAMEL + and #$7 + bne done_open_door + + ; increment + + inc DOOR_ACTIVATED + lda DOOR_ACTIVATED + cmp #6 + bne done_open_door + + jsr level_end + +done_open_door: + rts + + + +door_opening: + .word door_sprite0 + .word door_sprite0 + .word door_sprite1 + .word door_sprite2 + .word door_sprite1 + .word door_sprite0 + +door_sprite0: + .byte 4,4 + .byte $15,$55,$55,$f5 + .byte $55,$f5,$5f,$55 + .byte $25,$25,$25,$25 + .byte $55,$55,$55,$55 + +door_sprite1: + .byte 4,4 + .byte $51,$f5,$f5,$5f + .byte $55,$05,$05,$50 + .byte $05,$50,$50,$55 + .byte $52,$55,$55,$52 + +door_sprite2: + .byte 4,4 + .byte $f5,$05,$05,$f0 + .byte $55,$00,$00,$55 + .byte $55,$00,$00,$55 + .byte $05,$50,$50,$25 + diff --git a/games/keen/draw_keen.s b/games/keen/draw_keen.s new file mode 100644 index 00000000..43a98a11 --- /dev/null +++ b/games/keen/draw_keen.s @@ -0,0 +1,120 @@ + + ;========================= + ; draw keen + ;========================= +draw_keen: + + lda KEEN_X + sta XPOS + lda KEEN_Y + sta YPOS + + lda KEEN_DIRECTION + bmi keen_facing_left + + ;========================= + ; facing right + +keen_facing_right: + +check_falling_right: + lda KEEN_FALLING + beq check_jumping_right +draw_falling_right: + ldx #keen_sprite_falling_right + jmp actually_draw_keen + +check_jumping_right: + lda KEEN_JUMPING + beq check_shooting_right +draw_jumping_right: + ldx #keen_sprite_jumping_right + jmp actually_draw_keen + +check_shooting_right: + lda KEEN_SHOOTING + beq check_walking_right +draw_shooting_right: + ldx #keen_sprite_shooting_right + dec KEEN_SHOOTING + jmp actually_draw_keen + +check_walking_right: + lda KEEN_WALKING + beq draw_standing_right +draw_walking_right: + lda FRAMEL + and #$02 + beq draw_standing_right + + ldx #keen_sprite_walking_right + jmp actually_draw_keen + +draw_standing_right: + ldx #keen_sprite_stand_right + jmp actually_draw_keen + + + ;================== + ; facing left +keen_facing_left: + +check_falling_left: + lda KEEN_FALLING + beq check_jumping_left +draw_falling_left: + ldx #keen_sprite_falling_left + jmp actually_draw_keen + +check_jumping_left: + lda KEEN_JUMPING + beq check_shooting_left +draw_jumping_left: + ldx #keen_sprite_jumping_left + jmp actually_draw_keen + +check_shooting_left: + lda KEEN_SHOOTING + beq check_walking_left +draw_shooting_left: + ldx #keen_sprite_shooting_left + dec KEEN_SHOOTING + jmp actually_draw_keen + +check_walking_left: + lda KEEN_WALKING + beq draw_standing_left +draw_walking_left: + lda FRAMEL + and #$02 + beq draw_standing_left + + ldx #keen_sprite_walking_left + jmp actually_draw_keen + +draw_standing_left: + ldx #keen_sprite_stand_left + jmp actually_draw_keen + + + ;==================== + ; actually draw + +actually_draw_keen: + stx INL + sta INH + jsr put_sprite_crop + + rts + + diff --git a/games/keen/draw_tilemap.s b/games/keen/draw_tilemap.s new file mode 100644 index 00000000..677dd7a0 --- /dev/null +++ b/games/keen/draw_tilemap.s @@ -0,0 +1,143 @@ + ;================================ + ; draw local tilemap to screen + ;================================ + +draw_tilemap: + ldy #0 ; Y on screen currently drawing + sty tiley ; we draw two at a time + + ldx #1 ; offset in current screen + stx tilemap_offset ; tilemap + + lda #0 ; init odd/even + sta tile_odd + +tilemap_outer_loop: + ldy tiley ; setup pointer to current Y + lda gr_offsets,Y + sta GBASL + lda gr_offsets+1,Y + clc + adc DRAW_PAGE + sta GBASH + + ldy #6 ; we draw in window 6->34 +tilemap_loop: + ldx tilemap_offset ; get actual tile + lda TILEMAP,X + + asl ; *4 ; get offset in tile + asl + tax + + lda tile_odd + beq not_odd_line + inx + inx +not_odd_line: + + lda TILES,X ; draw two tiles + cmp #$AA ; transparency + beq skip_tile1 + sta (GBASL),Y +skip_tile1: + + iny + lda TILES+1,X + cmp #$AA + beq skip_tile2 + sta (GBASL),Y +skip_tile2: + iny + + inc tilemap_offset + + cpy #34 ; until done + bne tilemap_loop + + ; move to next line + lda tile_odd ; toggle odd/even + eor #$1 ; (should we just add/mask?) + sta tile_odd + bne move_to_odd_line + +move_to_even_line: + lda tilemap_offset + clc + adc #2 + jmp done_move_to_line + +move_to_odd_line: + lda tilemap_offset + sec + sbc #14 + +done_move_to_line: + sta tilemap_offset + + ldy tiley ; move to next line + iny + iny + sty tiley + + cpy #40 ; check if at end + bne tilemap_outer_loop + + rts + + ; these should probably be in the zero page +tilemap_offset: .byte $00 +tile_odd: .byte $00 +tiley: .byte $00 + + + ;=================================== + ; copy tilemap + ;=================================== + ; want to copy a 16x10 area from global tileset to local + + ; default at first we want to start at 128,88 + ; which is 13, 20??? + +copy_tilemap_subset: + + ; set start + lda TILEMAP_Y + clc + adc #>BIG_TILEMAP + sta cptl1_smc+2 ; set proper row in big tilemap + adc #$10 + sta cptl3_smc+1 ; set loop limit + + ; reset row + lda # 76,80 -> (/4,/4) -> 19,20 + .byte 1 ; out + .byte 0 ; exploding + .byte ENEMY_CAMERA ; type + .byte $ff ; direction + .byte 19,20 ; tilex,tiley + .byte 0,0 ; x,y + +enemy1: + ; 272,92 (-80,-12) -> 192,80 -> (/4,/4) -> 48,20 + .byte 1 ; out + .byte 0 ; exploding + .byte ENEMY_CAMERA ; type + .byte $ff ; direction + .byte 48,20 ; tilex,tiley + .byte 0,0 ; x,y + +enemy2: + ; 156,112 (-80,-12) -> 76,100 -> (/4,/4) -> 19,25 + .byte 1 ; out + .byte 0 ; exploding + .byte ENEMY_CRAWLER ; type + .byte $ff ; direction + .byte 19,25 ; tilex,tiley + .byte 0,0 ; x,y + +enemy3: + ; 184,116 (-80,-12) -> 104,104 -> (/4,/4) -> 26,26 + .byte 1 ; out + .byte 0 ; exploding + .byte ENEMY_BOT ; type + .byte $ff ; direction + .byte 26,26 ; tilex,tiley + .byte 0,0 ; x,y + + diff --git a/games/keen/gr_offsets.s b/games/keen/gr_offsets.s new file mode 100644 index 00000000..d3af91f7 --- /dev/null +++ b/games/keen/gr_offsets.s @@ -0,0 +1,5 @@ +gr_offsets: + .word $400,$480,$500,$580,$600,$680,$700,$780 + .word $428,$4a8,$528,$5a8,$628,$6a8,$728,$7a8 + .word $450,$4d0,$550,$5d0,$650,$6d0,$750,$7d0 + diff --git a/games/keen/gr_pageflip.s b/games/keen/gr_pageflip.s new file mode 100644 index 00000000..f199f5be --- /dev/null +++ b/games/keen/gr_pageflip.s @@ -0,0 +1,24 @@ + ;========== + ; page_flip + ;========== + +page_flip: + lda DISP_PAGE ; 3 + beq page_flip_show_1 ; 2nt/3 +page_flip_show_0: + bit PAGE0 ; 4 + lda #4 ; 2 + sta DRAW_PAGE ; DRAW_PAGE=1 ; 3 + lda #0 ; 2 + sta DISP_PAGE ; DISP_PAGE=0 ; 3 + rts ; 6 +page_flip_show_1: + bit PAGE1 ; 4 + sta DRAW_PAGE ; DRAW_PAGE=0 ; 3 + lda #1 ; 2 + sta DISP_PAGE ; DISP_PAGE=1 ; 3 + rts ; 6 + ;==================== + ; DISP_PAGE=0 26 + ; DISP_PAGE=1 24 + diff --git a/games/keen/gr_putsprite_crop.s b/games/keen/gr_putsprite_crop.s new file mode 100644 index 00000000..72fd8ab2 --- /dev/null +++ b/games/keen/gr_putsprite_crop.s @@ -0,0 +1,359 @@ + ;============================================= + ; put_sprite_crop + ;============================================= + ; Sprite to display in INH,INL + ; Location is XPOS,YPOS + ; Note, only works if YPOS is multiple of two + + ; transparent color is $A (grey #2) + ; this means we can have black ($0) in a sprite + + ; FIXME: force YPOS to be even? + +put_sprite_crop: + + ldy #0 ; byte 0 is xsize ; 2 + lda (INL),Y ; 5 + sta CH ; xsize is in CH ; 3 + iny ; 2 + clc + adc XPOS + sta XMAX + + lda (INL),Y ; byte 1 is ysize ; 5 + sta CV ; ysize is in CV ; 3 + iny ; 2 + + lda YPOS ; make a copy of ypos ; 3 + sta TEMPY ; as we modify it ; 3 + ;=========== + ; 28 +put_sprite_crop_loop: + sty TEMP ; save sprite pointer ; 3 + ldy TEMPY ; 3 + + bpl put_sprite_crop_pos ; if < 0, skip to next + + clc ; skip line in sprite too + lda TEMP + adc CH + tay + + bne crop_increment_y + +put_sprite_crop_pos: + +psc_smc1: + cpy #48 ; bge if >= 48, done sprite + bcs crop_sprite_done + + + lda gr_offsets,Y ; lookup low-res memory address ; 4 + clc ; 2 + adc XPOS ; add in xpos ; 3 + sta OUTL ; store out low byte of addy ; 3 + clc ; never wraps, handle negative + lda gr_offsets+1,Y ; look up high byte ; 4 + adc DRAW_PAGE ; ; 3 + sta OUTH ; and store it out ; 3 + ldy TEMP ; restore sprite pointer ; 3 + + ; OUTH:OUTL now points at right place + + ldx XPOS ; load xposition into x ; 3 + ;=========== + ; 34 +crop_put_sprite_pixel: + lda (INL),Y ; get sprite colors ; 5 + iny ; increment sprite pointer ; 2 + sty TEMP ; save sprite pointer ; 3 + + + cpx #0 ; if off-screen left, skip draw + bmi skip_drawing + cpx #40 + bcs skip_drawing ; if off-screen right, skip draw + + ldy #$0 ; 2 + + ; check if completely transparent + ; if so, skip + + cmp #$aa ; if all zero, transparent ; 2 + beq crop_put_sprite_done_draw ; don't draw it ; 2nt/3 + ;============== + ; 16/17 + + sta COLOR ; save color for later ; 3 + + ; check if top pixel transparent + + and #$f0 ; check if top nibble zero ; 2 + cmp #$a0 + bne crop_put_sprite_bottom ; if not skip ahead ; 2nt/3 + ;============== + ; 7/8 + + lda COLOR + and #$0f + sta COLOR + + lda #$f0 ; setup mask ; 2 + sta MASK ; 3 + bmi crop_put_sprite_mask ; always? ; 3 + ;============= + ; 8 + +crop_put_sprite_bottom: + lda COLOR ; re-load color ; 3 + and #$0f ; check if bottom nibble zero ; 2 + cmp #$0a + bne crop_put_sprite_all ; if not, skip ahead ; 2nt/3 + ;============= + ; 7/8 + + lda COLOR + and #$f0 + sta COLOR + lda #$0f ; 2 + sta MASK ; setup mask ; 3 + ;=========== + ; 5 + +crop_put_sprite_mask: + lda (OUTL),Y ; get color at output ; 5 + and MASK ; mask off unneeded part ; 3 + ora COLOR ; or the color in ; 3 + sta (OUTL),Y ; store it back ; 6 + + jmp crop_put_sprite_done_draw ; we are done ; 3 + ;=========== + ; 20 + +crop_put_sprite_all: + lda COLOR ; load color ; 3 + sta (OUTL),Y ; and write it out ; 6 + ;============ + ; 9 + +crop_put_sprite_done_draw: +skip_drawing: + + ldy TEMP ; restore sprite pointer ; 3 + + inc OUTL ; increment output pointer ; 5 + inx ; increment x counter ; 2 + cpx XMAX + bne crop_put_sprite_pixel ; if not done, keep looping ; 2nt/3 + ;============== + ; 12/13 +crop_increment_y: + + inc TEMPY ; each line has two y vars ; 5 + inc TEMPY ; 5 + dec CV ; decemenet total y count ; 5 + bne put_sprite_crop_loop ; loop if not done ; 2nt/3 + ;============== + ; 17/18 +crop_sprite_done: + rts ; return ; 6 + + + +; 0,0 = 400+0 +; -1,0 = 400+ff=4ff, inc=400 + +; sprite: 5x4 +; +; -2,0 Xstart=0, sprite_offset=2, xsize=3 +; -1,0, Xstart=0, sprite_offset=1, xsize=4 +; 0,0, Xstrat=0, sprite_offset=0, xsize=5 +; 1,0, Xstart=1, sprite_offset=0, xsize=5 +; +; 39,0 Xstart=39, sprite_offset=0, xsize=1 +; +; +; +; + + +.if 0 + ;============================================= + ; put_sprite_flipped_crop + ;============================================= + ; Sprite to display in INH,INL + ; Location is XPOS,YPOS + ; Note, only works if YPOS is multiple of two + + ; transparent color is $A (grey #2) + ; this means we can have black ($0) in a sprite + + +put_sprite_flipped_crop: + + ldy #0 ; byte 0 is xsize ; 2 + lda (INL),Y ; 5 + sta CH ; xsize is in CH ; 3 + iny ; 2 + + + lda (INL),Y ; byte 1 is ysize ; 5 + sta CV ; ysize is in CV ; 3 + dey ; make Y zero again ; 2 + + lda INH ; ??? + sta ppfc_smc+2 + clc + lda INL + adc #1 ; add one (not two) because X counts + ; from CH to 1 (not CH-1 to 0) + sta ppfc_smc+1 + bcc psfc16 + inc ppfc_smc+2 +psfc16: + + + lda YPOS ; make a copy of ypos ; 3 + + sta TEMPY ; as we modify it ; 3 + ;=========== + ; 28 + + + +put_spritefc_loop: +; sty TEMP ; save sprite pointer ; 3 + ldy TEMPY ; 3 + + bmi fcrop_increment_y ; if < 0, skip to next +psc_smc2: + cpy #48 ; bge if >= 48, done sprite + bcs fcrop_sprite_done + + + lda gr_offsets,Y ; lookup low-res memory address ; 4 + clc ; 2 + adc XPOS ; add in xpos ; 3 + sta OUTL ; store out low byte of addy ; 3 + lda gr_offsets+1,Y ; look up high byte ; 4 + clc + adc DRAW_PAGE ; ; 3 + sta OUTH ; and store it out ; 3 +; ldy TEMP ; restore sprite pointer ; 3 + + ; OUTH:OUTL now points at right place + + ldx CH ; load xsize into x ; 3 + ;=========== + ; 34 +put_spritefc_pixel: + + clc + txa ; want (CH-X-1)+XPOS + eor #$ff + adc CH + adc XPOS + + bmi cskip_drawing + cmp #40 + + bcs cskip_drawing ; if off-screen right, skip draw + +ppfc_smc: + lda $C000,X ; get sprite colors ; 5 +; iny ; increment sprite pointer ; 2 +; sty TEMP ; save sprite pointer ; 3 + ldy #$0 ; 2 + + ; check if completely transparent + ; if so, skip + + cmp #$aa ; if all zero, transparent ; 2 + beq put_spritefc_done_draw ; don't draw it ; 2nt/3 + ;============== + ; 16/17 + + sta COLOR ; save color for later ; 3 + + ; check if top pixel transparent + + and #$f0 ; check if top nibble zero ; 2 + cmp #$a0 + bne put_spritefc_bottom ; if not skip ahead ; 2nt/3 + ;============== + ; 7/8 + + lda COLOR + and #$0f + sta COLOR + + lda #$f0 ; setup mask ; 2 + sta MASK ; 3 + bmi put_spritefc_mask ; always? ; 3 + ;============= + ; 8 + +put_spritefc_bottom: + lda COLOR ; re-load color ; 3 + and #$0f ; check if bottom nibble zero ; 2 + cmp #$0a + bne put_spritefc_all ; if not, skip ahead ; 2nt/3 + ;============= + ; 7/8 + + lda COLOR + and #$f0 + sta COLOR + lda #$0f ; 2 + sta MASK ; setup mask ; 3 + ;=========== + ; 5 + +put_spritefc_mask: + lda (OUTL),Y ; get color at output ; 5 + and MASK ; mask off unneeded part ; 3 + ora COLOR ; or the color in ; 3 + sta (OUTL),Y ; store it back ; 6 + + jmp put_spritefc_done_draw ; we are done ; 3 + ;=========== + ; 20 + +put_spritefc_all: + lda COLOR ; load color ; 3 + sta (OUTL),Y ; and write it out ; 6 + ;============ + ; 9 + +put_spritefc_done_draw: +cskip_drawing: +; ldy TEMP ; restore sprite pointer ; 3 + + inc OUTL ; increment output pointer ; 5 + dex ; decrement x counter ; 2 + bne put_spritefc_pixel ; if not done, keep looping ; 2nt/3 + ;============== + ; 12/13 +fcrop_increment_y: + inc TEMPY ; each line has two y vars ; 5 + inc TEMPY ; 5 + + lda CH + clc + adc ppfc_smc+1 + sta ppfc_smc+1 + bcc psfco + inc ppfc_smc+2 +psfco: + + dec CV ; decemenet total y count ; 5 + beq fcrop_sprite_done ; loop if not done ; 2nt/3 + jmp put_spritefc_loop + ;============== + ; 17/18 + +fcrop_sprite_done: + rts ; return ; 6 + + +.endif diff --git a/games/keen/graphics/Makefile b/games/keen/graphics/Makefile index 8edb44e5..6fb28fa5 100644 --- a/games/keen/graphics/Makefile +++ b/games/keen/graphics/Makefile @@ -10,7 +10,7 @@ PNG2GR = ../../../utils/gr-utils/png2gr PNG2SPRITES = ../../../utils/gr-utils/png2sprites HGR_SPRITE = ../../../utils/hgr-utils/hgr_make_sprite -all: keen1_title.hgr.zx02 +all: keen1_title.hgr.zx02 level1_bg.gr.zx02 #### @@ -27,6 +27,15 @@ keen1_title.hgr: keen1_title.png #### +level1_bg.gr.zx02: level1_bg.gr + $(ZX02) level1_bg.gr level1_bg.gr.zx02 + +level1_bg.gr: level1_bg.png + $(PNG2GR) level1_bg.png level1_bg.gr + + +#### + clean: rm -f *~ *.o *.lst *.zx02 *.hgr diff --git a/games/keen/graphics/level1_bg.png b/games/keen/graphics/level1_bg.png new file mode 100644 index 00000000..9a6005f1 Binary files /dev/null and b/games/keen/graphics/level1_bg.png differ diff --git a/games/keen/handle_laser.s b/games/keen/handle_laser.s new file mode 100644 index 00000000..f3fe8469 --- /dev/null +++ b/games/keen/handle_laser.s @@ -0,0 +1,100 @@ + ; draw/move laser + + ; o/~ carrying a laser, down the road that I must travel o/~ + ; o/~ carrying a laser, in the darkness of the night o/~ + + ;==================== + ; move laser + ;==================== +move_laser: + lda LASER_OUT + beq done_move_laser + + lda LASER_X + clc + adc LASER_DIRECTION + sta LASER_X + +laser_check_tiles: + ; collision detect with tiles + + ; laser location is roughly + ; (y/4)*16 + (x/2) - 2 + lda LASER_Y + lsr + lsr + asl + asl + asl + asl + sta LASER_TILE + lda LASER_X + lsr + clc + adc LASER_TILE + sec + sbc #2 + sta LASER_TILE + + ldx LASER_TILE + lda TILEMAP,X + cmp #HARD_TILES + bcs destroy_laser + + +laser_check_enemies: + ; collision detect with enemies + + jsr laser_enemies + + + ; detect if off screen +laser_check_right: + lda LASER_X + cmp #31 + bcc laser_check_left ; not_too_far_right + bcs destroy_laser + +laser_check_left: + cmp #6 + bcs done_move_laser + bcc destroy_laser + +destroy_laser: + lda #0 + sta LASER_OUT + +done_move_laser: + rts + + ;==================== + ; draw laser + ;==================== + +draw_laser: + + lda LASER_OUT + beq done_draw_laser + + lda LASER_X + sta XPOS + lda LASER_Y + sta YPOS + +; lda LASER_DIRECTION + + + lda #laser_sideways_sprite + sta INH + jsr put_sprite_crop + +done_draw_laser: + rts + +laser_sideways_sprite: + .byte 4,1 +; .byte $3A,$cA,$3A,$cA + .byte $A3,$Ac,$A3,$Ac + diff --git a/games/keen/hello.bas b/games/keen/hello.bas new file mode 100644 index 00000000..0be6d720 --- /dev/null +++ b/games/keen/hello.bas @@ -0,0 +1,14 @@ +5 HOME +10 PRINT "LOADING KEEN V0.01" +20 PRINT " KEEN1 PROOF-OF-CONCEPT DEMAKE" +30 PRINT:PRINT +70 PRINT "BASED ON KEEN1 BY ID" +75 PRINT:PRINT +80 PRINT "APPLE II PORT: VINCE WEAVER" +90 PRINT "DISK CODE : QKUMBA" +95 PRINT +100 PRINT " ______" +110 PRINT " A \/\/\/ SOFTWARE PRODUCTION" +115 PRINT +120 PRINT " HTTP://WWW.DEATER.NET/WEAVE/VMWPROD" +130 PRINT CHR$(4);"BRUN LOADER" diff --git a/games/keen/init_vars.s b/games/keen/init_vars.s index 538b9b1d..e7800fa8 100644 --- a/games/keen/init_vars.s +++ b/games/keen/init_vars.s @@ -9,16 +9,16 @@ init_vars: sta FRAMEH sta DISP_PAGE sta JOYSTICK_ENABLED - sta DUKE_WALKING - sta DUKE_JUMPING + sta KEEN_WALKING + sta KEEN_JUMPING sta LEVEL_OVER sta LASER_OUT - sta DUKE_XL + sta KEEN_XL sta SCORE0 sta SCORE1 sta SCORE2 - sta DUKE_FALLING - sta DUKE_SHOOTING + sta KEEN_FALLING + sta KEEN_SHOOTING sta KICK_UP_DUST sta DOOR_ACTIVATED sta INVENTORY diff --git a/games/keen/item_level1.s b/games/keen/item_level1.s new file mode 100644 index 00000000..043c7ae3 --- /dev/null +++ b/games/keen/item_level1.s @@ -0,0 +1,49 @@ + + ;================== + ; check for items + ;================== + ; X holds ?? +check_item: + + +check_red_key: + lda TILEMAP,X + cmp #31 ; red key + bne check_blue_key + + jsr pickup_noise + lda INVENTORY + ora #INV_RED_KEY + sta INVENTORY + + ; erase red key (304,96) + lda #0 + sta $A938 ; hack + + jsr copy_tilemap_subset + + jsr update_status_bar + + jmp done_check_item + +check_blue_key: + cmp #30 ; blue key + bne done_check_item + + jsr pickup_noise + lda INVENTORY + ora #INV_BLUE_KEY + sta INVENTORY + + ; erase blue key + lda #0 + sta $970c ; hack + + jsr copy_tilemap_subset + + jsr update_status_bar + + jmp keen_check_head + +done_check_item: + rts diff --git a/games/keen/joystick.s b/games/keen/joystick.s new file mode 100644 index 00000000..751fd039 --- /dev/null +++ b/games/keen/joystick.s @@ -0,0 +1,46 @@ +; Oliver Schmidt +; comp.sys.apple2.programmer + +; Call with joystick number (0 or 1) in A. +; Results are stored in value0 and value1. +; UPPER_THRESHOLD is the paddle value you want to consider as "right enough" / +; "down enough". + +UPPER_THRESHOLD = 128 + +;PTRIG = $c070 +PADDL1 = $C065 + +handle_joystick: + lda #0 + + ; Read both paddles simultaneously + asl ; Joystick number -> paddle number + tax + ldy #$00 + sty value0 + sty value1 + lda PTRIG ; Trigger paddles +loop: lda PADDL0,x ; Read paddle (0 or 2) + bmi set0 ; Cycles: 2 3 + nop ; Cycles: 2 + bpl nop0 ; Cycles: 3 +set0: sty value0 ; Cycles: 4 +nop0: ; - - + ; Cycles: 7 7 + lda PADDL1,x ; Read paddle (1 or 3) + bmi set1 ; Cycles: 2 3 + nop ; Cycles: 2 + bpl nop1 ; Cycles: 3 +set1: sty value1 ; Cycles: 4 +nop1: ; - - + ; Cycles: 7 7 + iny + cpy #UPPER_THRESHOLD+1 + bne loop + + rts + +value0: .byte $00 +value1: .byte $00 + diff --git a/games/keen/keen_level1.s b/games/keen/keen_level1.s new file mode 100644 index 00000000..b5ecb7fe --- /dev/null +++ b/games/keen/keen_level1.s @@ -0,0 +1,243 @@ +; Keen PoC Level 1 + +; by deater (Vince Weaver) + +; Zero Page + .include "zp.inc" + .include "hardware.inc" + .include "common_defines.inc" + +keen_start: + ;=================== + ; init screen + jsr TEXT + jsr HOME + bit KEYRESET + + bit SET_GR + bit PAGE0 + bit LORES + bit TEXTGR + + jsr clear_top ; avoid grey stripes at load + + ;===================== + ; init vars + ;===================== + + lda #0 + sta ANIMATE_FRAME + sta FRAMEL + sta FRAMEH + sta DISP_PAGE + sta JOYSTICK_ENABLED + sta KEEN_WALKING + sta KEEN_JUMPING + sta LEVEL_OVER + sta LASER_OUT + sta KEEN_XL + sta SCORE0 + sta SCORE1 + sta SCORE2 + sta KEEN_FALLING + sta KEEN_SHOOTING + sta KICK_UP_DUST + sta DOOR_ACTIVATED + sta INVENTORY + + lda #enemy_data + sta ENEMY_DATAH + + ; FIXME: temporary +; lda INVENTORY +; ora #INV_RED_KEY +; sta INVENTORY + +; lda #$10 +; sta SCORE0 + + lda #1 + sta FIREPOWER + + lda #2 ; draw twice (both pages) + sta UPDATE_STATUS + + lda #7 + sta HEALTH + + lda #4 + sta DRAW_PAGE + + lda #18 + sta KEEN_X + lda #0 + sta KEEN_Y + lda #1 + sta KEEN_DIRECTION + + + jsr update_status_bar + + ;==================================== + ; load level1 background + ;==================================== + + lda #level1_bg_zx02 + sta ZX0_src+1 + + lda #$c ; load to page $c00 + + jsr full_decomp + + + ;==================================== + ; load level1 tilemap + ;==================================== + + lda #level1_data_zx02 + sta ZX0_src+1 + lda #$90 ; load to page $9000 + jsr full_decomp + + ;==================================== + ; copy in tilemap subset + ;==================================== + lda #28 + sta TILEMAP_X + lda #0 + sta TILEMAP_Y + + jsr copy_tilemap_subset + + ;==================================== + ;==================================== + ; Main LOGO loop + ;==================================== + ;==================================== + +keen_loop: + + ; copy over background + + jsr gr_copy_to_current + + ; draw tilemap + + jsr draw_tilemap + + ; draw enemies + + jsr draw_enemies + + ; draw laser + + jsr draw_laser + + ; draw keen + + jsr draw_keen + + ; handle door opening + + jsr check_open_door + + ; draw a status bar + + jsr draw_status_bar + + jsr page_flip + + jsr handle_keypress + + jsr move_keen + +; jsr move_enemies + + jsr move_laser + + + ;======================== + ; increment frame count + ;======================== + + inc FRAMEL + bne no_frame_oflo + inc FRAMEH +no_frame_oflo: + + ;=========================== + ; check end of level + ;=========================== + + lda LEVEL_OVER + beq do_keen_loop + + jmp done_with_keen + + + + +do_keen_loop: + + ; delay +; lda #200 +; jsr WAIT + + jmp keen_loop + + +done_with_keen: + bit KEYRESET ; clear keypress + + + lda #LOAD_KEEN2 + sta WHICH_LOAD + + rts ; exit back + + + ;========================== + ; includes + ;========================== + + ; level graphics +level1_bg_zx02: + .incbin "graphics/level1_bg.gr.zx02" + + .include "text_print.s" + .include "gr_offsets.s" + .include "gr_fast_clear.s" + .include "gr_copy.s" + .include "gr_pageflip.s" + .include "gr_putsprite_crop.s" + .include "zx02_optim.s" + + .include "status_bar.s" + .include "keyboard.s" + .include "joystick.s" + + .include "text_drawbox.s" + .include "print_help.s" + .include "quit_yn.s" + .include "level_end.s" + + .include "draw_keen.s" + .include "keen_sprites.inc" + .include "move_keen.s" + .include "handle_laser.s" + .include "draw_tilemap.s" + .include "enemies_level1.s" + .include "actions_level1.s" + .include "item_level1.s" + + .include "sound_effects.s" + .include "speaker_tone.s" + +level1_data_zx02: + .incbin "maps/level1_map.zx02" diff --git a/games/keen/keen_title.s b/games/keen/keen_title.s index 262e51e6..364ecbae 100644 --- a/games/keen/keen_title.s +++ b/games/keen/keen_title.s @@ -186,7 +186,7 @@ done_intro: ;============================ ; set up initial location - lda #LOAD_DUKE1 + lda #LOAD_KEEN1 sta WHICH_LOAD ; start at first level rts diff --git a/games/keen/keyboard.s b/games/keen/keyboard.s new file mode 100644 index 00000000..7cab8b56 --- /dev/null +++ b/games/keen/keyboard.s @@ -0,0 +1,251 @@ +JUMP_HEIGHT = 8 + + ;============================== + ; Handle Keypress + ;============================== +handle_keypress: + + ; first handle joystick + lda JOYSTICK_ENABLED + beq actually_handle_keypress + + ; only check joystick every-other frame + lda FRAMEL + and #$1 + beq actually_handle_keypress + +check_button: + lda PADDLE_BUTTON0 + bpl button_clear + + lda JS_BUTTON_STATE + bne js_check + + lda #1 + sta JS_BUTTON_STATE + lda #' ' + jmp check_sound + +button_clear: + lda #0 + sta JS_BUTTON_STATE + +js_check: + jsr handle_joystick + +js_check_left: + lda value0 + cmp #$20 + bcs js_check_right ; if less than 32, left + lda #'A' + bne check_sound + +js_check_right: + cmp #$40 + bcc js_check_up + lda #'D' + bne check_sound + +js_check_up: + lda value1 + cmp #$20 + bcs js_check_down + lda #'W' + + bne check_sound + +js_check_down: + cmp #$40 + bcc done_joystick + lda #'S' + bne check_sound + + +done_joystick: + + + +actually_handle_keypress: + lda KEYPRESS + bmi keypress + + jmp no_keypress + +keypress: + and #$7f ; clear high bit + cmp #' ' + beq check_sound ; make sure not to lose space + and #$df ; convert uppercase to lower case + +check_sound: + cmp #$14 ; control-T + bne check_help + + lda SOUND_STATUS + eor #SOUND_DISABLED + sta SOUND_STATUS + jmp done_keypress + +check_help: + cmp #'H' ; H (^H is same as left) + bne check_joystick + + jsr print_help + jmp done_keypress + + ; can't be ^J as that's the same as down +check_joystick: + cmp #'J' ; J + bne check_left + + lda JOYSTICK_ENABLED + eor #1 + sta JOYSTICK_ENABLED + jmp done_keypress + +check_left: + cmp #'A' + beq left_pressed + cmp #8 ; left key + bne check_right +left_pressed: + + lda KEEN_DIRECTION + cmp #$ff ; check if facing left + bne face_left + + lda #1 + sta KEEN_WALKING + jmp done_left_pressed + +face_left: + lda #$ff + sta KEEN_DIRECTION + lda #0 + sta KEEN_WALKING + +done_left_pressed: + jmp done_keypress + +check_right: + cmp #'D' + beq right_pressed + cmp #$15 ; right key + bne check_up +right_pressed: + lda KEEN_DIRECTION + cmp #$1 ; check if facing right + bne face_right + + lda #1 + sta KEEN_WALKING + jmp done_left_pressed + +face_right: + lda #$1 + sta KEEN_DIRECTION + lda #0 + sta KEEN_WALKING + +done_right_pressed: + jmp done_keypress + +check_up: + cmp #'W' + beq up_pressed + cmp #$0B ; up key + bne check_down +up_pressed: + + jsr up_action + +done_up_pressed: + jmp done_keypress + +check_down: + cmp #'S' + beq down_pressed + cmp #$0A + bne check_space +down_pressed: + +done_down_pressed: + jmp done_keypress + +check_space: + cmp #' ' + bne check_return +space_pressed: + + ; jump + lda KEEN_JUMPING + bne done_keypress ; don't jump if already jumping + + lda KEEN_FALLING + bne done_keypress ; don't jump if falling + + lda #JUMP_HEIGHT + sta KEEN_JUMPING + + jsr jump_noise + + jmp done_keypress + +check_return: + cmp #13 + bne check_escape + +return_pressed: + + ; shoot + lda LASER_OUT + bne done_return + + jsr laser_noise + + lda KEEN_DIRECTION + sta LASER_DIRECTION + + lda #2 + sta KEEN_SHOOTING + + cmp #1 + beq laser_right +laser_left: + lda KEEN_X + sec + sbc #1 + jmp laser_assign + +laser_right: + lda KEEN_X + clc + adc #2 + +laser_assign: + sta LASER_X + + lda KEEN_Y + clc + adc #4 + sta LASER_Y + + inc LASER_OUT + +done_return: + jmp no_keypress + +check_escape: + cmp #27 + bne done_keypress + + jsr print_quit + + jmp done_keypress + + + +done_keypress: +no_keypress: + bit KEYRESET + rts diff --git a/games/keen/level_end.s b/games/keen/level_end.s new file mode 100644 index 00000000..2836b615 --- /dev/null +++ b/games/keen/level_end.s @@ -0,0 +1,65 @@ +; Print bonuses + +level_end: + bit KEYRESET ; clear keyboard + bit SET_TEXT + + lda #12 + sta drawbox_x1 + lda #26 + sta drawbox_x2 + lda #19 + sta drawbox_y1 + lda #23 + sta drawbox_y2 + + lda #21 + sta bonus_text+1 + +scroll_bonus_loop: + lda #' '|$80 + sta clear_all_color+1 + jsr clear_all + + jsr drawbox + + jsr normal_text + + lda #bonus_text + sta OUTH + jsr move_and_print_list + + jsr page_flip + + lda #220 + jsr WAIT + lda #220 + jsr WAIT + +level_end_wait: + lda KEYPRESS + bmi really_end_level + + dec drawbox_y1 + dec drawbox_y2 + dec bonus_text+1 + + lda drawbox_y1 + bmi really_end_level + + jmp scroll_bonus_loop + +really_end_level: + bit KEYRESET + lda #NEXT_LEVEL + sta LEVEL_OVER + + jsr clear_all + + rts + +bonus_text: +.byte 14,10,"BONUS: NONE",0 +.byte 255 diff --git a/games/keen/maps/LEVEL_RULES b/games/keen/maps/LEVEL_RULES new file mode 100644 index 00000000..dab4da83 --- /dev/null +++ b/games/keen/maps/LEVEL_RULES @@ -0,0 +1,3 @@ +Urgh I've completely forgotten all of this + +Fall through tiles that are < HARD_TILES which defaults to 32 diff --git a/games/keen/maps/Makefile b/games/keen/maps/Makefile new file mode 100644 index 00000000..c20515dc --- /dev/null +++ b/games/keen/maps/Makefile @@ -0,0 +1,34 @@ +CC = gcc +CFLAGS = -g -Wall -O2 + +ZX02 = ~/research/6502_compression/zx02.git/build/zx02 -f + +all: level1_map.zx02 png2map + +### + +level1_map.zx02: level1_map.inc + $(ZX02) level1_map.inc level1_map.zx02 + +level1_map.inc: level1_map.png png2map + ./png2map level1_map.png level1_map.inc + +### + +loadpng.o: loadpng.c loadpng.h + $(CC) $(CFLAGS) -c loadpng.c + +rle_common.o: rle_common.c rle_common.h + $(CC) $(CFLAGS) -c rle_common.c +### + +png2map: png2map.o loadpng.o + $(CC) $(LFLAGS) -o png2map png2map.o loadpng.o -lpng + +png2map.o: png2map.c loadpng.h + $(CC) $(CFLAGS) -c png2map.c + +### + +clean: + rm -f *~ *.o *.inc png2map diff --git a/games/keen/maps/level1_map.png b/games/keen/maps/level1_map.png new file mode 100644 index 00000000..8fe435b4 Binary files /dev/null and b/games/keen/maps/level1_map.png differ diff --git a/games/keen/maps/loadpng.c b/games/keen/maps/loadpng.c new file mode 100644 index 00000000..dae7fbaa --- /dev/null +++ b/games/keen/maps/loadpng.c @@ -0,0 +1,236 @@ +/* Loads a 80x48 (or 40x48) PNG image into a 40x48 Apple II layout */ +/* It's not interleaved like an actual Apple II */ +/* But the top/bottom are pre-packed into a naive 40x24 array */ + + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "loadpng.h" + +static int convert_color(int color, char *filename) { + + int c=0; + + switch(color) { + case 0x000000: c=0; break; /* black */ + case 0xe31e60: c=1; break; /* magenta */ + case 0x604ebd: c=2; break; /* dark blue */ + case 0xff44fd: c=3; break; /* purple */ + case 0x00a360: c=4; break; /* dark green */ + case 0x9c9c9c: c=5; break; /* grey 1 */ + case 0x14cffd: c=6; break; /* medium blue */ + case 0xd0c3ff: c=7; break; /* light blue */ + case 0x607203: c=8; break; /* brown */ + case 0xff6a3c: c=9; break; /* orange */ + case 0x9d9d9d: c=10; break; /* grey 2 */ + case 0xffa0d0: c=11; break; /* pink */ + case 0x14f53c: c=12; break; /* bright green */ + case 0xd0dd8d: c=13; break; /* yellow */ + case 0x72ffd0: c=14; break; /* aqua */ + case 0xffffff: c=15; break; /* white */ + default: + fprintf(stderr,"Unknown color %x, file %s\n", + color,filename); + exit(-1); + break; + } + + return c; +} + +/* expects a PNG where the xsize is either 1280x200 */ + +int loadpng(char *filename, unsigned char **image_ptr, int *xsize, int *ysize, + int png_type) { + + int x,y,ystart,yadd,xadd; + int color; + FILE *infile; + int debug=0; + unsigned char *image,*out_ptr; + int width, height; + int a2_color; + + png_byte bit_depth; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers; + png_byte color_type; + + unsigned char header[8]; + + /* open file and test for it being a png */ + infile = fopen(filename, "rb"); + if (infile==NULL) { + fprintf(stderr,"Error! Could not open %s\n",filename); + return -1; + } + + /* Check the header */ + fread(header, 1, 8, infile); + if (png_sig_cmp(header, 0, 8)) { + fprintf(stderr,"Error! %s is not a PNG file\n",filename); + return -1; + } + + /* initialize stuff */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fprintf(stderr,"Error create_read_struct\n"); + exit(-1); + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fprintf(stderr,"Error png_create_info_struct\n"); + exit(-1); + } + + png_init_io(png_ptr, infile); + png_set_sig_bytes(png_ptr, 8); + + png_read_info(png_ptr, info_ptr); + + width = png_get_image_width(png_ptr, info_ptr); + height = png_get_image_height(png_ptr, info_ptr); + + if (width==1280) { + *xsize=1280; + xadd=1; + } + else { + fprintf(stderr,"Unsupported width %d\n",width); + return -1; + } + + if (png_type==PNG_WHOLETHING) { + *ysize=height; + ystart=0; + yadd=1; + } + else { + fprintf(stderr,"Unknown type\n"); + return -1; + } + + color_type = png_get_color_type(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + + if (debug) { + printf("PNG: width=%d height=%d depth=%d\n",width,height,bit_depth); + if (color_type==PNG_COLOR_TYPE_RGB) printf("Type RGB\n"); + else if (color_type==PNG_COLOR_TYPE_RGB_ALPHA) printf("Type RGBA\n"); + else if (color_type==PNG_COLOR_TYPE_PALETTE) printf("Type palette\n"); + printf("Generating output size %d x %d\n",*xsize,*ysize); + } + +// number_of_passes = png_set_interlace_handling(png_ptr); + png_read_update_info(png_ptr, info_ptr); + + row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); + for (y=0; y>4); + } + a2_color&=0xf; + +// /* bottom color */ +// color=row_pointers[y+(yadd/2)][x/2]; +// if (x%2==0) { +// color=(color>>4); +// } +// color&=0xf; + +// a2_color|=(color<<4); + + if (debug) { + printf("%x ",a2_color); + } + + *out_ptr=a2_color; + out_ptr++; + + } + } + if (debug) printf("\n"); + } + } + else { + printf("Unknown color type\n"); + } + + *image_ptr=image; + + return 0; +} + + + + + diff --git a/games/keen/maps/loadpng.h b/games/keen/maps/loadpng.h new file mode 100644 index 00000000..51882f61 --- /dev/null +++ b/games/keen/maps/loadpng.h @@ -0,0 +1,9 @@ +#define PNG_WHOLETHING 0 +#define PNG_ODDLINES 1 +#define PNG_EVENLINES 2 + +int loadpng(char *filename, unsigned char **image_ptr, int *xsize, int *ysize, + int png_type); +int loadpng80(char *filename, unsigned char **image_ptr, int *xsize, int *ysize, + int png_type); + diff --git a/games/keen/maps/png2map.c b/games/keen/maps/png2map.c new file mode 100644 index 00000000..37ff27ba --- /dev/null +++ b/games/keen/maps/png2map.c @@ -0,0 +1,188 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "loadpng.h" + + +/* converts a png of map to format by our duke engine */ + +/* 1280x200 image */ +/* 256 sprites of size 2x4 in a 16x16 grid at 8,4 */ + + +static unsigned char tiles[256][2][4]; +static unsigned char tilemap[256][40]; +static unsigned char temp_tile[2][4]; + +static int ascii_output=0; + +int main(int argc, char **argv) { + + int i,j,x,y; + int numtiles=0,found_tile; + + unsigned char *image; + int xsize,ysize; + FILE *outfile; + + if (argc<3) { + fprintf(stderr,"Usage:\t%s INFILE OUTFILE\n\n",argv[0]); + exit(-1); + } + + outfile=fopen(argv[2],"w"); + if (outfile==NULL) { + fprintf(stderr,"Error! Could not open %s\n",argv[2]); + exit(-1); + } + + if (loadpng(argv[1],&image,&xsize,&ysize,PNG_WHOLETHING)<0) { + fprintf(stderr,"Error loading png!\n"); + exit(-1); + } + + fprintf(stderr,"Loaded image %d by %d\n",xsize,ysize); + +// for(x=0;x<128;x++) { +// for(y=0;y<64;y++) { +// printf("%02X,",image[(y*xsize)+x]); +// } +// printf("\n"); +// } + + /* loading tiles */ + for(x=0;x<16;x++) { + for(y=0;y<16;y++) { + tiles[(y*16)+x][0][0]=image[((y*4+4)*xsize)+8+(x*4)]; + tiles[(y*16)+x][1][0]=image[((y*4+4)*xsize)+8+(x*4)+2]; + tiles[(y*16)+x][0][1]=image[((y*4+5)*xsize)+8+(x*4)]; + tiles[(y*16)+x][1][1]=image[((y*4+5)*xsize)+8+(x*4)+2]; + tiles[(y*16)+x][0][2]=image[((y*4+6)*xsize)+8+(x*4)]; + tiles[(y*16)+x][1][2]=image[((y*4+6)*xsize)+8+(x*4)+2]; + tiles[(y*16)+x][0][3]=image[((y*4+7)*xsize)+8+(x*4)]; + tiles[(y*16)+x][1][3]=image[((y*4+7)*xsize)+8+(x*4)+2]; + } + } + + i=0; + for(j=0;j<256;j++) { + if ((tiles[j][0][0]!=0) || + (tiles[j][1][0]!=0) || + (tiles[j][0][1]!=0) || + (tiles[j][1][1]!=0) || + (tiles[j][0][2]!=0) || + (tiles[j][1][2]!=0) || + (tiles[j][0][3]!=0) || + (tiles[j][1][3]!=0)) { + numtiles=j+1; + } + } + + printf("Found %d tiles\n",numtiles); + + if (ascii_output) { + fprintf(outfile,"tiles:\n"); + for(i=0;i INT(26/4)*16 = 96 + 7 = 103 = 6R7 + + ; 0 = 32 (2) + ; 1 = 32 (2) + ; 2 = 32 (2) + ; 3 = 32 (2) + ; 4 = 48 (3) + ; 5 = 48 (3) + ; 6 = 48 (3) + ; 7 = 48 (3) + + lda KEEN_Y + + clc + adc #4 ; +4 + + lsr ; / 4 (INT) + lsr + + asl ; *4 + asl + asl + asl + + sta KEEN_FOOT_OFFSET + +; lda KEEN_DIRECTION +; bmi foot_left + +foot_right: + + lda KEEN_X + clc + adc #2 +; jmp foot_done + +;foot_left: +; lda KEEN_X +; sec +; sbc #1 + +foot_done: + lsr + + ; offset by two block at edge of screen + sec + sbc #2 + + clc + adc KEEN_FOOT_OFFSET + sta KEEN_FOOT_OFFSET + + rts + + + ;========================= + ; check_falling + ;========================= +check_falling: + + lda KEEN_JUMPING + bne done_check_falling ; don't check falling if jumping + + lda KEEN_FOOT_OFFSET + clc + adc #16 ; underfoot is on next row (+16) + + tax + lda TILEMAP,X + + ; if tile# < HARD_TILES then we fall + cmp #HARD_TILES + bcs feet_on_ground ; bge + + ;======================= + ; falling + + lda #1 + sta KEEN_FALLING + + ; scroll but only if Y>=20 (YDEFAULT) + + lda KEEN_Y + cmp #YDEFAULT + bcs scroll_fall ; bge + + inc KEEN_Y + inc KEEN_Y + jmp done_check_falling + + +scroll_fall: + inc TILEMAP_Y + + jsr copy_tilemap_subset + jmp done_check_falling + +feet_on_ground: + + ;=========================== + ; if had been falling + ; kick up dust, make noise + ; stop walking? + + lda KEEN_FALLING + beq was_not_falling + + ; clear falling + lda #0 + sta KEEN_FALLING + + lda #2 + sta KICK_UP_DUST + + lda #0 + sta KEEN_WALKING + + jsr land_noise + +was_not_falling: + ; check to see if Y still hi, if so scroll back down + + lda KEEN_Y + cmp #YDEFAULT + bcs done_check_falling ; bge + + ; too high up on screen, adjust down and also adjust tilemap down + + inc KEEN_Y + inc KEEN_Y + dec TILEMAP_Y ; share w above? + jsr copy_tilemap_subset + +done_check_falling: + rts + + + diff --git a/games/keen/quit_yn.s b/games/keen/quit_yn.s new file mode 100644 index 00000000..a1256c5c --- /dev/null +++ b/games/keen/quit_yn.s @@ -0,0 +1,56 @@ +; Quit? Are you sure? + +print_quit: + bit KEYRESET ; clear keyboard + bit SET_TEXT + + lda #' '|$80 + sta clear_all_color+1 + jsr clear_all + + lda #6 + sta drawbox_x1 + lda #33 + sta drawbox_x2 + lda #8 + sta drawbox_y1 + lda #13 + sta drawbox_y2 + jsr drawbox + + jsr normal_text + + lda #quit_text + sta OUTH + jsr move_and_print_list + + jsr page_flip + +query_quit: + lda KEYPRESS + bpl query_quit + bit KEYRESET + + cmp #'Y'|$80 + beq really_quit + cmp #'y'|$80 + beq really_quit + + bit SET_GR + rts + +really_quit: + lda #GAME_OVER + sta LEVEL_OVER + + lda #LOAD_TITLE + sta WHICH_LOAD + + rts + +quit_text: +.byte 8,10,"ARE YOU SURE YOU WANT TO",0 +.byte 14,11,"QUIT? (Y/N)",0 +.byte 255 diff --git a/games/keen/sound_effects.s b/games/keen/sound_effects.s new file mode 100644 index 00000000..fb1c0acd --- /dev/null +++ b/games/keen/sound_effects.s @@ -0,0 +1,149 @@ + ;====================== + ; noise when jump +jump_noise: + + lda SOUND_STATUS + bmi done_jump_noise + +; bit $C030 + +done_jump_noise: + rts + + + ;====================== + ; noise when bump head +head_noise: + + lda SOUND_STATUS + bmi done_head_noise + + lda #NOTE_D3 + sta speaker_frequency + lda #5 + sta speaker_duration + jsr speaker_tone + + +; bit $C030 +; bit $C030 + +done_head_noise: + rts + + + ;====================== + ; noise when land after jump +land_noise: + + lda SOUND_STATUS + bmi done_land_noise + +; bit $C030 + +done_land_noise: + rts + + + ;====================== + ; rumble noise +rumble_noise: + + lda SOUND_STATUS + bmi done_rumble_noise + + ldx #50 +rumble_red: + bit $C030 + lda #100 + jsr WAIT + dex + bne rumble_red + +done_rumble_noise: + rts + + + ;====================== + ; pickup noise + ; C, two octaves+C? +pickup_noise: + + lda SOUND_STATUS + bmi done_pickup_noise + + lda #NOTE_C3 + sta speaker_frequency + lda #25 + sta speaker_duration + jsr speaker_tone + + lda #NOTE_C5 + sta speaker_frequency + lda #20 + sta speaker_duration + jsr speaker_tone + +done_pickup_noise: + rts + + + ;====================== + ; buzzer noise + ; C, two octaves+C? +buzzer_noise: + + lda SOUND_STATUS + bmi done_buzzer_noise + + lda #NOTE_C3 + sta speaker_frequency + lda #10 + sta speaker_duration + jsr speaker_tone + +done_buzzer_noise: + rts + + + + + + ;====================== + ; enemy noise +enemy_noise: + + lda SOUND_STATUS + bmi done_enemy_noise + + lda #NOTE_A3 + sta speaker_frequency + lda #20 + sta speaker_duration + jsr speaker_tone + + lda #NOTE_A4 + sta speaker_frequency + lda #10 + sta speaker_duration + jsr speaker_tone + +done_enemy_noise: + rts + + ;====================== + ; laser noise +laser_noise: + + lda SOUND_STATUS + bmi done_enemy_noise + + lda #NOTE_D4 + sta speaker_frequency + lda #15 + sta speaker_duration + jsr speaker_tone + +done_laser_noise: + rts + diff --git a/games/keen/speaker_tone.s b/games/keen/speaker_tone.s new file mode 100644 index 00000000..069cf498 --- /dev/null +++ b/games/keen/speaker_tone.s @@ -0,0 +1,67 @@ +; based on code from here +; http://eightbitsoundandfury.ld8.org/programming.html + +; A,X,Y trashed +; duration also trashed + +NOTE_C3 = 255 +NOTE_CSHARP3 = 241 +NOTE_D3 = 227 +NOTE_DSHARP3 = 214 +NOTE_E3 = 202 +NOTE_F3 = 191 +NOTE_FSHARP3 = 180 +NOTE_G3 = 170 +NOTE_GSHARP3 = 161 +NOTE_A3 = 152 +NOTE_ASHARP3 = 143 +NOTE_B3 = 135 + +NOTE_C4 = 128 +NOTE_CSHARP4 = 121 +NOTE_D4 = 114 +NOTE_DSHARP4 = 108 +NOTE_E4 = 102 +NOTE_F4 = 96 +NOTE_FSHARP4 = 91 +NOTE_G4 = 85 +NOTE_GSHARP4 = 81 +NOTE_A4 = 76 +NOTE_ASHARP4 = 72 +NOTE_B4 = 68 + +NOTE_C5 = 64 +NOTE_CSHARP5 = 60 +NOTE_D5 = 57 +NOTE_DSHARP5 = 54 +NOTE_E5 = 51 +NOTE_F5 = 48 +NOTE_FSHARP5 = 45 +NOTE_G5 = 43 +NOTE_GSHARP5 = 40 +NOTE_A5 = 38 +NOTE_ASHARP5 = 36 +NOTE_B5 = 34 + + + +speaker_tone: + lda $C030 ; click speaker +speaker_loop: + dey ; y never set? + bne slabel1 ; duration roughly 256*? + dec speaker_duration ; (Duration) + beq done_tone +slabel1: + dex + bne speaker_loop + ldx speaker_frequency ; (Frequency) + jmp speaker_tone +done_tone: + rts + +speaker_duration: + .byte $00 +speaker_frequency: + .byte $00 + diff --git a/games/keen/status_bar.s b/games/keen/status_bar.s new file mode 100644 index 00000000..d167af36 --- /dev/null +++ b/games/keen/status_bar.s @@ -0,0 +1,177 @@ +; Draw Status Bar + + ;=========================== + ; inc_score_by_10 + ;=========================== + ; FIXME: make sure interrupt routine handles d flag properly +inc_score_by_10: + + sed + lda SCORE0 + clc + adc #$10 + sta SCORE0 + + lda SCORE1 + adc #0 + sta SCORE1 + + lda SCORE2 + adc #0 + sta SCORE2 + cld + + jsr update_score + + rts + + ;=========================== + ; update score + ;=========================== + +update_score: + + lda SCORE0 + and #$f + ora #$b0 ; 0 -> $b0 + sta status_string+6 + + lda SCORE0 + lsr + lsr + lsr + lsr + ora #$b0 ; 0 -> $b0 + sta status_string+5 + + lda SCORE1 + and #$f + ora #$b0 ; 0 -> $b0 + sta status_string+4 + + lda SCORE1 + lsr + lsr + lsr + lsr + ora #$b0 ; 0 -> $b0 + sta status_string+3 + + lda SCORE2 + and #$f + ora #$b0 ; 0 -> $b0 + sta status_string+2 + + lda #2 + sta UPDATE_STATUS + + rts + + + ;=========================== + ; update health + ;=========================== + +update_health: + + ldx #0 +update_health_loop: + cpx HEALTH + bcc health_on + lda #'_'|$80 + bne done_health +health_on: + lda #' ' +done_health: + sta status_string+9,X + + inx + cpx #8 + bne update_health_loop + + rts + + ;=========================== + ; update items + ;=========================== + +update_items: + + lda INVENTORY + + and #INV_RED_KEY + beq done_red_key + + lda #'R'&$3f + sta status_string+33 + +done_red_key: + + lda INVENTORY + + and #INV_BLUE_KEY + beq done_blue_key + + lda #'B'&$3f + sta status_string+35 + +done_blue_key: + + rts + + + + + ;=========================== + ; update the status bar + ;=========================== +update_status_bar: + + jsr update_score + + jsr update_health + + jsr update_items + + lda #2 + sta UPDATE_STATUS + + rts + + ;=========================== + ; draw the status bar + ;=========================== +draw_status_bar: + + ; to improve frame rate, only draw if update status set? + ; not implemented yet + + jsr inverse_text ; print help node + lda #help_string + sta OUTH + jsr move_and_print + + jsr normal_text ; (normal) + jsr move_and_print ; print explain text + jsr raw_text + jsr move_and_print ; print status line + rts + + +help_string: + .byte 3,20," PRESS 'H' FOR HELP ",0 + +score_string: + ; 012456789012345678901234567890123456789 + .byte 0,22,"SCORE HEALTH FIREPOWER INVENTORY",0 +status_string: +; .byte 0,23,"ZZZZZ XXXXXXXX =- ",0 + .byte 0,23,"ZZZZZ" + .byte ' '|$80,' '|$80 + .byte "XXXXXXXX" + .byte ' '|$80,' '|$80 + .byte '='|$80,'-'|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80 + .byte ' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,' '|$80,0 + diff --git a/games/keen/text_drawbox.s b/games/keen/text_drawbox.s new file mode 100644 index 00000000..8855c0bc --- /dev/null +++ b/games/keen/text_drawbox.s @@ -0,0 +1,84 @@ + ; draw inverse box on text screen + + ; we could use HLIN/VLIN instead? + + ; note Y should be in text coords (0..23) not GR (0..47) + +drawbox: + lda drawbox_y1 + jsr text_hlin + lda drawbox_y2 + jsr text_hlin + + jsr text_vlins + + rts + + ;======================== + ; text hlin + ;======================== + ; draw from x1,A to x2,A + +text_hlin: + asl + tay + + lda gr_offsets,Y + sta OUTL + + lda gr_offsets+1,Y + clc + adc DRAW_PAGE + sta OUTH + + lda #' ' + ldy drawbox_x1 +drawbox_hlin_loop: + sta (OUTL),Y + iny + cpy drawbox_x2 + bne drawbox_hlin_loop + + rts + + ;======================== + ; text vlin + ;======================== + ; draw from A,y1 to A,y2 + +text_vlins: + + ldx drawbox_y1 + +text_vlins_loop: + txa + asl + tay + lda gr_offsets,Y + sta OUTL + + lda gr_offsets+1,Y + clc + adc DRAW_PAGE + sta OUTH + + lda #' ' + + ldy drawbox_x1 + sta (OUTL),Y + ldy drawbox_x2 + sta (OUTL),Y + + inx + cpx drawbox_y2 + bcc text_vlins_loop + beq text_vlins_loop ; ble + + rts + + +drawbox_x1: .byte $00 +drawbox_x2: .byte $00 +drawbox_y1: .byte $00 +drawbox_y2: .byte $00 + diff --git a/games/keen/zp.inc b/games/keen/zp.inc index 25419a34..5d3c3036 100644 --- a/games/keen/zp.inc +++ b/games/keen/zp.inc @@ -88,12 +88,12 @@ PATTERN_H = $7F WHICH_LOAD = $80 ; which file to load -DUKE_XL = $81 -DUKE_X = $82 ; location of protagonist -DUKE_Y = $83 -DUKE_DIRECTION = $84 -DUKE_WALKING = $85 -DUKE_JUMPING = $86 +KEEN_XL = $81 +KEEN_X = $82 ; location of protagonist +KEEN_Y = $83 +KEEN_DIRECTION = $84 +KEEN_WALKING = $85 +KEEN_JUMPING = $86 LASER_OUT = $87 LASER_X = $88 @@ -102,7 +102,7 @@ LASER_DIRECTION = $8A TILEMAP_X = $8B TILEMAP_Y = $8C -DUKE_FOOT_OFFSET = $8D +KEEN_FOOT_OFFSET = $8D FIREPOWER = $8E INVENTORY = $8F @@ -116,8 +116,8 @@ SCORE1 = $92 SCORE2 = $93 UPDATE_STATUS = $94 -DUKE_FALLING = $95 -DUKE_SHOOTING = $96 +KEEN_FALLING = $95 +KEEN_SHOOTING = $96 KICK_UP_DUST = $97 SUPPRESS_WALK = $98 ENEMY_DATAL = $99