4cade/src/ui.search.mode.a

360 lines
10 KiB
Plaintext

;license:MIT
;(c) 2018-9 by 4am
;
; Search Mode - main UI
;
; Public functions
; - SearchMode
;
;------------------------------------------------------------------------------
; SearchMode
; main entry point for Search Mode, which allows the user to search the game
; catalog and launch games
;
; in: none
; out: never returns to caller (may JMP to other major modes)
;------------------------------------------------------------------------------
SearchMode
ldx #$FF
txs
stx SelectedIndex ; no game selected
inx
stx OffscreenPage
jsr Home ; clear screen
jsr OnInputChanged ; draw UI on HGR page 1
bit $C052
bit $C057
bit $C050 ; show HGR page 1
bit $C010
jsr _ResetInputTimeout
_SearchModeInputLoop
lda $C000
bmi @gotKey
inc $4F ; these are only ever incremented, never
bne + ; reset (may be used as a pseudorandom
inc $4E ; seed)
+
dec Timeout ; these are a 3-byte timeout counter
bne _SearchModeInputLoop ; that counts down from a number set
dec Timeout+1 ; in _ResetInputTimeout and reset
bne _SearchModeInputLoop ; on every keypress (whether or not
dec Timeout+2 ; the key leads to an action)
bne _SearchModeInputLoop
jsr _CoverFade ; no input for ~30 seconds, switch to
jmp MegaAttractMode ; mega-attract mode
@gotKey
bit $C010
jsr _ResetInputTimeout
cmp #$9B ; Esc clears the input buffer (if any)
bne + ; or switches to attract mode
ldx #kInputClear
bne InputDispatch ; always branches
+
cmp #$8D ; ENTER launches the current game (if any)
bne +
ldx #kInputLaunch
bne InputDispatch ; always branches
+
cmp #$FF ; delete key
bne +
- ldx #kInputBack
bne InputDispatch ; always branches
+ cmp #$88 ; left arrow = delete
beq -
and #$7F ; strip high bit for search characters
cmp #$30 ; control keys and punctuation ignored
bcc @badkey
cmp #$3A ; numbers are good input
bcc @goodkey
cmp #$41 ; more punctuation (also ignored)
bcc @badkey
cmp #$5B ; uppercase letters are good input
bcs +
ora #$20 ; convert uppercase letters to lowercase
bne @goodkey ; always branches
+ cmp #$61 ; more punctuation (also ignored)
bcc @badkey
cmp #$7B ; lowercase letters are good input
bcc @goodkey
@badkey
jsr SoftBell ; beep on invalid input
jmp _SearchModeInputLoop ; and start over
@goodkey
ldx #kInputSearch
; execution falls through here
InputDispatch
pha ; save key pressed
txa
asl
tax
lda InputDispatchTable,x
sta @j+1
lda InputDispatchTable+1,x
sta @j+2
pla ; restore key pressed
@j jsr $FDFD ; SMC
bcc _SearchModeInputLoop
jmp SearchMode ; if carry is set, force full redraw
OnClear
ldx InputLength
bne +
jsr _CoverFade ; Esc with no input switches to
jmp MegaAttractMode ; mega-attract mode
+ ldx #0 ; Esc with input clears the input
stx InputLength
jmp OnInputChanged
OnBack
ldx InputLength
bne +
jsr SoftBell
rts
+ dec InputLength
jmp OnInputChanged
OnLaunch
ldx SelectedIndex
cpx #$FF
beq +
jsr PlayGameFromSearch
sec ; tell caller to redraw UI
rts
+ clc
rts
OnSearch
ldx InputLength
cpx #MaxInputLength
bne +
jsr SoftBell
rts
+ sta InputBuffer,x
inc InputLength
; execution falls through here
OnInputChanged
lda InputLength
bne @findMatchingTitle
; no input, reset params and UI
lda #$FF
sta SelectedIndex ; no game selected
ldx #40 ; reset visible line
lda #0
- sta UILine1-1,x
dex
bne -
ldy #MaxInputLength ; clear visible search bar
lda #" "
- sta UILine2+1,y
dey
bne -
lda #$7F
sta UILine2+1
jsr _LoadCoverOffscreen
jsr _DrawSearchBarOffscreen
jsr _ShowOtherPage
clc
rts
@findMatchingTitle
jsr ResetTextRank
jsr okvs_iter_values ; iterate through all game titles
!word gGamesListStore ; and rank them for the best match
!word TextRankCallback ; to the current input buffer
lda MatchCount ; any matches at all?
bne +
jsr SoftBell ; no matches for this input buffer, beep
dec InputLength ; and ignore the last key typed
rts
+
lda BestMatchIndex ; check if the new best match is the same
cmp SelectedIndex ; as the current best match
php ; (we'll use this later to skip reloading)
sta SelectedIndex
sta @index
jsr okvs_nth
!word gGamesListStore
@index !byte $FD
+STAY @key
plp
bne +
lda OffscreenPage
eor #$01
sta OffscreenPage
jmp @skipload
+ lda OffscreenPage ; we have a new best match, so load the
beq + ; new title screenshot (offscreen)
lda #$40
+HIDE_NEXT_2_BYTES
+ lda #$20
sta @titleaddress+1
+LDADDR kHGRTitleDirectory
jsr SetPath
+LDAY @key
jsr AddToPath
jsr LoadFileAt
@titleaddress
!word $FD00
@skipload
jsr okvs_get
!word gGamesListStore
@key !word $FDFD
+STAY SRC ; A/Y points to game title (in OKVS)
ldy #0 ; copy game title into search bar buffer
lda (SRC),y
sta SAVE ; game title length
inc SAVE
- iny
cpy SAVE
bcc @printTitleChar
beq @printCursor
lda #" "
+HIDE_NEXT_2_BYTES
@printCursor
lda #$7F
+HIDE_NEXT_2_BYTES
@printTitleChar
lda (SRC),y ; copy game title to search UI
sta UILine2,y
cpy #MaxInputLength+1
bcc -
ldx #40
lda #0
- sta UILine1-1,x ; reset search bar
dex
bne -
tay
- lda (SRC),y
+LOW_ASCII_TO_LOWER
cmp InputBuffer,x
bne +
lda #$0B ; add dots to highlight matched characters
sta UILine1,y
inx
cpx InputLength
beq @doneHighlight
+ inc HTAB
iny
cpy #40
bne -
@doneHighlight
jsr _DrawSearchBarOffscreen; actually draw the search UI (offscreen)
jmp _ShowOtherPage ; now show everything at once
;------------------------------------------------------------------------------
_LoadCoverOffscreen
; clobbers all
lda OffscreenPage
beq +
lda #$40
+HIDE_NEXT_2_BYTES
+ lda #$20
sta @coveraddress+1
+LDADDR Cover
jsr SetPath
jsr LoadFileAt ; load default background
@coveraddress
!word $FD00 ; SMC
rts
_DrawSearchBarOffscreen
; clobbers all
lda #22 ; draw visible search bar
sta VTAB
lda OffscreenPage
ror ; draw on offscreen page
+LDADDR UILine1
jsr Draw40Chars
lda OffscreenPage
ror ; draw on offscreen page
+LDADDR UILine2
jmp Draw40Chars
_ShowOtherPage
; clobbers A, preserves X/Y
lda OffscreenPage
eor #$01
sta OffscreenPage
bne +
bit $C055 ; show page 2
rts
+ bit $C054 ; show page 1
rts
_ResetInputTimeout
; clobbers X, preserves A/Y
ldx #$16
stx Timeout
stx Timeout+1
stx Timeout+2
rts
_CoverFade
; clobbers all
jsr _LoadCoverOffscreen
jsr _ShowOtherPage
lda OffscreenPage
bne +
jsr _LoadCoverOffscreen
jsr _ShowOtherPage
+
; load transition effect code at $6000
+LDADDR kFXDirectory
jsr SetPath
+LDADDR kFXCoverFade
jsr AddToPath
jsr LoadFile
jmp $6000
kFXCoverFade
!byte 9
!text "COVERFADE"
Cover !byte 5
!text "COVER"
UILine1
!byte 0,0,0,0,0,0,0,0,0,0
!byte 0,0,0,0,0,0,0,0,0,0
!byte 0,0,0,0,0,0,0,0,0,0
!byte 0,0,0,0,0,0,0,0,0,0
UILine2
!text "["
!byte $7F
!text " "
!text "] "
UILine2_GameCount
!text "000 games"
OffscreenPage
!byte 1 ; 0 = currently showing HGR page 2
; (so offscreen is page 1 @ $2000)
; 1 = currently showing HGR page 1
; (so offscreen is page 2 @ $4000)
; indices into InputDispatchTable
kInputSearch = 0
kInputClear = 1
kInputBack = 2
kInputLaunch = 3
InputDispatchTable
!word OnSearch
!word OnClear
!word OnBack
!word OnLaunch