diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..09d4e6aa --- /dev/null +++ b/TODO.md @@ -0,0 +1,18 @@ +# Pending +* lowstack should be option instead of mode +* pressing b on multi page should go to top of page +* rework tests to fit multi mode +* add animation to cursor arrows +* menu options stored in sram +* option to default all +* multiple scoreboards +* clear scoreboard individually +* lowstack & crunch compatibility + + +# Bugs +* abss from game renders part of playfield in menu +* lowstack line doesn't respond to vert mirror flag +* lowstack nope gets mirrored with horiz mirror flag +* ghost piece briefly flashes at the bottom of the board during entry delay +* harddrop current piece sprite glitchy during lineclears diff --git a/build.js b/build.js index f1051ac2..8c61c0ba 100644 --- a/build.js +++ b/build.js @@ -23,6 +23,7 @@ if (args.includes('-h')) { -m mapper -a faster aeppoz + press select to end game +-A build anydas -s disable highscores/SRAM -k Famicom Keyboard support -w force WASM compiler @@ -97,6 +98,13 @@ if (args.includes('--')) { console.log(); +// build menu +if (!args.includes('-M')) { + console.time('menu'); + require('./src/gamemode/gametypemenu/menu'); + console.timeEnd('menu'); +} + // build / compress nametables console.time('nametables'); diff --git a/src/boot.asm b/src/boot.asm index 707d2ac2..8e01d24f 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -26,19 +26,24 @@ ; cpx #0 ; dex sets z flag bne @loop - ; default pace to A - lda #$A + lda #$FF sta paceModifier + sta floorModifier - lda #$10 + lda #NTSC_DAS sta dasModifier + lda #NTSC_ARR + sta arrModifier + + lda #MODE_TETRIS + sta practiseType lda #INITIAL_LINECAP_LEVEL sta linecapLevel - lda #INITIAL_LINECAP_LINES - sta linecapLines - lda #INITIAL_LINECAP_LINES_1 + lda #INITIAL_LINECAP_LINES_LO sta linecapLines+1 + lda #INITIAL_LINECAP_LINES_HI + sta linecapLines jsr resetScores diff --git a/src/chr/game_tileset.png b/src/chr/game_tileset.png index fee1c5ac..3ce4b37c 100644 Binary files a/src/chr/game_tileset.png and b/src/chr/game_tileset.png differ diff --git a/src/constants.asm b/src/constants.asm index f0560c57..509d980c 100644 --- a/src/constants.asm +++ b/src/constants.asm @@ -31,8 +31,12 @@ SWAP_DUTY_CYCLES := 0 ; counters the duty cycle swap present in some clone conso INITIAL_CUSTOM_LEVEL := 29 INITIAL_LINECAP_LEVEL := 39 -INITIAL_LINECAP_LINES := $30 ; bcd -INITIAL_LINECAP_LINES_1 := 3 ; hex (lol) + +; these bytes are currently 2 byte bcd in the menu +; they are swapped and will not work +INITIAL_LINECAP_LINES_LO := $30 ; bcd +INITIAL_LINECAP_LINES_HI := $03 ; bcd (tbd) + BTYPE_START_LINES := $25 ; bcd MENU_HIGHLIGHT_COLOR := $12 ; $12 in gym, $16 in original BLOCK_TILES := $7B @@ -56,152 +60,116 @@ BUTTON_SELECT := $20 BUTTON_START := $10 BUTTON_DPAD := BUTTON_UP | BUTTON_DOWN | BUTTON_LEFT | BUTTON_RIGHT -RENDER_LINES = $01 -RENDER_LEVEL = $02 -RENDER_SCORE = $04 -RENDER_DEBUG = $08 -RENDER_HZ = $10 -RENDER_STATS = $40 -RENDER_HIGH_SCORE_LETTER = $80 +RENDER_LINES := $01 +RENDER_LEVEL := $02 +RENDER_SCORE := $04 +RENDER_DEBUG := $08 +RENDER_HZ := $10 +RENDER_STATS := $40 +RENDER_HIGH_SCORE_LETTER := $80 .enum +MODE_DEFAULT MODE_TETRIS MODE_TSPINS -MODE_SEED -MODE_PARITY -MODE_PACE MODE_PRESETS +MODE_STACKING MODE_TYPEB -MODE_FLOOR -MODE_CRUNCH MODE_TAP +MODE_TAPQTY MODE_TRANSITION MODE_MARATHON -MODE_TAPQTY +MODE_DROUGHT MODE_CHECKERBOARD MODE_GARBAGE -MODE_DROUGHT -MODE_DAS MODE_LOWSTACK -MODE_KILLX2 -MODE_INVISIBLE -MODE_HARDDROP MODE_SPEED_TEST -MODE_SCORE_DISPLAY -MODE_CRASH -MODE_STRICT -MODE_HZ_DISPLAY -MODE_INPUT_DISPLAY -MODE_DISABLE_FLASH -MODE_DISABLE_PAUSE -MODE_DARK -MODE_GOOFY -MODE_DEBUG -MODE_LINECAP -MODE_DASONLY -MODE_QUAL -MODE_PAL -.if KEYBOARD = 1 -MODE_KEYBOARD -.endif +MODE_ANYDAS .endenum -.if KEYBOARD = 1 -MODE_QUANTITY = MODE_KEYBOARD + 1 -.else -MODE_QUANTITY = MODE_PAL + 1 -.endif +; .endum +; MODE_FLOOR +; MODE_CRUNCH +; MODE_DAS +; MODE_KILLX2 +; MODE_INVISIBLE +; MODE_HARDDROP +; MODE_SCORE_DISPLAY +; MODE_CRASH +; MODE_STRICT +; MODE_HZ_DISPLAY +; MODE_INPUT_DISPLAY +; MODE_DISABLE_FLASH +; MODE_DISABLE_PAUSE +; MODE_DARK +; MODE_GOOFY +; MODE_DEBUG +; MODE_LINECAP +; MODE_DASONLY +; MODE_QUAL +; MODE_PAL +; .if KEYBOARD = 1 +; MODE_KEYBOARD +; .endif +; .endenum -MODE_GAME_QUANTITY = MODE_HARDDROP + 1 +.enum +SCORING_CLASSIC +SCORING_LETTERS +SCORING_SEVENDIGIT +SCORING_FLOAT +SCORING_SCORECAP +SCORING_HIDDEN +.endenum -SCORING_CLASSIC := 0 ; for scoringModifier -SCORING_LETTERS := 1 -SCORING_SEVENDIGIT := 2 -SCORING_FLOAT := 3 -SCORING_SCORECAP := 4 -SCORING_HIDDEN := 5 +.enum +LINECAP_INACTIVE +LINECAP_KILLX2 +LINECAP_FLOOR +LINECAP_INVISIBLE +LINECAP_HALT +.endenum -LINECAP_KILLX2 := 1 -LINECAP_FLOOR := 2 -LINECAP_INVISIBLE := 3 -LINECAP_HALT := 4 +.enum +LINECAP_OFF +LINECAP_LEVEL +LINECAP_LINES +.endenum -CRASH_OFF := 0 -CRASH_SHOW := 1 -CRASH_TOPOUT := 2 -CRASH_CRASH := 3 +.enum +CRASH_OFF +CRASH_SHOW +CRASH_TOPOUT +CRASH_CRASH +.endenum -LINECAP_WHEN_STRING_OFFSET := $10 -LINECAP_HOW_STRING_OFFSET := $12 +LINECAP_WHEN_STRING_OFFSET := $FF +LINECAP_HOW_STRING_OFFSET := $2 MENU_SPRITE_Y_BASE := $46 MENU_MAX_Y_SCROLL := $A0 MENU_TOP_MARGIN_SCROLL := 7 ; in blocks -; menuConfigSizeLookup -; menu ram is defined at menuRAM in ./ram.asm -.macro MENUSIZES - .byte $0 ; MODE_TETRIS - .byte $0 ; MODE_TSPINS - .byte $0 ; MODE_SEED - .byte $0 ; MODE_PARITY - .byte $F ; MODE_PACE - .byte $7 ; MODE_PRESETS - .byte $8 ; MODE_TYPEB - .byte $C ; MODE_FLOOR - .byte $F ; MODE_CRUNCH - .byte $20 ; MODE_TAP - .byte $10 ; MODE_TRANSITION - .byte $4 ; MODE_MARATHON - .byte $1F ; MODE_TAPQTY - .byte $8 ; MODE_CHECKERBOARD - .byte $4 ; MODE_GARBAGE - .byte $12 ; MODE_DROUGHT - .byte $10 ; MODE_DAS - .byte $12 ; MODE_LOWSTACK - .byte $0 ; MODE_KILLX2 - .byte $0 ; MODE_INVISIBLE - .byte $0 ; MODE_HARDDROP - .byte $0 ; MODE_SPEED_TEST - .byte $5 ; MODE_SCORE_DISPLAY - .byte $3 ; MODE_CRASH - .byte $1 ; MODE_STRICT - .byte $1 ; MODE_HZ_DISPLAY - .byte $1 ; MODE_INPUT_DISPLAY - .byte $1 ; MODE_DISABLE_FLASH - .byte $1 ; MODE_DISABLE_PAUSE - .byte $5 ; MODE_DARK - .byte $1 ; MODE_GOOFY - .byte $1 ; MODE_DEBUG - .byte $1 ; MODE_LINECAP - .byte $1 ; MODE_DASONLY - .byte $1 ; MODE_QUAL - .byte $1 ; MODE_PAL -.if KEYBOARD = 1 - .byte $1 ; MODE_KEYBOARD -.endif -.endmacro +NTSC_DAS = 16 +NTSC_ARR = 6 + +PAL_DAS = 12 +PAL_ARR = 4 .macro MODENAMES .byte "TETRIS" .byte "TSPINS" - .byte " SEED " - .byte "STACKN" - .byte " PACE " .byte "SETUPS" + .byte "STACKN" .byte "B-TYPE" - .byte "FLOOR " - .byte "CRUNCH" .byte "QCKTAP" + .byte "TAPQTY" .byte "TRNSTN" .byte "MARTHN" - .byte "TAPQTY" + .byte "LOBARS" .byte "CKRBRD" .byte "GARBGE" - .byte "LOBARS" - .byte "DASDLY" .byte "LOWSTK" .byte "KILLX2" - .byte "INVZBL" - .byte "HRDDRP" + .byte "ANYDAS" .endmacro diff --git a/src/gamemode/gametypemenu/.gitignore b/src/gamemode/gametypemenu/.gitignore new file mode 100644 index 00000000..154cd49e --- /dev/null +++ b/src/gamemode/gametypemenu/.gitignore @@ -0,0 +1,2 @@ +menudata.asm +menuram.asm diff --git a/src/gamemode/gametypemenu/menu.asm b/src/gamemode/gametypemenu/menu.asm index f4fb67b1..32916045 100644 --- a/src/gamemode/gametypemenu/menu.asm +++ b/src/gamemode/gametypemenu/menu.asm @@ -1,651 +1,960 @@ -.include "linecap.asm" +; to do +; get into game +; do arbitrary action +; get back into menu from game or level menu +; get back into menu from game w/block tool on +; each title associated with action +; more sanity checks +; set defaults +; save/restore to/from sram + + +AUTO_MENU_VARS_HI = >autoMenuVars + +; valid background chars are 0-253 +EOL = $FE +EOF = $FF +NORAM = $00 + +MENU_TITLE_PPU = $2106 +MENU_STRIPE_WIDTH = 20 +MENU_ROWS = 9 +MENU_STACK = $DF ; $01C8 - $01DF intended range + +menuDataStart: +.include "menudata.asm" +.out .sprintf("Menu data: %d", *-menuDataStart) + +; tttnnnnnn n = mode +PAGE_DEFAULT = %00000000 + +; table of first items instead +; + table of item counts + +VALUE_MASK = %00011111 +TYPE_MASK = %11100000 + +; tttnnnnn +TYPE_UNUSED = %00000000 +TYPE_NUMBER = %00100000 ; n = limit +TYPE_CHOICES = %01000000 ; n = wordlist index +TYPE_FF_OFF = %01100000 ; n = limit + +TYPE_HEX = %10000000 ; n = digits +TYPE_MODE_ONLY = %10100000 ; n = mode +TYPE_BCD = %11000000 ; n = digits, v bit to differentiate from hex +TYPE_SUBMENU = %11100000 ; n = menu index + +DIGIT_MASK = %10100000 +DIGIT_COMPARE = %10000000 + + gameMode_gameTypeMenu: .if NO_MENU - inc gameMode - rts + inc gameMode + rts .endif - jsr makeNotReady - jsr calc_menuScrollY - sta menuScrollY - lda #0 - sta hideNextPiece - lda #$1 - sta renderMode - jsr updateAudioWaitForNmiAndDisablePpuRendering - jsr disableNmi - jsr bulkCopyToPpu - .addr title_palette - jsr copyRleNametableToPpu - .addr game_type_menu_nametable - lda #$28 - sta tmp3 - jsr copyRleNametableToPpuOffset - .addr game_type_menu_nametable_extra + jsr updateAudioWaitForNmiAndDisablePpuRendering + jsr disableNmi + jsr bulkCopyToPpu + .addr title_palette + jsr copyRleNametableToPpu + .addr game_type_menu_nametable .if INES_MAPPER <> 0 - lda #CHRBankSet0 - jsr changeCHRBanks + lda #CHRBankSet0 + jsr changeCHRBanks .endif - lda #NMIEnable - sta currentPpuCtrl - jsr waitForVBlankAndEnableNmi - jsr updateAudioWaitForNmiAndResetOamStaging - jsr updateAudioWaitForNmiAndEnablePpuRendering - jsr updateAudioWaitForNmiAndResetOamStaging + lda #NMIEnable + sta currentPpuCtrl + jsr waitForVBlankAndEnableNmi + jsr updateAudioWaitForNmiAndResetOamStaging + jsr updateAudioWaitForNmiAndEnablePpuRendering + jsr updateAudioWaitForNmiAndResetOamStaging + + lda #AUTO_MENU_VARS_HI + sta byteSpriteAddr+1 + lda #$1 + sta renderMode + lda #0 + sta hideNextPiece + sta byteSpriteTile + sta gameStarted + jsr makeNotReady + +; check to see if returning from level menu or game + ldy activeMenu + iny + bne @initMenu + jsr exitSubmenuNoSfx + jmp gameTypeLoop +@initMenu: + lda #MENU_STACK + sta menuStackPtr + lda #0 + jsr enterMenu gameTypeLoop: - ; memset FF-02 used to happen every loop - ; but it's done in ResetOamStaging anyway? - jmp seedControls - -gameTypeLoopContinue: - jsr menuConfigControls - jsr practiseTypeMenuControls - -gameTypeLoopCheckStart: - lda newlyPressedButtons_player1 - cmp #BUTTON_START - bne gameTypeLoopNext - - ; check double killscreen - lda practiseType - cmp #MODE_KILLX2 - bne @checkSpeedTest - lda #29 - sta startLevel - sta levelNumber - lda #$00 - sta gameModeState - lda #$02 - sta soundEffectSlot1Init - - jsr bufferScreen ; hides glitchy scroll - - inc gameMode - inc gameMode - rts - -@checkSpeedTest: - ; check if speed test mode - cmp #MODE_SPEED_TEST - beq changeGameTypeToSpeedTest - cmp #MODE_LINECAP - beq gotoLinecapMenu - - ; check for seed of 0000XX - cmp #MODE_SEED - bne @checkSelectable - lda set_seed_input - bne @checkSelectable - lda set_seed_input+1 - and #$FE ; treat 0001 like 0000 - beq gameTypeLoopNext - -@checkSelectable: - lda practiseType - cmp #MODE_GAME_QUANTITY - bpl gameTypeLoopNext - - lda #$02 - sta soundEffectSlot1Init - inc gameMode - rts - -changeGameTypeToSpeedTest: - lda #$02 - sta soundEffectSlot1Init - lda #7 - sta gameMode - rts - -gotoLinecapMenu: - jmp linecapMenu - -gameTypeLoopNext: - jsr renderMenuVars - jsr updateAudioWaitForNmiAndResetOamStaging - jmp gameTypeLoop - -seedControls: - lda practiseType - cmp #MODE_SEED - bne gameTypeLoopContinue - - lda newlyPressedButtons_player1 - cmp #BUTTON_SELECT - bne @skipSeedSelect - lda rng_seed - sta set_seed_input - lda rng_seed+1 - sta set_seed_input+1 - lda rng_seed+1 - eor #$77 - ror - sta set_seed_input+2 -@skipSeedSelect: - - lda #BUTTON_LEFT - jsr menuThrottle - beq @skipSeedLeft - lda #$01 - sta soundEffectSlot1Init - lda menuSeedCursorIndex - bne @noSeedLeftWrap - lda #7 - sta menuSeedCursorIndex -@noSeedLeftWrap: - dec menuSeedCursorIndex -@skipSeedLeft: - - lda #BUTTON_RIGHT - jsr menuThrottle - beq @skipSeedRight -@moveRight: - lda #$01 - sta soundEffectSlot1Init - inc menuSeedCursorIndex - lda menuSeedCursorIndex - cmp #7 - bne @skipSeedRight - lda #0 - sta menuSeedCursorIndex -@skipSeedRight: - - lda menuSeedCursorIndex + lda gameStarted + beq @noGame + inc gameMode + lda #$2 + sta soundEffectSlot1Init + rts +@noGame: + ; todo: write down which vars are used by which func + jsr collectControllerInput + jsr setScratch + jsr addInputs + jsr respondToInput + jsr stageCursor + + ; scratch is not important anymore + jsr stageBackgroundTiles + jsr stageCurrentValues +gameTypeLoopWait: + jsr updateAudioWaitForNmiAndResetOamStaging + jmp gameTypeLoop + + +.out .sprintf("bg setup & loop: %d", *-gameMode_gameTypeMenu) + +.macro switchToMenuStack + tsx + stx stackPtr + ldx menuStackPtr + txs +.endmacro + +.macro switchToNormalStack + tsx + stx menuStackPtr + ldx stackPtr + txs +.endmacro + +enterSubMenu: + ldy #$02 + sty soundEffectSlot1Init + pha + switchToMenuStack + lda activeRow + pha + lda activePage + pha + lda activeMenu + pha + switchToNormalStack + pla +enterMenu: + sta activeMenu + tay + iny + bne @normalMenu + rts +@normalMenu: + lda #0 +enterPage: + sta activePage + sta originalPage + ldy activeMenu + clc + adc startPageByMenu,y + sta actualPage + tax + + lda pageTypes,x + and #VALUE_MASK + sta unpackedPageValue + beq @noStorePractiseType + sta practiseType + +@noStorePractiseType: + lda pageTypes,x + and #TYPE_MASK + sta unpackedPageType + + lda pageCountByMenu,y + ldy #$00 + sty activeColumn + cmp #$1 + beq @storeRow + dey ; start at page select row for multipage + dec unpackedPageType ; hack for now +@storeRow: + sty activeRow + +setScratch: + ldx actualPage + lda activeRow + clc + adc startItemByPage,x + sta activeItem + tax + lda itemTypes,x + tay + and #VALUE_MASK + sta unpackedItemValue + + tya + and #TYPE_MASK + sta unpackedItemType + + jsr setupLR + jmp setupUD + +exitSubmenu: + ldy #$02 + sty soundEffectSlot1Init + +exitSubmenuNoSfx: + switchToMenuStack + pla + switchToNormalStack + + jsr enterMenu + + switchToMenuStack + pla + switchToNormalStack + + jsr enterPage .if KEYBOARD = 1 -@kbSeedLow = generalCounter -@kbSeedHigh = generalCounter2 - bne @checkForKbSeedEntry - jmp @skipSeedControl -@checkForKbSeedEntry: - jsr readKbSeedEntry - bmi @noKeysPressed - sta @kbSeedLow - asl - asl - asl - asl - sta @kbSeedHigh - ldy menuSeedCursorIndex - dey - tya - lsr - tay - ; y = (index-1) // 2 - ; c = (index-1) % 2 - lda set_seed_input,y - bcc @highByte -; low byte: - and #$F0 - ora @kbSeedLow - bcs @storeSeed -@highByte: - and #$0F - ora @kbSeedHigh -@storeSeed: - sta set_seed_input,y - jmp @moveRight -@noKeysPressed: -.else - beq @skipSeedControl +.warning "keyboard menu seed code is broken" +; @kbSeedLow = generalCounter +; @kbSeedHigh = generalCounter2 +; bne @checkForKbSeedEntry +; jmp @skipSeedControl +; @checkForKbSeedEntry: +; jsr readKbSeedEntry +; bmi @noKeysPressed +; sta @kbSeedLow +; asl +; asl +; asl +; asl +; sta @kbSeedHigh +; ldy menuSeedCursorIndex +; dey +; tya +; lsr +; tay +; ; y = (index-1) // 2 +; ; c = (index-1) % 2 +; lda set_seed_input,y +; bcc @highByte +; ; low byte: +; and #$F0 +; ora @kbSeedLow +; bcs @storeSeed +; @highByte: +; and #$0F +; ora @kbSeedHigh +; @storeSeed: +; sta set_seed_input,y +; jmp @moveRight +; @noKeysPressed: +; .else +; beq @skipSeedControl .endif - lda menuSeedCursorIndex - sbc #1 - lsr - tax ; save seed offset - - ; handle changing seed vals - - lda #BUTTON_UP - jsr menuThrottle - beq @skipSeedUp - lda #$01 - sta soundEffectSlot1Init - lda menuSeedCursorIndex - and #1 - beq @lowNybbleUp - - lda set_seed_input, x - clc - adc #$10 - sta set_seed_input, x - - jmp @skipSeedUp -@lowNybbleUp: - lda set_seed_input, x - clc - tay - and #$F - cmp #$F - bne @noWrapUp - tya - and #$F0 - sta set_seed_input, x - jmp @skipSeedUp -@noWrapUp: - tya - adc #1 - sta set_seed_input, x -@skipSeedUp: - - lda #BUTTON_DOWN - jsr menuThrottle - beq @skipSeedDown - lda #$01 - sta soundEffectSlot1Init - lda menuSeedCursorIndex - and #1 - beq @lowNybbleDown - - lda set_seed_input, x - sbc #$10 - clc - sta set_seed_input, x - - jmp @skipSeedDown -@lowNybbleDown: - lda set_seed_input, x - tay - and #$F - ; cmp #$0 ; and sets z flag - bne @noWrapDown - tya - and #$F0 - clc - adc #$F - sta set_seed_input, x - jmp @skipSeedDown -@noWrapDown: - tya - sec - sbc #1 - sta set_seed_input, x -@skipSeedDown: - - jmp gameTypeLoopCheckStart -@skipSeedControl: - jmp gameTypeLoopContinue - -menuConfigControls: - ; account for 'gaps' in config items of size zero - ; previously the offset was just set on X directly - - ldx #0 ; memory offset we want - ldy #0 ; cursor -@searchByte: - cpy practiseType - bne @notYet - lda menuConfigSizeLookup, y - beq @configEnd - ; if zero, caller will beq to skip the config - jmp @searchEnd -@notYet: - lda menuConfigSizeLookup, y - beq @noMem - inx -@noMem: - iny - jmp @searchByte -@searchEnd: - - ; actual offset now in Y - ; RAM offset now in X - - ; check if pressing left - lda #BUTTON_LEFT - jsr menuThrottle - beq @skipLeftConfig - ; check if zero - lda menuVars, x - ; cmp #0 ; lda sets z flag - beq @skipLeftConfig - ; dec value - dec menuVars, x - lda #$01 - sta soundEffectSlot1Init - jsr assertValues -@skipLeftConfig: - - ; check if pressing right - lda #BUTTON_RIGHT - jsr menuThrottle - beq @skipRightConfig - ; check if within the offset - lda menuVars, x - cmp menuConfigSizeLookup, y - bpl @skipRightConfig - inc menuVars, x - lda #$01 - sta soundEffectSlot1Init - jsr assertValues -@skipRightConfig: -@configEnd: - rts - -menuConfigSizeLookup: - MENUSIZES - -assertValues: - ; make sure you can only have block or qual - lda practiseType - cmp #MODE_QUAL - bne @noQual - lda menuVars, x - beq @noQual - lda #0 - sta debugFlag -@noQual: - lda practiseType - cmp #MODE_DEBUG - bne @noDebug - lda menuVars, x - beq @noDebug - lda #0 - sta qualFlag -@noDebug: - ; goofy - lda practiseType - cmp #MODE_GOOFY - bne @noFlip - lda heldButtons_player1 - asl - and #$AA - sta tmp3 - lda heldButtons_player1 - and #$AA - lsr - ora tmp3 - sta heldButtons_player1 -@noFlip: - rts - -practiseTypeMenuControls: - ; down - lda #BUTTON_DOWN - jsr menuThrottle - beq @downEnd - lda #$01 - sta soundEffectSlot1Init - - inc practiseType - lda practiseType - cmp #MODE_QUANTITY - bne @downEnd - lda #0 - sta practiseType -@downEnd: - - ; up - lda #BUTTON_UP - jsr menuThrottle - beq @upEnd - lda #$01 - sta soundEffectSlot1Init - lda practiseType - bne @noWrap - lda #MODE_QUANTITY - sta practiseType -@noWrap: - dec practiseType -@upEnd: - rts - -renderMenuVars: - - ; playType / seed cursors - - lda menuSeedCursorIndex - bne @seedCursor - - lda practiseType - jsr menuItemY16Offset - bne @cursorFinished - stx spriteYOffset - lda #$17 - sta spriteXOffset - lda #$1D - sta spriteIndexInOamContentLookup - jsr loadSpriteIntoOamStaging - jmp @cursorFinished - -@seedCursor: - clc - lda #MENU_SPRITE_Y_BASE + 7 - sbc menuScrollY - sta spriteYOffset - lda menuSeedCursorIndex - asl a - asl a - asl a - adc #$B1 - sta spriteXOffset - lda #$1B - sta spriteIndexInOamContentLookup - jsr loadSpriteIntoOamStaging - - ; indicator - - lda set_seed_input - bne @renderIndicator - lda set_seed_input+1 - and #$FE ; treat 0001 like 0000 - beq @cursorFinished -@renderIndicator: - ldx #$E - lda set_seed_input+2 - and #$F0 - beq @v5 - lda set_seed_input - bne @v4 - lda set_seed_input+1 - beq @v5 - jmp @v4 -@v5: - ldx #$F -@v4: - stx spriteIndexInOamContentLookup - sec - lda #(MODE_SEED*8) + MENU_SPRITE_Y_BASE + 1 - sbc menuScrollY - sta spriteYOffset - lda #$A0 - sta spriteXOffset - jsr stringSprite - -@cursorFinished: - -menuCounter := tmp1 -menuRAMCounter := tmp3 -menuYTmp := tmp2 - - ; render seed - - lda #$b8 - sta spriteXOffset - lda #MODE_SEED - jsr menuItemY16Offset - bne @notSeed - stx spriteYOffset - lda #set_seed_input - sta byteSpriteAddr - lda #0 - sta byteSpriteAddr+1 - lda #0 - sta byteSpriteTile - lda #3 - sta byteSpriteLen - jsr byteSprite -@notSeed: - - ; render config vars - - ; YTAX - lda #0 - sta menuCounter - sta menuRAMCounter + switchToMenuStack + pla + switchToNormalStack + + sta activeRow + jmp setScratch + + +setupUD: + ldy activeColumn + bne setupUDDigitChange + +setupUDRowChange: +; ud change row 1/2 - activeColumn == 0 + ldy #$00 + lda unpackedPageType + bpl @storeMin ; no page select row for single page + dey +@storeMin: + sty udMin + ldx actualPage + lda itemCountByPage,x + sta udMax + + lda #>activeRow + sta udPointer+1 + lda # 0 + dey + tya + lsr + tay ; y points to digit + php ; save for later, carry clear if hi byte + lda #$0 + sta udMin + sta udPointer+1 ; won't work if nybbleTemp is not zeropage + lda #activePage + sta lrPointer+1 + lda #= 0 && itemType < 128 + lda #AUTO_MENU_VARS_HI + sta lrPointer+1 + ldx activeItem + lda memoryOffsets,x + sta lrPointer + ldy #$0 + lda unpackedItemType + and #TYPE_MASK + cmp #TYPE_FF_OFF + bne @storeMin + dey +@storeMin: + sty lrMin + ldx unpackedItemValue + cmp #TYPE_CHOICES + bne @storeMax + lda choiceSetCounts,x + tax +@storeMax: + stx lrMax + rts + +setupLRColumnChange: +; setupLRColumnChange itemType & %10100000 == %10000000 + lda #0 + sta lrMin + lda #>activeColumn + sta lrPointer+1 + lda #MENU_TITLE_PPU + sta stack + lda # high byte of offset in A -; -> low byte in X -menuItemY16Offset: - sta tmpY - lda #8 - sta tmpX - ; get 16bit menuitem * 8 in tmpX/tmpY - lda #$0 - ldx #$8 - clc -@mulLoop: - bcc @mulLoop1 - clc - adc tmpY -@mulLoop1: - ror - ror tmpX - dex - bpl @mulLoop - sta tmpY - ; add offset - clc - lda tmpX - adc #MENU_SPRITE_Y_BASE + 1 - sta tmpX - lda tmpY - adc #0 - sta tmpY - ; remove menuscroll - sec - lda tmpX - sbc menuScrollY - sta tmpX - tax - lda tmpY - sbc #0 - rts - -bufferScreen: - lda #$0 - sta renderMode - jsr updateAudioWaitForNmiAndDisablePpuRendering - jsr disableNmi - jsr drawBlackBGPalette - jsr resetScroll - jsr waitForVBlankAndEnableNmi - jsr updateAudioWaitForNmiAndResetOamStaging - jsr updateAudioWaitForNmiAndEnablePpuRendering - jsr updateAudioWaitForNmiAndResetOamStaging - lda #$3 - sta sleepCounter -@endLoop: - jsr updateAudioWaitForNmiAndResetOamStaging - lda sleepCounter - bne @endLoop - rts + ldy #0 + lda (@stringPtr),y + tay + iny + beq @fillBlank ; stop advancing pointer when $FF is reached + inc @stringPtr + bne @noCarry + inc @stringPtr+1 +@noCarry: + iny + beq @fillBlank ; $FE also blanks line but after advancing pointer + sta stack,x + dec @blankCounter + inx + bne @loop ; always taken +@fillBlank: ; should only be entered directly when end of string reached + dec @blankCounter + bmi @finishRow + lda #$FF + sta stack,x + inx + bne @fillBlank ; always taken + +@finishRow: +; check if all rows drawn + dec @rowCounter + beq @shiftTitleRow + +; set next row based on last row + lda stack-((MENU_STRIPE_WIDTH+2)-1),x + clc + adc #$40 + sta stack+1,x + lda stack-(MENU_STRIPE_WIDTH+2),x + adc #$00 + sta stack,x + inx + inx + bne @nextRow ; always taken +@shiftTitleRow: +; bump title row 4 tiles to the right + lda stack+1 + eor #%1111 + sta stack+1 + rts + + +.out .sprintf("background staging: %d", *-stageBackgroundTiles) + + +stageCurrentValues: + @counter = blankCounter + @itemCount = rowCounter + + lda #$00 + sta @counter + lda #AUTO_MENU_VARS_HI + + ldx actualPage + lda startItemByPage,x + + sta activeItem + lda itemCountByPage,x + + sta @itemCount + + lda#(MENU_STRIPE_WIDTH+2) - 8 + sta stackPtr + +@memoryStageLoop: + lda stackPtr + clc + adc #MENU_STRIPE_WIDTH+2 + sta stackPtr + tax + + ldy activeItem + lda memoryOffsets,y + sta byteSpriteAddr + lda #AUTO_MENU_VARS_HI + sta byteSpriteAddr+1 + lda itemTypes,y + tax + ldy #0 + and #TYPE_MASK + bmi @digitInputOrEdge + + cmp #TYPE_CHOICES + beq @drawString + + cmp #TYPE_NUMBER + bne @drawFFOff +@setupOneByte: + lda #$02 + bne @drawOneByte + +@drawFFOff: + lda (byteSpriteAddr),y + bpl @setupOneByte + ldx #CHOICESET_OFFON + jsr @setStringList + jmp @startCopy + +@drawString: + txa + and #%11111 + tax + jsr @setStringList + lda (byteSpriteAddr),y + tay +@startCopy: + lda (stringSetPtr),y + tay + lda choiceSetTable,y + beq @endCopy + sta generalCounter + jsr setStackOffset + iny +@nextChar: + lda choiceSetTable,y + sta stack,x + inx + iny + dec generalCounter + bne @nextChar + +@endCopy: + jmp @nextByte + +@setStringList: + lda choiceSetIndexes,x + clc + adc #choiceSets + sta stringSetPtr+1 + rts + +@digitInputOrEdge: + and #TYPE_MASK + cmp #TYPE_MODE_ONLY + beq @nextByte + cmp #TYPE_SUBMENU + beq @nextByte + txa + and #%11111 +@drawOneByte: + pha + sec + sbc #1 + lsr + clc + adc #$1 + sta generalCounter + pla + + jsr setStackOffset + ldy #$00 +@digitLoop: + lda (byteSpriteAddr),y + pha + lsr + lsr + lsr + lsr + sta stack,x + inx + pla + and #$0F + sta stack,x + inx + iny + dec generalCounter + bne @digitLoop + jmp @nextByte + +@nextByte: + inc activeItem + inc @counter + lda @counter + cmp @itemCount + beq @ret + jmp @memoryStageLoop +@ret: + rts + +setStackOffset: + eor #$FF + clc + adc #$09 + clc + adc stackPtr + tax + rts + + +.out .sprintf("value staging: %d", *-stageCurrentValues) + + +stageCursor: + ldx activeMenu + lda pageCountByMenu,x + cmp #$1 + beq @singlePage + ldx oamStagingLength + sta oamStaging+9,x + lda #$4F + sta oamStaging+5,x + + lda #$CB + sta oamStaging+0,x + sta oamStaging+4,x + sta oamStaging+8,x + + lda #$C8 + sta oamStaging+3,x + clc + adc #$08 + sta oamStaging+7,x + adc #$08 + sta oamStaging+11,x + + lda #$00 + sta oamStaging+2,x + sta oamStaging+6,x + sta oamStaging+10,x + + ldy activePage + iny + tya + sta oamStaging+1,x + txa + clc + adc #$C + sta oamStagingLength + +@singlePage: + + lda activeRow + bpl @notTitle + + lda #$3F + sta spriteYOffset + lda #$10 + sta spriteXOffset + lda #$23 ; page select + sta spriteIndexInOamContentLookup + jmp loadSpriteIntoOamStaging + +@notTitle: + asl + asl + asl + asl + clc + adc #$4F + sta spriteYOffset +; digit input + ldx activeColumn + beq @notColumn + sec + sbc #$09 + sta spriteYOffset + txa + asl + asl + asl + clc + adc #$B9 + sta spriteXOffset + ldx activeItem + lda itemTypes,x + and #VALUE_MASK + sec + sbc #1 + lsr + asl + asl + asl + asl + eor #$FF + clc + adc #$01 + clc + adc spriteXOffset + sta spriteXOffset + lda #$1B ; digit select + bne @store +@notColumn: + lda #$14 + sta spriteXOffset + lda #$1D ; option select +@store: + sta spriteIndexInOamContentLookup +@stage: + jmp loadSpriteIntoOamStaging +gotoEdgeCase: + rts + + +.out .sprintf("cursor staging: %d", *-stageCursor) + + +render_mode_menu: + tsx + txa + ldx #$ff + txs + tax + ldy #MENU_ROWS +@nextRow: + pla + sta PPUADDR + pla + sta PPUADDR + .repeat MENU_STRIPE_WIDTH + pla + sta PPUDATA + .endrepeat + dey + bne @nextRow + txs + rts + + +.out .sprintf("render dump: %d", *-render_mode_menu) + + +.out .sprintf("total: %d", *-gameMode_gameTypeMenu) diff --git a/src/gamemode/gametypemenu/menu.js b/src/gamemode/gametypemenu/menu.js new file mode 100644 index 00000000..943c2ecd --- /dev/null +++ b/src/gamemode/gametypemenu/menu.js @@ -0,0 +1,387 @@ +const { mainMenu, extraSpriteStrings } = require("./menudata"); +const { writeFileSync } = require("fs"); + + +MAX_LENGTH_NAME = 14; +MAX_LENGTH_VALUE = 8; +DEBUG = false; + +labelMap = { + TYPE_BCD: typeDigit, + TYPE_HEX: typeDigit, + TYPE_NUMBER: typeNumber, + TYPE_FF_OFF: typeNumber, + TYPE_CHOICES: typeChoices, + TYPE_MODE_ONLY: getOutputLines, + TYPE_SUBMENU: typeSubMenu, + TYPE_BOOL: typeBool, +}; + +addedStrings = []; +buffer = []; +choiceSetCounts = []; +choiceSetEnums = []; +choiceSetIndexes = []; +choiceSets = []; +index = 0; +items = []; +lookupConstants = []; +memoryBuffer = []; +memoryMap = []; +memoryReservations = {}; +menuCount = 0; +menuEnums = []; +newStringLines = []; +pageCountByMenu = []; +pageIndex = 0; +pageLabelText = {}; +pagesOutput = []; +startItemByPage = []; +startPageByMenu = []; +unlabeledStringSets = {}; + +function checkStringSanity(string) { + if (string.length > MAX_LENGTH_VALUE) { + throw new Error(`${string} is more than MAX_LENGTH_VALUE chars`); + } + if ((match = string.match(/[^-\/ a-z0-9_?!*]/i))) { + throw new Error(`${string} has invalid char '${match[0]}'`); + } +} + +function cleanWord(word) { + word = word.toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase()); + return word.replace(/[- *?!(),\/]/g, ""); +} + +function getStringName(word) { + return `string${cleanWord(word)}`; +} + +function getChoiceSetName(word) { + return `choiceSet${cleanWord(word)}`; +} + +function getStringConstant(name) { + return `STRING_${cleanWord(name).toUpperCase()}`; +} + +function getChoiceSetConstant(name) { + return `CHOICESET_${cleanWord(name).toUpperCase()}`; +} + +function getByteLine(byte) { + return ` .byte ${byte}`; +} + +function getHexByte(number) { + if (isNaN(number)) return number; + return `$${number.toString(16).padStart(2, "0").toUpperCase()}`; +} + +function getOutputLines(itemType, string, memory) { + return { + string: string, + label: getByteLine(`${itemType} ; ${string}`), + memory: memory, // has to be processed separately to get output line + }; +} + +function getStringByte(c) { + replaceMap = { + ",": "$25", + "/": "$4F", + "(": "$5E", + ")": "$5F", + "*": "$69", // KSx2 x + " ": "$EF", + }; + return replaceMap[c] ? replaceMap[c] : `"${c.toUpperCase()}"`; +} + +function getStringBytes(string) { + return [...string.split("").map((c) => getStringByte(c))].join(","); +} + +function getLineString(string, multiline = false) { + if (string.length > MAX_LENGTH_NAME) { + throw new Error(`${string} is more than MAX_LENGTH_NAME chars`); + } + + return multiline + ? string + .split("") + .map((c) => getByteLine(getStringByte(c))) + .join("\n") + : getByteLine(getStringBytes(string)); +} + +function getPageLines(title, page, pages) { + DEBUG && console.log(`getPageLines`, title, page, pages); + pageType = "PAGE_DEFAULT"; + [_, label, mode] = title.match(/([^[]*)(?:\s*\[mode=(\w+)\])?/i); + const modifier = mode ? `MODE_${mode.toUpperCase()}` : "MODE_DEFAULT"; + const pagelabelsName = `pageLabels${cleanWord(label)}`; + + const endLabel = getByteLine("EOL"); + const endLabelSet = getByteLine("EOF"); + + pageLabelTextLines = []; + pageLabelTextLines.push(`${pagelabelsName}:`); + padding = [...Array(Math.round((MAX_LENGTH_NAME - label.length) / 2))] + .map(() => " ") + .join(""); + pageLabelTextLines.push(getLineString(`${padding}${label}`)); + pageLabelTextLines.push(endLabel); + page.forEach((p, i) => { + pageLabelTextLines.push(getLineString(p[1])); + if (i + 1 != page.length) pageLabelTextLines.push(endLabel); + }); + pageLabelTextLines.push(endLabelSet); + joined = pageLabelTextLines.join("\n"); + existing = pageLabelText[joined]; + if (!existing) pageLabelText[joined] = pagelabelsName; + + return { + label: getByteLine(`${pageType} | ${modifier} ; ${label}`), + count: getByteLine(`${getHexByte(page.length)} ; ${label}`), + hibytes: getByteLine( + `>${existing ? existing : pagelabelsName} ; ${label}`, + ), + lobytes: getByteLine( + `<${existing ? existing : pagelabelsName} ; ${label}`, + ), + choicesets: existing ? "" : joined, + }; +} + +function typeDigit(label, string, digits, memoryLabel) { + if (digits < 2 || digits > 8 || digits & 1) { + throw new Error(`${string}: digits can only be 2, 4, 6 or 8`); + } + memory = memoryLabel ? memoryLabel : (digits + 1) >> 1; + return getOutputLines(`${label} | ${getHexByte(digits)}`, string, memory); +} + +function typeChoices(label, string, choiceSet, memoryLabel) { + DEBUG && console.log(`Choice set ${string} with options ${choiceSet}`); + stringSet = [...choiceSet].map((c) => cleanWord(c.slice(0, 6))).join(""); + unlabeledStringSets[stringSet] = choiceSet; + return getOutputLines( + `${label} | ${getChoiceSetConstant(stringSet)}`, + string, + memoryLabel ? memoryLabel : 1, + ); +} + +function typeNumber(label, string, limit, memoryLabel) { + return getOutputLines( + `${label} | ${getHexByte(limit)}`, + string, + memoryLabel ? memoryLabel : 1, + ); +} + +function typeBool(label, string, memoryLabel) { + return typeChoices( + "TYPE_CHOICES", + string, + ["off", "on"], + memoryLabel ? memoryLabel : 1, + ); +} +function typeSubMenu(label, string) { + return getOutputLines( + `${label} | SUBMENU_${cleanWord(string).toUpperCase()}`, + `${string}`, + ); +} + +function getMemoryLabel(string, bytes) { + if (isNaN(bytes)) return bytes; // if label is specified use that instead + label = `menuVar${cleanWord(string)}`; + memoryReservations[label] = bytes; + return label; +} + +processPageSet = (pages, name) => { + DEBUG && name && console.log(`submenu ${name}`); + DEBUG && !name && console.log(`main menu`); + if (name) menuEnums.push(`SUBMENU_${cleanWord(name).toUpperCase()}`); + startPageByMenu.push( + `${getByteLine(getHexByte(pageIndex))} ; ${name ? name : "main menu"}`, + ); + // collect submenus to process after all pages + let subPageSets = {}; + Object.entries(pages).forEach(([title, page]) => { + DEBUG && console.log(`${title} with ${page.length} entries`); + pageIndex++; + startItemByPage.push( + getByteLine(`${getHexByte(index)} ; ${cleanWord(title)}`), + ); + pagesOutput.push(getPageLines(title, page, pages, index)); + page.forEach((item) => { + items.push(labelMap[item[0]](...item)); + index++; + if (item[0] === "TYPE_SUBMENU") subPageSets[item[1]] = item[2]; + }); + }); + pageCountByMenu.push( + getByteLine( + `${getHexByte(Object.values(pages).length)} ; ${name ? name : "main menu"}`, + ), + ); + + // process any submenus the same was as the main menu + Object.entries(subPageSets).forEach(([name, pages]) => { + processPageSet(pages, name); + }); +}; +processPageSet(mainMenu); + +items.forEach((i) => { + line = getByteLine( + `${i.memory ? "<" + getMemoryLabel(i.string, i.memory) : "NORAM"} ; ${i.string}`, + ); + memoryMap.push(line); +}); + +memoryBuffer.push("; generated by menu.js"); +memoryBuffer.push("autoMenuVars:"); +Object.entries(memoryReservations).forEach(([label, bytes]) => + memoryBuffer.push(`${label}: .res ${getHexByte(bytes)}`), +); +memoryBuffer.push(""); +// memory into separate file +writeFileSync(__dirname + "/menuram.asm", [...memoryBuffer, ""].join("\n")); + +[ + ["extraSpriteStrings", extraSpriteStrings], + ...Object.entries(unlabeledStringSets), +].forEach(([name, choiceSet], i) => { + if (!i) newStringLines.push("stringTable:"); + if (i == 1) { + newStringLines.push( + '\n.out .sprintf("%d/256 sprite string bytes", * - stringTable)\n', + ); + newStringLines.push("choiceSetTable:"); + } + + DEBUG && console.log(`stringlist`, name, choiceSet); + if (name != "extraSpriteStrings") { + choiceSetEnums.push(getChoiceSetConstant(name)); + choiceSetCounts.push(getByteLine(getHexByte(choiceSet.length))); + choiceSetIndexes.push( + getByteLine(`${getChoiceSetName(name)}-choiceSets`), + ); + choiceSets.push(`${getChoiceSetName(name)}:`); + } + DEBUG && console.log(`choiceSet: `, choiceSet); + choiceSet.forEach((choice) => { + choice = choice.toLowerCase(); + checkStringSanity(choice); + if (!addedStrings.includes(choice)) { + addedStrings.push(choice); + newStringLines.push(`${getStringName(choice)}:`); + newStringLines.push( + getByteLine( + `${getHexByte(choice.length)},${getStringBytes(choice)}`, + ), + ); + } + if (name == "extraSpriteStrings") { + lookupConstants.push( + `${getStringConstant(choice)} = ${getStringName(choice)}-stringTable`, + ); + } else { + choiceSets.push( + // getByteLine(`${getStringName(choice)}-${getChoiceSetName(name)}`), + getByteLine(`${getStringName(choice)}-choiceSetTable`), + ); + } + }); +}); +newStringLines.push( + '\n.out .sprintf("%d/256 choice set bytes", * - choiceSetTable)\n', +); + +buffer.push("; generated by menu.js"); +buffer.push("; will be overwritten unless built with -M"); +buffer.push(""); + +buffer.push(...lookupConstants); +buffer.push(""); + +buffer.push(".enum"); +buffer.push("MAIN_MENU"); +buffer.push(...menuEnums); +buffer.push("MENU_COUNT"); +buffer.push(".endenum"); +buffer.push('\n.out .sprintf("%d/32 menus", MENU_COUNT)\n'); +buffer.push(""); + +buffer.push(".enum"); +buffer.push(...choiceSetEnums); +buffer.push("CHOICESET_COUNT"); +buffer.push(".endenum"); +buffer.push('\n.out .sprintf("%d/32 choicesets", CHOICESET_COUNT)\n'); +buffer.push(""); + +buffer.push("; index activeMenu"); +buffer.push("startPageByMenu:"); +buffer.push(...startPageByMenu); +buffer.push(""); + +buffer.push("pageCountByMenu:"); +buffer.push(...pageCountByMenu); +buffer.push(""); + +buffer.push("; index activePage"); +buffer.push("pageTypes:"); +buffer.push(...pagesOutput.map((p) => p.label)); +buffer.push(""); + +buffer.push("itemCountByPage:"); +buffer.push(...pagesOutput.map((p) => p.count)); +buffer.push(""); + +buffer.push("pageLabelsHi:"); +buffer.push(...pagesOutput.map((p) => p.hibytes)); +buffer.push(""); + +buffer.push("pageLabelsLo:"); +buffer.push(...pagesOutput.map((p) => p.lobytes)); +buffer.push(""); + +buffer.push("startItemByPage:"); +buffer.push(...startItemByPage); +buffer.push(""); + +buffer.push("; index activeItem"); +buffer.push("memoryOffsets:"); +buffer.push(...memoryMap); +buffer.push(""); + +buffer.push("itemTypes:"); +buffer.push(...items.map((i) => i.label)); +buffer.push(""); + +buffer.push("choiceSetIndexes:"); +buffer.push(...choiceSetIndexes); +buffer.push(""); + +buffer.push("choiceSetCounts:"); +buffer.push(...choiceSetCounts); +buffer.push(""); + +buffer.push("choiceSets:"); +buffer.push(...choiceSets); +buffer.push(""); + +buffer.push(...newStringLines); +buffer.push(""); + +buffer.push(...pagesOutput.map((p) => p.choicesets)); +buffer.push(""); + +writeFileSync(__dirname + "/menudata.asm", [...buffer, ""].join("\n")); diff --git a/src/gamemode/gametypemenu/menudata.js b/src/gamemode/gametypemenu/menudata.js new file mode 100644 index 00000000..1049bd4f --- /dev/null +++ b/src/gamemode/gametypemenu/menudata.js @@ -0,0 +1,279 @@ +const seedFlag = ["TYPE_BOOL", "Seed Enabled", "seedEnabled"]; +const seedInput = ["TYPE_HEX", "seed", 6, "set_seed_input"]; +const linecapWhen = [ + "TYPE_CHOICES", + "linecap", + ["off", "level", "lines"], + "linecapWhen", +]; +const linecapHow = [ + "TYPE_CHOICES", + "linecap how", + ["ks*2", "floor", "inviz", "halt"], + "linecapHow", +]; +const linecapLevel = ["TYPE_NUMBER", "linecap level", 0, "linecapLevel"]; +const linecapLines = ["TYPE_BCD", "linecap lines", 4, "linecapLines"]; +const dasOnly = ["TYPE_BOOL", "das only", "dasOnlyFlag"]; + +const scoringModifier = [ + "TYPE_CHOICES", + "scoring", + ["classic", "letters", "7digit", "m", "capped", "hidden"], + "scoringModifier", +]; +const paceModifier = ["TYPE_FF_OFF", "Pace", 16, "paceModifier"]; +const hzFlag = ["TYPE_BOOL", "HZ DISPLAY", "hzFlag"]; +const inputDisplayFlag = ["TYPE_BOOL", "Input Display", "inputDisplayFlag"]; +const disableFlash = ["TYPE_BOOL", "Disable Flash", "disableFlashFlag"]; +const darkMode = [ + "TYPE_CHOICES", + "dark mode", + ["off", "on", "neon", "lite", "teal", "og"], + "darkModifier", +]; +const paletteSelection = [ + "TYPE_CHOICES", + "palette", + ["vanilla", "pride", "white"], + "paletteFlag", +]; + +const crashModifier = [ + "TYPE_CHOICES", + "crash", + ["off", "show", "top", "crash"], + "crashModifier", +]; +const strictCrashFlag = ["TYPE_BOOL", "strict crash", "strictFlag"]; +const disablePause = ["TYPE_BOOL", "disable pause", "disablePauseFlag"]; +const goofyFlag = ["TYPE_BOOL", "goofy foot", "goofyFlag"]; +const debugFlag = ["TYPE_BOOL", "block tool", "debugFlag"]; +const palFlag = ["TYPE_BOOL", "pal mode", "palFlag"]; +const keyboardFlag = ["TYPE_BOOL", "keyboard"]; +const qualFlag = ["TYPE_BOOL", "qual", "qualFlag"]; + +const floorModifier = ["TYPE_FF_OFF", "floor", 16, "floorModifier"]; +const crunchModifier = ["TYPE_NUMBER", "crunch", 16, "crunchModifier"]; +const invisibleFlag = ["TYPE_BOOL", "invisible", "invisibleOptionFlag"]; +const ghostPiece = ["TYPE_BOOL", "ghost", "ghostPieceFlag"]; +const hardDrop = ["TYPE_BOOL", "hardDrop", "hardDropFlag"]; +const killX2 = ["TYPE_BOOL", "killX2", "killX2Flag"]; + +const horizMirror = ["TYPE_BOOL", "mirror horiz", "mirrorHorizFlag"]; +const vertMirror = ["TYPE_BOOL", "mirror vert", "mirrorVertFlag"]; + +const presetModifier = ["TYPE_NUMBER", "setups", 8, "presetModifier"]; +const typeBModifier = ["TYPE_NUMBER", "type-b height", 9, "typeBModifier"]; +const checkerModifier = ["TYPE_NUMBER", "checker height", 9, "checkerModifier"]; +const quickTapLeftModifier = [ + "TYPE_NUMBER", + "left cols", + 20, + "tapLeftModifier", +]; +const quickTapRightModifier = [ + "TYPE_NUMBER", + "right cols", + 20, + "tapRightModifier", +]; +const transitionModifier = [ + "TYPE_NUMBER", + "transition", + 17, + "transitionModifier", +]; +const marathonModifier = ["TYPE_NUMBER", "marathon", 5, "marathonModifier"]; +const tapqtyModifier = ["TYPE_NUMBER", "qty height", 16, "tapqtyModifier"]; +const garbageModifier = ["TYPE_NUMBER", "garbage", 5, "garbageModifier"]; +const droughtModifier = ["TYPE_NUMBER", "drought", 20, "droughtModifier"]; +const lowStackRowModifier = [ + "TYPE_NUMBER", + "lowstack", + 20, + "lowStackRowModifier", +]; + +const anydasDas = ["TYPE_NUMBER", "das", 32, "dasModifier"]; +const anydasArr = ["TYPE_NUMBER", "arr", 32, "arrModifier"]; +const anydasEntryDelay = [ + "TYPE_CHOICES", + "entry delay", + ["off", "hydrant", "kitaru"], + "entryDelayModifier", + "entryDelayModifier", +]; +const trtFlag = ["TYPE_BOOL", "trt", "trtFlag"]; +const dasMeterFlag = ["TYPE_BOOL", "das meter", "dasMeterFlag"]; + +const modsSubMenu = { + "board[mode=default]": [ + floorModifier, + crunchModifier, + invisibleFlag, + ghostPiece, + hardDrop, + killX2, + horizMirror, + vertMirror, + ], +}; + +const anydasSubMenu = { + "anydas[mode=default]": [ + anydasDas, + anydasArr, + anydasEntryDelay, + trtFlag, + dasMeterFlag, + ], +}; + +const displaySubMenu = { + "display[mode=default]": [ + scoringModifier, + paceModifier, + hzFlag, + inputDisplayFlag, + disableFlash, + darkMode, + paletteSelection, + ], +}; + +const moreSubMenu = { + "more options[mode=default]": [ + crashModifier, + strictCrashFlag, + disablePause, + goofyFlag, + debugFlag, + palFlag, + qualFlag, + keyboardFlag, + ], +}; + +const tournamentSubMenu = { + "tournament[mode=default]": [ + seedInput, + seedFlag, + linecapWhen, + linecapHow, + linecapLevel, + linecapLines, + dasOnly, + ], +}; + +const goToTournament = ["TYPE_SUBMENU", "tournament", tournamentSubMenu]; +const goToMods = ["TYPE_SUBMENU", "board", modsSubMenu]; +const goToDisplay = ["TYPE_SUBMENU", "display", displaySubMenu]; +const goToAnydas = ["TYPE_SUBMENU", "anydas etc", anydasSubMenu]; +const goToMore = ["TYPE_SUBMENU", "more", moreSubMenu]; + +const mainMenu = { + "play tetris[mode=tetris]": [ + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "t-spins[mode=tspins]": [goToMods, goToDisplay, goToAnydas, goToMore], + "setups[mode=presets]": [ + presetModifier, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "stacking[mode=stacking]": [ + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "b-type[mode=typeb]": [ + typeBModifier, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "(quick)tap[mode=tap]": [ + quickTapLeftModifier, + quickTapRightModifier, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "tap quantity[mode=tapqty]": [ + tapqtyModifier, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "transition[mode=transition]": [ + transitionModifier, + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "marathon[mode=marathon]": [ + marathonModifier, + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "drought[mode=drought]": [ + droughtModifier, + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "checkerboard[mode=checkerboard]": [ + checkerModifier, + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "garbage[mode=garbage]": [ + garbageModifier, + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "lowstack[mode=lowstack]": [ + lowStackRowModifier, + goToTournament, + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], + "tap/roll speed[mode=speed_test]": [ + goToMods, + goToDisplay, + goToAnydas, + goToMore, + ], +}; + +const extraSpriteStrings = ["pause", "block", "clear?", "sure?!", "confetti"]; + +module.exports = { mainMenu, extraSpriteStrings }; diff --git a/src/gamemode/levelmenu.asm b/src/gamemode/levelmenu.asm index 751b4810..2eec95c0 100644 --- a/src/gamemode/levelmenu.asm +++ b/src/gamemode/levelmenu.asm @@ -20,10 +20,16 @@ gameMode_levelMenu: sta tmp2 jsr displayModeText jsr showHighScores - lda linecapFlag + lda linecapWhen beq @noLinecapInfo jsr levelMenuLinecapInfo @noLinecapInfo: + jsr checkIfSeeded + ; patch if seeded + ldy #$20 + ldx #$B5 + jsr patchSeed + ; render lines when loading screen lda #RENDER_LINES sta renderFlags @@ -201,7 +207,7 @@ levelControlClearHighScores: sta spriteXOffset lda #$C8 sta spriteYOffset - lda #$C + lda #STRING_CLEAR_O sta spriteIndexInOamContentLookup jsr stringSprite @@ -222,7 +228,7 @@ levelControlClearHighScoresConfirm: sta spriteXOffset lda #$C8 sta spriteYOffset - lda #$D + lda #STRING_SURE_O sta spriteIndexInOamContentLookup jsr stringSprite diff --git a/src/gamemode/waitscreen.asm b/src/gamemode/waitscreen.asm index 5b956d64..ae95b51d 100644 --- a/src/gamemode/waitscreen.asm +++ b/src/gamemode/waitscreen.asm @@ -11,7 +11,7 @@ waitScreenLoad: .if INES_MAPPER <> 0 ; NROM (and possibly FDS in the future) won't load the 2nd bankset ; and will instead use the title/menu chrset letters. This won't be noticeable -; unless a graphic is added +; unless a graphic is added lda #CHRBankSet1 jsr changeCHRBanks .endif diff --git a/src/gamemodestate/branch.asm b/src/gamemodestate/branch.asm index 772fb6b5..4867a881 100644 --- a/src/gamemodestate/branch.asm +++ b/src/gamemodestate/branch.asm @@ -1,4 +1,6 @@ branchOnGameModeState: + lda #0 + sta mainLoopWait branchTo gameModeState, \ gameModeState_initGameBackground, \ gameModeState_initGameState, \ diff --git a/src/gamemodestate/handlegameover.asm b/src/gamemodestate/handlegameover.asm index cfec037c..57b8be28 100644 --- a/src/gamemodestate/handlegameover.asm +++ b/src/gamemodestate/handlegameover.asm @@ -37,11 +37,6 @@ gameModeState_handleGameOver: sta playState jsr updateAudioWaitForNmiAndResetOamStaging ldx #3 ; levelMenu - lda practiseType - cmp #MODE_KILLX2 - bne @notGameTypeMenu - dex -@notGameTypeMenu: stx gameMode rts diff --git a/src/gamemodestate/initbackground.asm b/src/gamemodestate/initbackground.asm index 99c9a839..3e024083 100644 --- a/src/gamemodestate/initbackground.asm +++ b/src/gamemodestate/initbackground.asm @@ -10,8 +10,19 @@ gameModeState_initGameBackground: jsr copyRleNametableToPpu .addr game_nametable jsr scoringBackground + + + lda trtFlag + beq @noTrtPatch + jsr bulkCopyToPpu + .addr trt_nametable +@noTrtPatch: jsr debugNametableUI + ldy #$20 + ldx #$A2 + jsr patchSeed + ldy darkModifier beq @notDarkMode jsr drawDarkMode @@ -178,9 +189,8 @@ statisticsNametablePatch: rts showPaceDiffText: - lda practiseType - cmp #MODE_PACE - bne @done + lda paceModifier + bmi @done jsr bulkCopyToPpu .addr paceDiffText lda #0 @@ -218,6 +228,12 @@ seven_digit_nametable: .byte $21, $1E, $41, $0 ; 0 .byte $FF +trt_nametable: + .byte $23,$17,$4,$74,$34,$34,$75 + .byte $23,$37,$4,$35,$00,$00,$36 + .byte $23,$57,$4,$76,$37,$37,$77 + .byte $FF + savestate_nametable: .byte $22,$F7,$8,$74,$34,$34,$34,$34,$34,$34,$75 .byte $23,$17,$8,$35,$1C,$15,$18,$1D,$FF,$FF,$36 diff --git a/src/gamemodestate/initstate.asm b/src/gamemodestate/initstate.asm index 23ca3bfd..8643b94a 100644 --- a/src/gamemodestate/initstate.asm +++ b/src/gamemodestate/initstate.asm @@ -1,8 +1,10 @@ gameModeState_initGameState: - lda #$EF - ldx #$04 - ldy #$04 - jsr memset_page + lda #EMPTY_TILE + ldx #$00 +@clearPlayfield: + sta playfield,x + inx + bne @clearPlayfield ldx #$0F lda #$00 ; statsByType @@ -47,18 +49,19 @@ gameModeState_initGameState: sta invisibleFlag sta currentFloor sta crashState + sta trtLineCounter + sta trtLineCounter+1 + sta trtScratch+5 ; initialize currentFloor if necessary - lda practiseType - cmp #MODE_FLOOR - bne @notFloor + lda floorModifier + bmi @notFloor lda floorModifier sta currentFloor @notFloor: - lda practiseType - cmp #MODE_INVISIBLE - bne @notInvisible + lda invisibleOptionFlag + beq @notInvisible sta invisibleFlag @notInvisible: diff --git a/src/gamemodestate/pause.asm b/src/gamemodestate/pause.asm index 34fcb550..332eff87 100644 --- a/src/gamemodestate/pause.asm +++ b/src/gamemodestate/pause.asm @@ -55,8 +55,11 @@ pause: @pauseLoopCommon: clc - lda #$A - adc debugFlag + lda #STRING_PAUSE_O + ldx debugFlag + beq @notDebug + lda #STRING_BLOCK_O +@notDebug: sta spriteIndexInOamContentLookup jsr stringSprite diff --git a/src/gamemodestate/updateplayer1.asm b/src/gamemodestate/updateplayer1.asm index a00be232..85508049 100644 --- a/src/gamemodestate/updateplayer1.asm +++ b/src/gamemodestate/updateplayer1.asm @@ -10,9 +10,39 @@ gameModeState_updatePlayer1: jsr checkDebugGameplay jsr practiseAdvanceGame jsr practiseGameHUD +; do nothing while piece is active (playstate = 1) + ldx playState + dex + beq @branchOnPlaystate +; do nothing if not kitaru charge + lda entryDelayModifier + cmp #2 + bne @branchOnPlaystate +; do nothing when down is held + lda heldButtons + and #BUTTON_DOWN + bne @branchOnPlaystate +; reset das on new input + lda newlyPressedButtons + and #BUTTON_LEFT|BUTTON_RIGHT + bne @resetDas + lda heldButtons + and #BUTTON_LEFT|BUTTON_RIGHT + beq @branchOnPlaystate +; charge das (unless charged) + ldx autorepeatX + cpx dasModifier + beq @branchOnPlaystate + inc autorepeatX ; will clear zero flag + bne @branchOnPlaystate +@resetDas: + lda 0 + sta autorepeatX +@branchOnPlaystate: jsr branchOnPlayStatePlayer1 jsr stageSpriteForCurrentPiece jsr stageSpriteForNextPiece + jsr stageDasMeterSprites inc gameModeState ; 5 rts diff --git a/src/main.asm b/src/main.asm index e240c812..0656e880 100644 --- a/src/main.asm +++ b/src/main.asm @@ -87,6 +87,8 @@ mainLoop: .include "modes/crunch.asm" .include "modes/qtap.asm" .include "modes/garbage.asm" +.include "seeds.asm" +.include "modes/dasmeter.asm" .align $100 ; these tables benefit from page alignment diff --git a/src/modes/crash.asm b/src/modes/crash.asm index 19899240..4581e3f8 100644 --- a/src/modes/crash.asm +++ b/src/modes/crash.asm @@ -1,8 +1,8 @@ ; Hydrant's crash theory sheet https://docs.google.com/spreadsheets/d/1zAQIo_mnkk0c9e4-hpeDvVxrl9r_HvLSx8V4h4ttmrs/edit#gid=1013692687 testCrash: - lda #$00 - sta lagState ;clearing lag state between calculations + lda #$00 + sta lagState ;clearing lag state between calculations lda #$1C ; setting all cycles which always happen. for optimizing, this can be removed if all compared numbers are reduced by $6F1C. sta cycleCount lda #$6F @@ -460,7 +460,7 @@ confettiHandler: sta spriteYOffset ;either frameCounter or 80 loaded to A depending on confetti type lda #$A8 ;center of playfield sta spriteXOffset - lda #$19 ;ID for "confetti" text + lda #STRING_CONFETTI sta spriteIndexInOamContentLookup jsr stringSpriteAlignRight ;draw to screen lda #$00 diff --git a/src/modes/dasmeter.asm b/src/modes/dasmeter.asm new file mode 100644 index 00000000..b3543020 --- /dev/null +++ b/src/modes/dasmeter.asm @@ -0,0 +1,100 @@ +stageDasMeterSprites: + lda dasMeterFlag + beq @ret + lda playState + beq @ret + +@dasValue = generalCounter +@tile = generalCounter2 +@redCompare = generalCounter3 +@orangeCompare = generalCounter4 +@yCoordinate = 211 +@xStart = 103 + lda dasModifier + lsr + lsr + sta @orangeCompare + inc @orangeCompare ; 5 when ntsc vanilla + lsr + sta @redCompare + inc @redCompare ; 3 when ntsc vanilla + + lda #$FE + sta @tile + lda autorepeatX + lsr + php + sta @dasValue + cmp @orangeCompare + bcs @stageSprites + dec @tile + cmp @redCompare + bcs @stageSprites + dec @tile +@stageSprites: + ldx oamStagingLength + lda #0 + ldy #@xStart + +@loop: + lda @tile + sta oamStaging+1,x + tya + sta oamStaging+3,x + lda #@yCoordinate + sta oamStaging+0,x + lda #3 + sta oamStaging+2,x + inx + inx + inx + inx + tya + clc + adc #8 + tay + dec @dasValue + stx oamStagingLength + bpl @loop + plp + bcc @ret + lda @tile + sec + sbc #32 + sta oamStaging+1,x + tya + sta oamStaging+3,x + lda #3 + sta oamStaging+2,x + lda #@yCoordinate + sta oamStaging+0,x + inx + inx + inx + inx + stx oamStagingLength +@ret: + rts + +; render_mode_play_and_demo_then_dasmeter: +; lda #$23 +; sta PPUADDR +; lda #$89 +; sta PPUADDR +; ldx autorepeatX +; beq @ret +; lda dasMeterTile +; @drawDas: +; sta PPUDATA +; dex +; bne @drawDas +; ldx dasValue +; beq @ret +; lda #$FF +; @drawNonDas: +; sta PPUDATA +; dex +; bne @drawNonDas +; @ret: +; jsr render_mode_play_and_demo +; rts diff --git a/src/modes/events.asm b/src/modes/events.asm index 477578e6..8002ad4d 100644 --- a/src/modes/events.asm +++ b/src/modes/events.asm @@ -5,30 +5,29 @@ practiseInitGameState: jsr initChecker @skipChecker: jsr practiseEachPiece - cmp #MODE_FLOOR - bne @skipFloor - jmp advanceGameFloor + lda floorModifier + bmi @skipFloor + jsr advanceGameFloor @skipFloor: - lda practiseType - cmp #MODE_CRUNCH - bne @skipCrunch + lda crunchModifier + beq @skipCrunch jsr advanceGameCrunch @skipCrunch: rts practisePrepareNext: - lda practiseType - cmp #MODE_PACE - bne @skipPace - jmp prepareNextPace + lda paceModifier + bmi @skipPace + jsr prepareNextPace @skipPace: + lda practiseType cmp #MODE_GARBAGE bne @skipGarbo - jmp prepareNextGarbage + jsr prepareNextGarbage @skipGarbo: - cmp #MODE_PARITY + cmp #MODE_STACKING bne @skipParity - jmp prepareNextParity + jsr prepareNextParity @skipParity: jsr practiseEachPiece rts @@ -42,6 +41,7 @@ practiseAdvanceGame: rts practiseEachPiece: ; only used in this file + lda practiseType ; not necessary, but explicit cmp #MODE_TAPQTY bne @skipTapQuantity jsr prepareNextTapQuantity @@ -62,9 +62,8 @@ practiseGameHUD: jsr controllerInputDisplay @noInput: - lda practiseType - cmp #MODE_PACE - bne @skipPace + lda paceModifier + bmi @skipPace jsr gameHUDPace @skipPace: diff --git a/src/modes/qtap.asm b/src/modes/qtap.asm index 45d8dca9..312a2971 100644 --- a/src/modes/qtap.asm +++ b/src/modes/qtap.asm @@ -1,16 +1,13 @@ advanceGameTap: + @leftSide = $BF + @rightSide = $C6 + @secondLoop = generalCounter jsr clearPlayfield - ldx tapModifier - ; cpx #0 ; ldx sets z flag - beq @skip ; skip if zero - ldy #$BF ; left side - cpx #$11 - bmi @loop - ldy #$C6 ; right side - txa - sbc #$10 - tax - + lda #$00 + sta @secondLoop + ldx tapLeftModifier + beq @checkRight + ldy #@leftSide @loop: lda #$7B sta $400, y @@ -21,5 +18,13 @@ advanceGameTap: tay dex bne @loop -@skip: + lda @secondLoop + bne @ret +@checkRight: + inc @secondLoop + ldx tapRightModifier + beq @ret + ldy #@rightSide + bne @loop +@ret: rts diff --git a/src/nametables.asm b/src/nametables.asm index 70dd5f9d..172a546b 100644 --- a/src/nametables.asm +++ b/src/nametables.asm @@ -1,7 +1,5 @@ game_type_menu_nametable: ; RLE .incbin "nametables/game_type_menu_nametable_practise.bin" -game_type_menu_nametable_extra: ; RLE - .incbin "nametables/game_type_menu_nametable_extra.bin" level_menu_nametable: ; RLE .incbin "nametables/level_menu_nametable_practise.bin" game_nametable: ; RLE diff --git a/src/nametables/game_type_menu.js b/src/nametables/game_type_menu.js index a12cb1a1..86d27bfc 100644 --- a/src/nametables/game_type_menu.js +++ b/src/nametables/game_type_menu.js @@ -39,46 +39,8 @@ drawTiles(buffer, lookup, ` #a d# #a d# #a d# -#a TETRIS d# -#a T-SPINS d# -#a SEED d# -#a STACKING d# -#a PACE d# -#a SETUPS d# -#a B-TYPE d# -#a FLOOR d# -#a CRUNCH d# -#a (QUICK)TAP d# -#a TRANSITION d# -#a MARATHON d# -#a TAP QUANTITY d# -#a CHECKERBOARD d# -#a GARBAGE d# -#a DROUGHT d# -#a DAS DELAY d# -#a LOW STACK d# -#a KILLSCREEN ยป2 d# -#a INVISIBLE d# -#a HARD DROP d# -`);drawTiles(extra, lookup, ` -#a TAP/ROLL SPEED d# -#a SCORING d# -#a CRASH d# -#a STRICT CRASH d# -#a HZ DISPLAY d# -#a INPUT DISPLAY d# -#a DISABLE FLASH d# -#a DISABLE PAUSE d# -#a DARK MODE d# -#a GOOFY FOOT d# -#a BLOCK TOOL d# -#a LINECAP d# -#a DAS ONLY d# -#a QUAL MODE d# -#a PAL MODE d# #a d# #a d# -#a V6 d# #a d# #a d# #a d# @@ -93,6 +55,11 @@ drawTiles(buffer, lookup, ` #a d# #a d# #a d# +#a d# +#a d# +#a V67 d# +#a d# +#a d# `); if (process.env['GYM_FLAGS']?.match(/-D KEYBOARD=1/)) { @@ -133,14 +100,14 @@ const background = ` `; drawTiles(buffer, lookup, background); -drawTiles(extra, lookup, background); drawRect(buffer, 8, 2, 10, 5, 0xB0); // draw logo const urlX = 3; -const urlY = 17; -drawRect(extra, urlX, urlY, 12, 1, 0x74); -drawRect(extra, urlX+12, urlY, 12, 1, 0x84); +const urlY = 27; + +drawRect(buffer, urlX, urlY, 12, 1, 0x74); +drawRect(buffer, urlX+12, urlY, 12, 1, 0x84); drawAttrs(buffer, [` 2222222222222222 @@ -157,37 +124,12 @@ drawAttrs(buffer, [` 2222222222222222 2222222222222222 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 -`]); - -drawAttrs(extra, [` - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 -`, ` 2333333333333332 2222222222222222 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 - 2222222222222222 `]); writeRLE( __dirname + '/game_type_menu_nametable_practise.bin', buffer, ); - -writeRLE( - __dirname + '/game_type_menu_nametable_extra.bin', - extra, -); diff --git a/src/nmi/nmi.asm b/src/nmi/nmi.asm index b008898e..57389382 100644 --- a/src/nmi/nmi.asm +++ b/src/nmi/nmi.asm @@ -3,35 +3,40 @@ nmi: pha pha tya pha - lda #$00 - sta oamStagingLength jsr render + lda ppuScrollX + sta PPUSCROLL + lda ppuScrollY + sta PPUSCROLL lda currentPpuCtrl sta PPUCTRL - dec sleepCounter - lda sleepCounter - cmp #$FF - bne @jumpOverIncrement - inc sleepCounter -@jumpOverIncrement: - jsr copyOamStagingToOam + lda #$00 + sta OAMADDR + lda #$02 + sta OAMDMA renderComplete: - lda frameCounter - clc - adc #$01 - sta frameCounter - lda #$00 - adc frameCounter+1 - sta frameCounter+1 + lda sleepCounter + beq @noSleep + dec sleepCounter +@noSleep: + + inc frameCounter + bne @noCarry + inc frameCounter+1 +@noCarry: + ldx #rng_seed jsr generateNextPseudorandomNumber - jsr copyCurrentScrollAndCtrlToPPU + jsr pollControllerButtons + lda #$00 + sta oamStagingLength sta lagState ; clear flag after lag frame achieved lda #$01 sta verticalBlankingInterval + pla tay tsx diff --git a/src/nmi/render.asm b/src/nmi/render.asm index 1858e84d..63cdb700 100644 --- a/src/nmi/render.asm +++ b/src/nmi/render.asm @@ -1,6 +1,6 @@ render: branchTo renderMode, \ render_mode_static, \ - render_mode_scroll, \ + render_mode_menu, \ render_mode_congratulations_screen, \ render_mode_play_and_demo, \ render_mode_pause, \ @@ -22,7 +22,6 @@ render_mode_static: .include "render_mode_pause.asm" .include "render_mode_congratulations_screen.asm" .include "render_mode_rocket.asm" -.include "render_mode_scroll.asm" .include "render_mode_speed_test.asm" .include "render_mode_play_and_demo.asm" diff --git a/src/nmi/render_mode_linecap.asm b/src/nmi/render_mode_linecap.asm index 8fe02656..d4ee03bc 100644 --- a/src/nmi/render_mode_linecap.asm +++ b/src/nmi/render_mode_linecap.asm @@ -16,14 +16,18 @@ render_mode_linecap_menu: render_linecap_level_lines: lda linecapWhen - bne @linecapLines + cmp #LINECAP_LINES + beq @linecapLines + cmp #LINECAP_LEVEL + bne @ret lda linecapLevel jsr renderByteBCD jmp render_mode_static @linecapLines: - lda linecapLines+1 - sta PPUDATA lda linecapLines + sta PPUDATA + lda linecapLines+1 jsr twoDigsToPPU +@ret: rts diff --git a/src/nmi/render_mode_play_and_demo.asm b/src/nmi/render_mode_play_and_demo.asm index e2bb7670..4d43d1e7 100644 --- a/src/nmi/render_mode_play_and_demo.asm +++ b/src/nmi/render_mode_play_and_demo.asm @@ -13,6 +13,15 @@ render_mode_play_and_demo: jsr render_playfield @renderLines: + lda trtScratch+5 + beq LFC0C + ldx #$23 + stx PPUADDR + ldx #$38 + stx PPUADDR + jsr twoDigsToPPU +LFC0C: + lda scoringModifier bne @modernLines @@ -312,6 +321,7 @@ updatePaletteForLevel: ldx #$00 @loadLevelNumber: lda levelNumber,x + php ; keep track of glitched color range @mod10: cmp #$0A bmi @copyPalettes ; bcc fixes the colour bug sec @@ -321,6 +331,23 @@ updatePaletteForLevel: @copyPalettes: and #$3F tax + plp + bmi @checkPal ; skip custom palette when in glitched colors + ldy paletteFlag + beq @checkPal + dey + beq @pride + cpx #$0A + bcs @checkPal + adc #$4B ; clc unnecessary, carry already clear + tax + jmp @renderPalettes +@pride: + cpx #$0A ; only modify 0-9 + bcs @checkPal + adc #$41 ; clc unnecessary, carry already clear + tax +@checkPal: lda palFlag beq @renderPalettes cpx #$35 ; Level 181 & 245 and'd with $3F (level 53 & 117 are properly mod10'd) @@ -375,6 +402,14 @@ colorTable0: .byte $06,$4C,$BD,$19 .byte $00,$01,$03,$05 .byte $21 ; level 181/245 pal (different from NTSC) +; pride colors + .byte $30,$30,$25,$30 + .byte $30,$30,$30,$30 + .byte $30,$30 +; all white + .byte $30,$30,$30,$30 + .byte $30,$30,$30,$30 + .byte $30,$30 colorTable1: .byte $21,$29,$24,$2A @@ -394,6 +429,14 @@ colorTable1: .byte $38,$2A,$4E,$60 .byte $00,$01,$04,$05 .byte $2b ; level 181/245 pal (same as NTSC) +; pride colors + .byte $00,$00,$11,$00 + .byte $28,$21,$27,$00 + .byte $25,$27 +; all white + .byte $30,$30,$30,$30 + .byte $30,$30,$30,$30 + .byte $30,$30 colorTable2: .byte $12,$1A,$14,$12 @@ -413,6 +456,14 @@ colorTable2: .byte $E9,$99,$99,$00 .byte $01,$02,$04,$05 .byte $25 ; level 181/245 pal (same as NTSC) +; pride colors + .byte $21,$1a,$14,$25 + .byte $14,$2b,$11,$14 + .byte $21,$15 +; all white + .byte $30,$30,$30,$30 + .byte $30,$30,$30,$30 + .byte $30,$30 incrementPieceStat: tax diff --git a/src/nmi/render_util.asm b/src/nmi/render_util.asm index 8467ae85..5c45e181 100644 --- a/src/nmi/render_util.asm +++ b/src/nmi/render_util.asm @@ -88,7 +88,19 @@ copyPlayfieldRowToVRAM: cpx #$15 bpl @ret lda multBy10Table,x + ldy mirrorHorizFlag + beq @notHorizMirror + clc + adc #$9 +@notHorizMirror: tay + lda mirrorVertFlag + beq @notVertMirror + lda #$13 + sec + sbc vramRow + tax +@notVertMirror: txa asl a tax @@ -96,15 +108,16 @@ copyPlayfieldRowToVRAM: lda vramPlayfieldRows,x sta PPUADDR dex - lda vramPlayfieldRows,x sta PPUADDR @copyRow: ldx #$0A lda invisibleFlag bne @copyRowInvisible + lda mirrorHorizFlag + bne @copyRowMirrorHoriz @copyByte: - lda (playfieldAddr),y + lda playfield,y sta PPUDATA iny dex @@ -125,3 +138,11 @@ copyPlayfieldRowToVRAM: dex bne @copyByteInvisible jmp @rowCopied + +@copyRowMirrorHoriz: + lda playfield,y + sta PPUDATA + dey + dex + bne @copyRowMirrorHoriz + jmp @rowCopied diff --git a/src/palettes.asm b/src/palettes.asm index 3fbb3e0f..d035811f 100644 --- a/src/palettes.asm +++ b/src/palettes.asm @@ -13,7 +13,7 @@ game_palette: .byte $0F,$16,$2A,$22 ; sprite .byte $0F,$10,$16,$2D .byte $0F,$2C,$16,$29 - .byte $0F,$3C,$00,$30 + .byte $0F,$2A,$27,$16 .byte $FF title_palette: .byte $3F,$00 diff --git a/src/playstate/active.asm b/src/playstate/active.asm index 2299ded3..5689713e 100644 --- a/src/playstate/active.asm +++ b/src/playstate/active.asm @@ -1,7 +1,6 @@ playState_playerControlsActiveTetrimino: - lda practiseType - cmp #MODE_HARDDROP - bne @notHard + lda hardDropFlag + beq @notHard jsr harddrop_tetrimino lda playState cmp #8 @@ -40,7 +39,8 @@ harddrop_tetrimino: rts @noSonic: - ; hard drop + lda #$20 + sta vramRow lda #1 sta playState lda #0 @@ -282,9 +282,8 @@ drop_tetrimino: lda linecapState cmp #LINECAP_KILLX2 beq @killX2 - lda practiseType - cmp #MODE_KILLX2 - bne @normal + lda killX2Flag + beq @normal @killX2: jsr lookupDropSpeed sta tmpY @@ -411,30 +410,18 @@ shift_tetrimino: rts @dasOnlyEnd: - lda practiseType - cmp #MODE_DAS - bne @normalDAS - lda dasModifier - sta dasValueDelay - lda palFlag - eor #1 - asl - adc #$8 - sta dasValuePeriod - jmp @shiftTetrimino -@normalDAS: - ; region stuff - lda #$10 + lda dasModifier sta dasValueDelay - lda #$A + sec + sbc arrModifier sta dasValuePeriod ldy palFlag ; cpy #0 ; ldy sets z flag beq @shiftTetrimino - lda #$0C + lda #PAL_DAS sta dasValueDelay - lda #$08 + lda #PAL_DAS - PAL_ARR sta dasValuePeriod @shiftTetrimino: @@ -453,11 +440,16 @@ shift_tetrimino: lda autorepeatX cmp dasValueDelay bmi @ret +@zeroDas: lda dasValuePeriod + cmp dasValueDelay + beq @zeroArr sta autorepeatX jmp @buttonHeldDown @resetAutorepeatX: + lda dasValueDelay + beq @zeroDas lda #$00 sta autorepeatX @buttonHeldDown: @@ -488,3 +480,35 @@ shift_tetrimino: lda dasValueDelay sta autorepeatX @ret: rts + +@zeroArr: + lda heldButtons + and #BUTTON_RIGHT + beq @checkLeftPressed +@shiftRight: + inc tetriminoX + jsr isPositionValid + bne @shiftBackToLeft + lda #$03 + sta soundEffectSlot1Init + jmp @shiftRight +@checkLeftPressed: + lda heldButtons + and #BUTTON_LEFT + beq @leftNotPressed +@shiftLeft: + dec tetriminoX + jsr isPositionValid + bne @shiftBackToRight + lda #$03 + sta soundEffectSlot1Init + jmp @shiftLeft +@shiftBackToLeft: + dec tetriminoX + dec tetriminoX +@shiftBackToRight: + inc tetriminoX + lda dasValueDelay + sta autorepeatX +@leftNotPressed: + rts diff --git a/src/playstate/branch.asm b/src/playstate/branch.asm index aa5b982d..e7cb5154 100644 --- a/src/playstate/branch.asm +++ b/src/playstate/branch.asm @@ -32,5 +32,6 @@ playState_noop: .include "garbage.asm" .include "spawnnext.asm" .include "gameover_rocket.asm" +.include "trt.asm" .include "util.asm" diff --git a/src/playstate/completedrows.asm b/src/playstate/completedrows.asm index 53d57272..191e0d13 100644 --- a/src/playstate/completedrows.asm +++ b/src/playstate/completedrows.asm @@ -37,8 +37,8 @@ playState_checkForCompletedRows: beq @rowNotComplete ; lda practiseType ; accumulator is still practiseType - cmp #MODE_FLOOR - beq @floorCheck + lda floorModifier + bpl @floorCheck lda linecapState cmp #LINECAP_FLOOR beq @fullRowBurningCheck @@ -141,9 +141,8 @@ playState_checkForCompletedRows: @tapQtyEnd: ; update top row for crunch - lda practiseType - cmp #MODE_CRUNCH - bne @crunchEnd + lda crunchModifier + beq @crunchEnd jsr advanceSides ; clobbers generalCounter3 and generalCounter4 @crunchEnd: diff --git a/src/playstate/spawnnext.asm b/src/playstate/spawnnext.asm index 3cd6fcc2..82ea11d0 100644 --- a/src/playstate/spawnnext.asm +++ b/src/playstate/spawnnext.asm @@ -52,6 +52,12 @@ playState_spawnNextTetrimino: jsr incrementPieceStat jsr chooseNextTetrimino sta nextPiece + ldx entryDelayModifier + beq @resetDownHold + dex + bne @resetDownHold ; kitaru charge handled in playstate branch + lda dasModifier + sta autorepeatX ; store full charge for hydrant/1 @resetDownHold: lda #$00 sta autorepeatY @@ -96,18 +102,19 @@ pickTetriminoPre: lda practiseType cmp #MODE_TSPINS beq pickTetriminoT - ; lda practiseType ; accumulator is still practiseType - cmp #MODE_SEED - beq pickTetriminoSeed - ; lda practiseType cmp #MODE_TAPQTY beq pickTetriminoLongbar - ; lda practiseType cmp #MODE_TAP beq pickTetriminoLongbar - ; lda practiseType cmp #MODE_PRESETS beq pickTetriminoPreset + lda seedEnabled + beq pickRandomTetrimino + lda seedEnabled + beq @pickRandomTetrimino + lda seededPieces + bne pickTetriminoSeed +@pickRandomTetrimino: jmp pickRandomTetrimino pickTetriminoT: diff --git a/src/playstate/trt.asm b/src/playstate/trt.asm new file mode 100644 index 00000000..3c3e5514 --- /dev/null +++ b/src/playstate/trt.asm @@ -0,0 +1,158 @@ +trtCalculate: + ldx completedLines + cpx #$04 + bne @notTetris +@addToLineCounter: + inc trtLineCounter + lda trtLineCounter + and #$0F + cmp #$0A + bmi @noCarry + lda trtLineCounter + clc + adc #$06 + sta trtLineCounter + and #$F0 + cmp #$A0 + bcc @noCarry + lda trtLineCounter + and #$0F + sta trtLineCounter + inc trtLineCounter+1 +@noCarry: + dex + bne @addToLineCounter +@notTetris: + lda #$00 + ldx #$06 +@zeroScratch: + sta trtLineCounter+1,x + dex + bne @zeroScratch + ldy #$04 + lda trtLineCounter + sta trtRam+2 + lda trtLineCounter+1 + sta trtRam+1 +LFA41: clc + ldx #$03 +LFA44: rol trtRam,x + dex + bne LFA44 + dey + bne LFA41 +LFA4D: jsr LFADF + lda trtScratch+4 + cmp trtRam + bcc LFA6C + bne LFA85 + lda trtScratch+3 + cmp trtRam+1 + bcc LFA6C + bne LFA85 + lda trtScratch+2 + cmp trtRam+2 + bcs LFA85 +LFA6C: ldx #$03 +LFA6E: lda trtScratch+1,x + sta trtRam+3,x + dex + bne LFA6E + lda trtScratch+5 + adc #$10 + sta trtScratch+5 + cmp #$A0 + bne LFA4D + beq LFABD +LFA85: ldx #$03 +LFA87: lda trtRam+3,x + sta trtScratch+1,x + dex + bne LFA87 + ldy #$04 +LFA92: asl trtScratch+2 + rol trtScratch+3 + rol trtScratch+4 + dey + bne LFA92 +LFA9E: jsr LFADF + inc trtScratch+5 + lda trtScratch+4 + cmp trtLineCounter+1 + bcc LFAB6 + bne LFABD + lda trtScratch+3 + cmp trtLineCounter + bcs LFABD +LFAB6: lda trtScratch+5 + cmp #$A0 + bne LFA9E +LFABD: lda trtScratch+5 + and #$0F + cmp #$0A + bne LFACE + lda trtScratch+5 + adc #$05 + sta trtScratch+5 +LFACE: lda trtScratch+5 + cmp #$01 + bne LFADA + lda #$00 + sta trtScratch+5 +LFADA: + ; lda holdDownPoints + ; cmp #$02 + rts + +LFADF: lda lines + and #$0F + sta trtScratch+1 + lda trtScratch+2 + and #$0F + clc + adc trtScratch+1 + tax + lda trtScratch+2 + and #$F0 + adc trtBCDTable,x + cmp #$A0 + bcc LFB01 + adc #$5F + inc trtScratch+3 +LFB01: sta trtScratch+2 + lda lines + and #$F0 + sta trtScratch+1 + lda trtScratch+2 + clc + adc trtScratch+1 + bcc LFB19 + inc trtScratch+3 + adc #$5F +LFB19: cmp #$A0 + bcc LFB22 + adc #$5F + inc trtScratch+3 +LFB22: sta trtScratch+2 + lda trtScratch+3 + clc + adc lines+1 + sta trtScratch+3 + and #$0F + cmp #$0A + bcc LFB3C + lda trtScratch+3 + adc #$05 + sta trtScratch+3 +LFB3C: lda trtScratch+3 + cmp #$A0 + bcc LFB48 + adc #$5F + inc trtScratch+4 +LFB48: sta trtScratch+3 + rts + +trtBCDTable: + .byte $00,$01,$02,$03,$04,$05,$06,$07 + .byte $08,$09,$10,$11,$12,$13,$14,$15 + .byte $16,$17,$18 diff --git a/src/playstate/updatestats.asm b/src/playstate/updatestats.asm index 83b07592..531a4974 100644 --- a/src/playstate/updatestats.asm +++ b/src/playstate/updatestats.asm @@ -131,22 +131,21 @@ checkLevelUp: checkLinecap: ; set linecapState ; check if enabled - lda linecapFlag + ldx linecapWhen beq @linecapEnd - ; skip check if already set lda linecapState bne @linecapEnd - - lda linecapWhen + dex beq @linecapLevelCheck ;linecapLinesCheck +@linecapLines: lda lines+1 - cmp linecapLines+1 + cmp linecapLines bcc @linecapEnd lda lines - cmp linecapLines + cmp linecapLines+1 bcc @linecapEnd bcs @linecapApply @@ -182,6 +181,10 @@ checkLinecap: ; set linecapState @floorLinecapEnd: addPoints: + lda trtFlag + beq @noTetrisRate + jsr trtCalculate +@noTetrisRate: inc playState lda practiseType cmp #MODE_CHECKERBOARD @@ -211,7 +214,6 @@ addPointsRaw: .if NO_SCORING rts .endif - lda holdDownPoints cmp #$02 bmi @noPushDown diff --git a/src/playstate/util.asm b/src/playstate/util.asm index 7f7a8311..35144f39 100644 --- a/src/playstate/util.asm +++ b/src/playstate/util.asm @@ -74,9 +74,8 @@ updateMusicSpeed: ldy #50 ; replaces above ; check if crunch mode - ldx practiseType - cpx #MODE_CRUNCH - bne @notCrunch + ldx crunchModifier + beq @notCrunch ; add crunch left columns to y jsr unpackCrunchModifier diff --git a/src/ram.asm b/src/ram.asm index 4015c984..abeab605 100644 --- a/src/ram.asm +++ b/src/ram.asm @@ -24,12 +24,13 @@ allegroIndex: .res 1 ; $001F for crash wasAllegro: .res 1 ; $0020 for crash startParity: .res 1 ; $0021 for crash lagState: .res 1 ; $0022 for lagged lines & score - .res $10 + .res $F +mainLoopWait: .res 1 ; $0032 verticalBlankingInterval: .res 1 ; $0033 set_seed: .res 3 ; $0034 ; rng_seed, rng_seed+1, spawnCount -set_seed_input: .res 3 ; $0037 ; copied to set_seed during gameModeState_initGameState - .res 6 +.res 3 +.res 6 tetriminoX: .res 1 ; $0040 tetriminoY: .res 1 ; $0041 @@ -76,7 +77,56 @@ pztemp := mathRAM+$D byteSpriteAddr: .res 2 byteSpriteTile: .res 1 byteSpriteLen: .res 1 - .res $2A + +; (up to) 32 bytes menu scratch ram. can be reused in any other mode +; can also overlap with mathram +; this is to spread out for easier thinking + +; needs to be the same shape as lr* below +udPointer: .res $2 +udAdjust: .res $1 +udMin: .res $1 +udMax: .res $1 +; needs to be the same shape ud* above +lrPointer: .res $2 +lrAdjust: .res $1 +lrMin: .res $1 +lrMax: .res $1 + +activeItem: .res $1 +MENU_PTR_DISTANCE = lrPointer-udPointer +stringSetPtr: .res $2 +stackPtr: .res $1 + +unpackedPageType: .res $1 +unpackedPageValue: .res $1 +unpackedItemType: .res $1 +unpackedItemValue: .res $1 +digitPtr: .res $2 +originalPage: .res $1 +nybbleTemp: .res $1 +blankCounter: .res $1 +rowCounter: .res $1 + +; probably no value here +APressed: .res $1 +startPressed: .res $1 +startOrAPressed: .res $1 +BPressed: .res $1 +selectPressed: .res $1 + +actualPage: .res $1 +gameStarted: .res $1 +.res $1 + +; lr page ; mem address never changes (activePage) +; lr column ; mem address never changes (activeColumn) +; lr value ; current item is set every time anyway +; ud item ; mem address never changes (activeItem) +; ud value ; mem address never changes (expandedDigit) + + + .res $A spriteXOffset: .res 1 ; $00A0 spriteYOffset: .res 1 ; $00A1 @@ -162,15 +212,17 @@ currentPpuCtrl: .res 1 ; $00FF stack: .res $FF ; $0100 .res 1 oamStaging: .res $100 ; $0200 ; format: https://wiki.nesdev.com/w/index.php/PPU_programmer_reference#OAM - .res $F0 +trtLineCounter: .res $2 +trtScratch: .res $6 +trtRam: .res $8 + .res $E0 statsByType: .res $E ; $03F0 .res 2 playfield: .res $c8 ; $0400 .res $38 ; still technically part of playfield .res $100 ; $500 ; 2 player playfield - -practiseType: .res 1 ; $600 +.res 1 spawnDelay: .res 1 ; $601 dasValueDelay: .res 1 ; $602 dasValuePeriod: .res 1 ; $603 @@ -354,8 +406,40 @@ linecapFlag: .res 1 dasOnlyFlag: .res 1 qualFlag: .res 1 palFlag: .res 1 +paletteFlag: .res 1 +seedEnabled: .res 1 +seededPieces: .res 1 +ghostPieceFlag: .res 1 +hardDropFlag: .res 1 +noEntryDelayFlag: .res 1 +invisibleOptionFlag: .res 1 +killX2Flag: .res 1 +tapLeftModifier: .res 1 +tapRightModifier: .res 1 +mirrorHorizFlag: .res 1 +mirrorVertFlag: .res 1 +arrModifier: .res 1 +entryDelayModifier: .res 1 +anydasFlag: .res 1 +trtFlag: .res 1 +dasMeterFlag: .res 1 + + .if KEYBOARD = 1 keyboardFlag: .res 1 .endif + +set_seed_input: .res 3 ; $0037 ; copied to set_seed during gameModeState_initGameState +practiseType: .res 1 ; $600 +; menu +activeMenu: .res 1 +activePage: .res 1 +activeRow: .res 1 +activeColumn: .res 1 +menuStackPtr: .res 1 +; cursorToggle: .res 1 ; change this to flag if you need it later + +.include "gamemode/gametypemenu/menuram.asm" + ; ... $7FF diff --git a/src/seeds.asm b/src/seeds.asm new file mode 100644 index 00000000..2309be98 --- /dev/null +++ b/src/seeds.asm @@ -0,0 +1,17 @@ +checkIfSeeded: + lda #$00 + sta seededPieces + lda practiseType + cmp #MODE_TSPINS + beq @noSeed + cmp #MODE_TAPQTY + beq @noSeed + cmp #MODE_TAP + beq @noSeed + cmp #MODE_PRESETS + beq @noSeed + cmp #MODE_DROUGHT + beq @noSeed + inc seededPieces +@noSeed: + rts diff --git a/src/sprites/bytesprite.asm b/src/sprites/bytesprite.asm index c822b809..a951c276 100644 --- a/src/sprites/bytesprite.asm +++ b/src/sprites/bytesprite.asm @@ -1,19 +1,19 @@ byteSprite: -menuXTmp := tmp2 ldy #0 @loop: + ldx oamStagingLength tya asl asl asl asl adc spriteXOffset - sta menuXTmp - - ldx oamStagingLength + sta oamStaging+3,x + adc #$8 + sta oamStaging+7,x lda spriteYOffset - sta oamStaging, x - inx + sta oamStaging+0,x + sta oamStaging+4,x lda (byteSpriteAddr), y and #$F0 lsr a @@ -21,37 +21,18 @@ menuXTmp := tmp2 lsr a lsr a adc byteSpriteTile - sta oamStaging, x - inx + sta oamStaging+1,x lda #$00 - sta oamStaging, x - inx - lda menuXTmp - sta oamStaging, x - inx - - lda spriteYOffset - sta oamStaging, x - inx + sta oamStaging+2,x + sta oamStaging+6,x lda (byteSpriteAddr), y and #$F adc byteSpriteTile - sta oamStaging, x - inx - lda #$00 - sta oamStaging, x - inx - lda menuXTmp - adc #$8 - sta oamStaging, x - inx - - ; increase OAM index - lda #$08 + sta oamStaging+5, x + txa clc - adc oamStagingLength + adc #$08 sta oamStagingLength - iny cpy byteSpriteLen bne @loop diff --git a/src/sprites/loadsprite.asm b/src/sprites/loadsprite.asm index 23e0e427..978dca6e 100644 --- a/src/sprites/loadsprite.asm +++ b/src/sprites/loadsprite.asm @@ -79,6 +79,8 @@ oamContentLookup: .addr spriteReady ; $20 .addr spriteCustomLevelCursor ; $21 .addr spriteIngameHeart ; $22 + .addr spriteMenuPageSelect ; $23 +; .addr spriteMenuPageSelect2 ; $24 ; Sprites are sets of 4 bytes in the OAM format, terminated by FF. byte0=y, byte1=tile, byte2=attrs, byte3=x ; YY AA II XX sprite00LevelSelectCursor: @@ -88,6 +90,14 @@ sprite00LevelSelectCursor: sprite01GameTypeCursor: .byte $00,$27,$00,$00,$00,$27,$40,$3A .byte $FF +spriteMenuPageSelect: + .byte $00,$27,$40,$00 + .byte $00,$27,$00,$D9 + .byte $FF +; spriteMenuPageSelect2: +; .byte $00,$27,$40,$08 +; .byte $00,$27,$40,$D1 +; .byte $FF ; Used as a sort of NOOP for cursors sprite02Blank: .byte $00,$FF,$00,$00,$FF diff --git a/src/sprites/piece.asm b/src/sprites/piece.asm index 05e84a90..7afae416 100644 --- a/src/sprites/piece.asm +++ b/src/sprites/piece.asm @@ -2,10 +2,10 @@ stageSpriteForCurrentPiece: lda #$0 sta pieceTileModifier jsr stageSpriteForCurrentPiece_actual - - lda practiseType - cmp #MODE_HARDDROP - beq ghostPiece + lda hardDropFlag + bne ghostPiece + lda ghostPieceFlag + bne ghostPiece rts ghostPiece: @@ -26,6 +26,9 @@ ghostPiece: cmp tmp3 beq @noGhost + lda ghostPieceFlag + beq @noGhost + lda frameCounter and #1 asl @@ -33,9 +36,9 @@ ghostPiece: adc #$0D sta pieceTileModifier jsr stageSpriteForCurrentPiece_actual +@noGhost: lda tmp3 sta tetriminoY -@noGhost: rts tileModifierForCurrentPiece: @@ -60,7 +63,9 @@ stageSpriteForCurrentPiece_actual: @currentTile = generalCounter5 lda tetriminoX cmp #TETRIMINO_X_HIDE - beq stageSpriteForCurrentPiece_return + bne @notHidden + rts +@notHidden: asl a asl a asl a @@ -90,8 +95,15 @@ stageSpriteForCurrentPiece_actual: asl a clc adc generalCounter4 - sta oamStaging,y sta originalY + sta oamStaging,y + lda mirrorVertFlag + beq @notMirrorVert + lda #$F6 + sec + sbc originalY + sta oamStaging,y +@notMirrorVert: inc oamStagingLength iny jsr tileModifierForCurrentPiece ; used to just load from orientationTable @@ -107,7 +119,7 @@ stageSpriteForCurrentPiece_actual: inc oamStagingLength dey lda #$FF - sta oamStaging,y + sta oamStaging-1,y iny iny lda #$00 @@ -124,6 +136,12 @@ stageSpriteForCurrentPiece_actual: clc adc generalCounter3 sta oamStaging,y + lda mirrorHorizFlag + beq @finishLoop + lda #$08 + sec + sbc oamStaging,y + sta oamStaging,y @finishLoop: inc oamStagingLength iny @@ -150,9 +168,7 @@ stageSpriteForNextPiece: jmp loadSpriteIntoOamStaging @maybeDisplayNextPiece: - lda practiseType - cmp #MODE_HARDDROP - beq @displayNextPiece - lda debugFlag + lda hardDropFlag + ora debugFlag bne @displayNextPiece rts diff --git a/src/util/autodetect.asm b/src/util/autodetect.asm index 424c4728..e68d7fc6 100644 --- a/src/util/autodetect.asm +++ b/src/util/autodetect.asm @@ -6,15 +6,15 @@ ; Mapper detection for Holy Mapperel ; ; Copyright 2013-2017 Damian Yerrick -; +; ; This software is provided 'as-is', without any express or implied ; warranty. In no event will the authors be held liable for any damages ; arising from the use of this software. -; +; ; Permission is granted to anyone to use this software for any purpose, ; including commercial applications, and to alter it and redistribute it ; freely, subject to the following restrictions: -; +; ; 1. The origin of this software must not be misrepresented; you must not ; claim that you wrote the original software. If you use this software ; in a product, an acknowledgment in the product documentation would be diff --git a/src/util/check_region.asm b/src/util/check_region.asm index 3c710f8e..04454bc4 100644 --- a/src/util/check_region.asm +++ b/src/util/check_region.asm @@ -40,5 +40,9 @@ checkRegion: beq @ntsc lda #1 sta palFlag + lda #PAL_DAS + sta dasModifier + lda #PAL_ARR + sta arrModifier @ntsc: rts diff --git a/src/util/core.asm b/src/util/core.asm index 338c170e..aed55a81 100644 --- a/src/util/core.asm +++ b/src/util/core.asm @@ -264,15 +264,6 @@ generateNextPseudorandomNumber: sta oneThirdPRNG rts -; canon is initializeOAM -copyOamStagingToOam: - lda #$00 - sta OAMADDR - lda #$02 - sta OAMDMA - rts - - ; reg a: value; reg x: start page; reg y: end page (inclusive) memset_page: pha diff --git a/src/util/mapper.asm b/src/util/mapper.asm index 7abd1949..db3bb141 100644 --- a/src/util/mapper.asm +++ b/src/util/mapper.asm @@ -44,12 +44,12 @@ _setMMC1Control: changeCHRBanks: ; accum should be 0 or 2 (CHRBankset0 or CHRBankset1) - sta generalCounter + sta generalCounter ; autodetect .if INES_MAPPER = 1000 - ldx mapperId - beq @cnrom + ldx mapperId + beq @cnrom changeCHRBanksMMC1 rts @cnrom: diff --git a/src/util/modetext.asm b/src/util/modetext.asm index bad788b6..d4efbc2f 100644 --- a/src/util/modetext.asm +++ b/src/util/modetext.asm @@ -1,44 +1,118 @@ displayModeText: - ldx practiseType - cpx #MODE_SEED - bne @drawModeName - ; draw seed instead + + lda #$00 + sta anydasFlag +; set anydasFlag + lda entryDelayModifier + bne @anydas + lda palFlag + bne @pal + ldx #NTSC_DAS + ldy #NTSC_ARR + bne @testAnydas +@pal: + ldx #PAL_DAS + ldy #PAL_ARR +@testAnydas: + cpx dasModifier + bne @anydas + + cpy arrModifier + bne @anydas + beq @notanydas +@anydas: + inc anydasFlag +@notanydas: + ldx #MODE_ANYDAS*6 + lda anydasFlag + bne @drawMode + ; practiseType * 6 + lda practiseType + asl + sta generalCounter + asl + clc + adc generalCounter + tax +@drawMode: lda tmp1 sta PPUADDR lda tmp2 sta PPUADDR + + ldy #6 +@writeChar: + lda modeText-6, x + sta PPUDATA + inx + dey + bne @writeChar + rts + +patchSeed: + ; skip if not seeded + lda seedEnabled + beq @ret + lda seededPieces + beq @ret + sty PPUADDR + stx PPUADDR + lda gameMode + cmp #3 + beq @setupGameTiles + +; hack + lda #$35 + sta PPUDATA lda set_seed_input jsr twoDigsToPPU lda set_seed_input+1 jsr twoDigsToPPU lda set_seed_input+2 jsr twoDigsToPPU - rts + lda #$36 + jmp @nextRow -@drawModeName: - ; ldx practiseType - lda #0 -@loopAddr: - cpx #0 - beq @addr - clc - adc #6 - dex - jmp @loopAddr -@addr: - ; offset in X - tax +@setupGameTiles: + lda #$3B + sta PPUDATA + lda set_seed_input + jsr twoDigsToPPU + lda set_seed_input+1 + jsr twoDigsToPPU + lda set_seed_input+2 + jsr twoDigsToPPU + lda #$3C - lda tmp1 - sta PPUADDR - lda tmp2 +@nextRow: + sta PPUDATA + sty PPUADDR + txa + clc + adc #$20 sta PPUADDR - ldy #6 -@writeChar: - lda modeText, x + ldx #$07 + lda gameMode + cmp #3 + beq @menuBoxLoop +@gameBoxLoop: + lda bottomOfBoxGame,x sta PPUDATA - inx - dey - bne @writeChar + dex + bpl @gameBoxLoop rts + + +@menuBoxLoop: + lda bottomOfBoxMenu,x + sta PPUDATA + dex + bpl @menuBoxLoop +@ret: rts + + +bottomOfBoxMenu: + .byte $3F,$3E,$3E,$3E,$3E,$3E,$3E,$3D +bottomOfBoxGame: + .byte $77,$37,$37,$37,$37,$37,$37,$76 diff --git a/src/util/strings.asm b/src/util/strings.asm index f5ab17ee..1f10a66f 100644 --- a/src/util/strings.asm +++ b/src/util/strings.asm @@ -1,3 +1,11 @@ +; stringLineCapWhen: +; ldx linecapWhen +; lda choiceSetOfflineslevel, x +; jmp stringBackground +; stringLineCapHow: +; ldx linecapHow +; lda choiceSetKs2floorinvizhalt, x + stringBackground: ldx stringIndexLookup lda stringLookup, x @@ -70,98 +78,115 @@ stringSpriteLoop: rts stringLookup: - .byte stringClassic-stringLookup - .byte stringLetters-stringLookup - .byte stringSevenDigit-stringLookup - .byte stringFloat-stringLookup - .byte stringScorecap-stringLookup - .byte stringHidden-stringLookup - .byte stringNull-stringLookup ; reserved for future use - .byte stringNull-stringLookup - .byte stringOff-stringLookup ; 8 - .byte stringOn-stringLookup - .byte stringPause-stringLookup - .byte stringDebug-stringLookup - .byte stringClear-stringLookup - .byte stringConfirm-stringLookup - .byte stringV4-stringLookup - .byte stringV5-stringLookup ; F - .byte stringLevel-stringLookup - .byte stringLines-stringLookup - .byte stringKSX2-stringLookup - .byte stringFromBelow-stringLookup - .byte stringInviz-stringLookup - .byte stringHalt-stringLookup - .byte stringShown-stringLookup ;16 - .byte stringTopout-stringLookup - .byte stringCrash-stringLookup - .byte stringConfetti-stringLookup ;19 - .byte stringStrict-stringLookup - .byte stringNeon-stringLookup - .byte stringLite-stringLookup - .byte stringTeal-stringLookup - .byte stringOG-stringLookup -stringClassic: - .byte $7,'C','L','A','S','S','I','C' -stringLetters: - .byte $7,'L','E','T','T','E','R','S' -stringSevenDigit: - .byte $6,'7','D','I','G','I','T' -stringFloat: - .byte $1,'M' -stringScorecap: - .byte $6,'C','A','P','P','E','D' -stringHidden: - .byte $6,'H','I','D','D','E','N' -stringOff: - .byte $3,'O','F','F' -stringOn: - .byte $2,'O','N' -stringPause: - .byte $5,'P','A','U','S','E' -stringDebug: - .byte $5,'B','L','O','C','K' -stringClear: -.if SAVE_HIGHSCORES - .byte $6,'C','L','E','A','R','?' -.endif -stringConfirm: -.if SAVE_HIGHSCORES - .byte $6,'S','U','R','E','?','!' -.endif -stringV4: - .byte $2,'V','4' -stringV5: - .byte $2,'V','5' -stringLines: - .byte $5,'L','I','N','E','S' -stringLevel: - .byte $5,'L','E','V','E','L' -stringKSX2: - .byte $4,'K','S',$69,'2' -stringFromBelow: - .byte $5,'F','L','O','O','R' -stringInviz: - .byte $5,'I','N','V','I','Z' -stringHalt: - .byte $4,'H','A','L','T' -stringNull: - .byte $0 -stringShown: - .byte $4,'S','H','O','W' -stringTopout: - .byte $6,'T','O','P','O','U','T' -stringCrash: - .byte $5,'C','R','A','S','H' -stringConfetti: - .byte $8,'C','O','N','F','E','T','T','I' -stringStrict: - .byte $6,'S','T','R','I','C','T' -stringNeon: - .byte $4,'N','E','O','N' -stringTeal: - .byte $4,'T','E','A','L' -stringLite: - .byte $4,'L','I','T','E' -stringOG: - .byte $2,'O','G' + .byte stringLevelO-stringLookup + .byte stringLinesO-stringLookup + .byte stringKSX2O-stringLookup + .byte stringFromBelowO-stringLookup + .byte stringInvizO-stringLookup + .byte stringHaltO-stringLookup + .byte stringPauseO-stringLookup + .byte stringBlockO-stringLookup + .byte stringClearO-stringLookup + .byte stringSureO-stringLookup + .byte stringConfettiO-stringLookup +stringLevelO: + .byte $5,"LEVEL" +stringLinesO: + .byte $5,"LINES" +stringKSX2O: + .byte $4,"KS",$69,"2" +stringFromBelowO: + .byte $5,"FLOOR" +stringInvizO: + .byte $5,"INVIZ" +stringHaltO: + .byte $4,"HALT" +stringPauseO: + .byte $5, "PAUSE" +stringBlockO: + .byte $5, "BLOCK" +stringClearO: + .byte $06,"CLEAR?" +stringSureO: + .byte $06,"SURE?!" +stringConfettiO: + .byte $08,"CONFETTI" +.enum +STRING_LEVEL_O +STRING_LINES_O +STRING_KSX2_O +STRING_FLOOR_O +STRING_INVIZ_O +STRING_HALT_O +STRING_PAUSE_O +STRING_BLOCK_O +STRING_CLEAR_O +STRING_SURE_O +STRING_CONFETTI_O +.endenum +; stringBackgroundNotGood: +; tax +; lda choiceSetTable,x +; beq @ret +; tay +; inx +; @loop: +; lda choiceSetTable, x +; sta PPUDATA +; inx +; dey +; bne @loop +; @ret: +; rts +; +; stringSpriteNotGood: +; ldx spriteIndexInOamContentLookup +; lda stringTable, x +; sta tmpZ +; inx +; lda spriteXOffset +; sta tmpX +; jmp stringSpriteLoop +; +; stringSpriteAlignRightNotGood: +; ldx spriteIndexInOamContentLookup +; stringSpriteAlignRightANotGood: +; tax +; lda stringTable, x +; inx +; sta tmpZ +; lda tmpZ +; asl +; asl +; asl +; sta tmpX +; clc +; lda spriteXOffset +; sbc tmpX +; sta tmpX +; +; stringSpriteLoopNotGood: +; ldy oamStagingLength +; sec +; lda spriteYOffset +; sta oamStaging, y +; lda stringTable, x +; inx +; sta oamStaging+1, y +; lda #$00 +; sta oamStaging+2, y +; lda tmpX +; sta oamStaging+3, y +; clc +; adc #$8 +; sta tmpX +; ; increase OAM index +; lda #$04 +; clc +; adc oamStagingLength +; sta oamStagingLength +; +; dec tmpZ +; lda tmpZ +; bne stringSpriteLoop +; rts