million-perfect-letters/src/ui.play.a

463 lines
12 KiB
Plaintext

;license:MIT
;(c) 2020 by 4am
;
; main event loop for playing puzzles
;
; Public functions:
; - PlayEventLoop
; - ClearAndDrawPuzzle
; - DrawPuzzleChrome
; - AnimatePuzzleIntoPlace
; - AnimatePuzzleCompleted
;
; Codes returned by play event handlers
kKeepPlaying = 0 ; This code is checked with BEQ/BNE, so it must be 0
kCompletedPuzzle = 1 ; All non-zero codes will exit play event loop
kRequestedRestart = 2 ; with the code in A so caller knows what happened
kPressedEsc = 3
gSelectedLogicalColumn
!byte 0
gLastKeyPressed
!byte 0
kWorldLeftMargins
!byte 15,13,12,10
!byte 15,13,12,10
!byte 15,13,12,10
kWorldRightMargins
!byte 27,28,30,31
!byte 27,28,30,31
!byte 27,28,30,31
kThinLineStartingColor
!byte $D5,$AA
kPlayKeys ; must keep in sync with kPlayKeyHandlersLo/Hi arrays
!byte $0B ; up arrow
!byte $0A ; down arrow
!byte $08 ; left arrow
!byte $15 ; right arrow
!byte $1B ; Esc
!byte $12 ; Ctrl-R
!byte $0D ; Return
!byte $00 ; A-Z
kPlayKeyHandlersLo
!byte <PlayEventUpArrow
!byte <PlayEventDownArrow
!byte <PlayEventLeftArrow
!byte <PlayEventRightArrow
!byte <PlayEventEsc
!byte <PlayEventCtrlR
!byte <PlayEventReturn
!byte <PlayEventLetter
kPlayKeyHandlersHi
!byte >PlayEventUpArrow
!byte >PlayEventDownArrow
!byte >PlayEventLeftArrow
!byte >PlayEventRightArrow
!byte >PlayEventEsc
!byte >PlayEventCtrlR
!byte >PlayEventReturn
!byte >PlayEventLetter
;------------------------------------------------------------------------------
; PlayEventLoop
; main event loop for playing a puzzle
;
; in: puzzle has been loaded into memory, drawn on screen, animated, &c.
; and is ready to play
; out: A = reason why event loop ended (see list above)
; all other registers and flags clobbered
;------------------------------------------------------------------------------
PlayEventLoop
bit CLEARKBD
- lda KBD
bpl -
bit CLEARKBD
and #$7F
sta gLastKeyPressed
jsr HideMessage
ldx #0
- ldy kPlayKeys, x
beq @checkForLetter
cpy gLastKeyPressed
beq @dispatch
inx
bne -
@checkForLetter
cmp #$61
bcc +
cmp #$7B
bcs PlayEventLoop
and #$DF
sta gLastKeyPressed
+ cmp #$41
bcc PlayEventLoop
cmp #$5B
bcs PlayEventLoop
@dispatch
lda kPlayKeyHandlersLo, x
sta @j+1
lda kPlayKeyHandlersHi, x
sta @j+2
@j jsr $FDFD ; SMC
beq PlayEventLoop
rts
PlayEventEsc
lda #kPressedEsc ; caller will exit play event loop
rts
PlayEventCtrlR
lda #kRequestedRestart ; caller will exit play event loop
rts
PlayEventReturn
ldy gSelectedLogicalColumn
beq +
jsr EraseColumnSelectionIndicator
jmp MoveToFirstColumn
+ lda #kKeepPlaying
rts
PlayEventUpArrow
ldy gSelectedLogicalColumn
jsr ScrollPuzzleUp
bcs CantMove
jsr ScrollUp
jmp CheckAfterArrow
PlayEventDownArrow
ldy gSelectedLogicalColumn
jsr ScrollPuzzleDown
bcc +
CantMove
jsr SoftBell
KeepPlaying
lda #kKeepPlaying
rts
+ jsr ScrollDown
CheckAfterArrow
jsr CheckForTargetWord
bcs KeepPlaying
jsr MarkTargetWord
jsr CheckForPuzzleComplete
bne +
jmp PlayNextChord
+ jmp PlayFinalChord
PlayEventLeftArrow
ldy gSelectedLogicalColumn
jsr EraseColumnSelectionIndicator
bne +
ldy puzzle_logical_width
+ dey
sty gSelectedLogicalColumn
jsr DrawColumnSelectionIndicator
lda #kKeepPlaying
rts
PlayEventRightArrow
ldy gSelectedLogicalColumn
jsr EraseColumnSelectionIndicator
iny
cpy puzzle_logical_width
bcc +
MoveToFirstColumn
ldy #0
+ sty gSelectedLogicalColumn
jsr DrawColumnSelectionIndicator
lda #kKeepPlaying
rts
- jsr PlayNextChord
PlayEventLetter
lda gLastKeyPressed
ldy gSelectedLogicalColumn
jsr FindLetterInColumn
bcc +
jsr SoftBell ; didn't find letter, we're done
lda #kKeepPlaying
rts
+ beq PlayEventRightArrow ; found letter but it's already on center row, we're done, exit through right arrow handler
bmi @up ; scroll up or down ONCE, then reassess
jsr ScrollPuzzleDown
jsr ScrollDown
jmp +
@up jsr ScrollPuzzleUp
jsr ScrollUp
+ jsr CheckForTargetWord
bcs PlayEventLetter ; no word, check if more scrolling is required
jsr MarkTargetWord ; show that we've finished a word
jsr CheckForPuzzleComplete
beq - ; if puzzle isn't complete, check if more scrolling is required
jmp PlayFinalChord ; puzzle is complete, play final sound and return to caller with A = kCompletedPuzzle
MarkTargetWord
; in: none
; out: all registers & flags clobbered
ldx #4
ldy #0
- lda puzzle_data4, y
jsr DrawLargeCharacter
iny
cpy puzzle_logical_width
bne -
ldy #0
- lda #1
jsr ScrollUpBy
iny
cpy puzzle_logical_width
bne -
ldy #0
- lda #1
jsr ScrollDownBy
iny
cpy puzzle_logical_width
bne -
rts
CheckForPuzzleComplete
; in: none
; out: A = play event loop code
jsr IsPuzzleComplete
bcs +
lda #kCompletedPuzzle
+HIDE_NEXT_2_BYTES
+ lda #kKeepPlaying
rts
;------------------------------------------------------------------------------
; ClearAndDrawPuzzle
; clears screen and draws current puzzle (but not column selection indicator or
; other UI elements)
;
; in: none
; out: all registers and flags clobbered
;------------------------------------------------------------------------------
ClearAndDrawPuzzle
; jsr Home
; bit TEXTMODE
jsr DrawThinLines
+LDADDR puzzle_data0
+ST16 $FE
ldx #0 ; logical row
-- ldy #0 ; logical column
- lda ($FE), y
jsr DrawLargeCharacter
iny
cpy puzzle_logical_width
bne -
lda $FE
clc
adc #8
sta $FE
bcc +
inc $FF
+ inx
cpx puzzle_logical_height
bne --
bit GFXMODE
rts
;------------------------------------------------------------------------------
; AnimatePuzzleIntoPlace
;
; in: none
; out: all registers and flags clobbered
;------------------------------------------------------------------------------
AnimatePuzzleIntoPlace
ldx gWorldID
ldy kPuzzleWidths, x
sty @max+1
ldy #0
-- ldx #4
- jsr ScrollPuzzleDownUnconditionally
jsr ScrollDown
dex
bne -
iny
@max cpy #$FD ; SMC
bne --
rts
; +LDADDR test_message
; jsr SetNextMessage
; jmp ShowMessage
;
;test_message
; !byte 18
; !raw " PRESS ? FOR HELP"
;------------------------------------------------------------------------------
; DrawPuzzleChrome
; draw all elements on puzzle screen that are not the actual puzzle
; (e.g. column selection indicator, game title, help text)
;
; in: none
; out: all registers and flags clobbered
;------------------------------------------------------------------------------
kLevel
!byte 5
!raw "LEVEL"
DrawPuzzleChrome
+PRINT_AT kTitleLine1, 0, 0
+PRINT_AT kTitleLine2, 1, 0
+PRINT_AT kTitleLine3, 2, 0
+PRINT_AT kLevel, 0, 34
+LDADDR kWorldShortNames
+ST16 $FE
lda gWorldID
asl
asl
clc
adc $FE
sta $FE
bcc +
inc $FF
+
lda #2
sta VTAB
lda #33
sta HTAB
+LD16 $FE
jsr DrawHeavySilkString
+LDADDR kDash
jsr DrawHeavySilkString
ldx gPuzzleID
inx ; visible puzzle number is 1-based
lda #$30 ; padding character ('0')
jsr ToASCIIString
+LDADDR $00F1
jsr DrawHeavySilkString
jmp DrawColumnSelectionIndicator
;------------------------------------------------------------------------------
; AnimatePuzzleCompleted
;
; in: none
; out: all registers and flags clobbered
;------------------------------------------------------------------------------
AnimatePuzzleCompleted
jsr DrawThinLines
ldx gWorldID
lda kPuzzleWidths, x
sta @max+1
ldy #0
ldx #0
- jsr ScrollDown
inc puzzle_offsets, x
lda puzzle_offsets, x
cmp #9
bne -
inx
iny
@max cpy #$FD ; SMC
bne -
rts
;------------------------------------------------------------------------------
; DrawThinLines [private]
;
; in: none
; out: all registers and flags clobbered
;------------------------------------------------------------------------------
DrawThinLines
ldx #$55
jsr DrawThinLine
ldx #$6B
; /!\ execution falls through here
DrawThinLine
; in: X = HGR row (0x00..0xBF)
ldy HGRLO, x
sty $FE
ldy HGRHI, x
sty $FF
ldx gWorldID
ldy kWorldRightMargins, x
sty @right+1
ldy GlobalLeftMargin
dey
tya
and #1
tax
lda kThinLineStartingColor, x
- sta ($FE), y
eor #$7F
iny
@right cpy #$FD ; SMC
bcc -
rts
;------------------------------------------------------------------------------
; EraseColumnSelectionIndicator [private]
;
; in: none
; out: preserves X/Y
;------------------------------------------------------------------------------
EraseColumnSelectionIndicator
stx @x+1
sty @y+1
jsr DrawThinLines
@x ldx #$FD ; SMC
@y ldy #$FD ; SMC
rts
;------------------------------------------------------------------------------
; DrawColumnSelectionIndicator [private]
;
; in: none
; out: preserves X/Y
;------------------------------------------------------------------------------
DrawColumnSelectionIndicator
stx @x+1
sty @y+1
ldx #$55
jsr DrawOneSelectionIndicator
ldx #$6B
jsr DrawOneSelectionIndicator
@x ldx #$FD ; SMC
@y ldy #$FD ; SMC
rts
;------------------------------------------------------------------------------
; DrawOneSelectionIndicator [private]
;
; in: none
; out: all registers and flags clobbered
;------------------------------------------------------------------------------
DrawOneSelectionIndicator
lda HGRLO, x
sta $FE
lda HGRHI, x
sta $FF
ldy GlobalLeftMargin
dey
tya
and #1
eor #1
tax
lda kThinLineStartingColor, x
ldx gSelectedLogicalColumn
beq +
- iny
iny
iny
eor #$7F
dex
bne -
+ ldx #4
- sta ($FE), y
eor #$7F
iny
dex
bne -
rts