mirror of
https://github.com/a2-4am/million-perfect-letters.git
synced 2024-06-09 23:29:39 +00:00
472 lines
13 KiB
Plaintext
472 lines
13 KiB
Plaintext
;license:MIT
|
|
;(c) 2020 by 4am
|
|
;
|
|
; main event loop for playing puzzles
|
|
;
|
|
; Public functions:
|
|
; - PlayEventLoop
|
|
; - ClearAndDrawPuzzle
|
|
; - DrawPuzzleChrome
|
|
; - AnimatePuzzleIntoPlace
|
|
; - AnimatePuzzleCompleted
|
|
;
|
|
|
|
; Codes returned by 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 X so caller knows what happened
|
|
kReturnToMainMenu = 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 $2F ; /
|
|
!byte $3F ; ?
|
|
!byte $00 ; A-Z
|
|
|
|
kPlayKeyHandlersLo
|
|
!byte <PlayEventUpArrow
|
|
!byte <PlayEventDownArrow
|
|
!byte <PlayEventLeftArrow
|
|
!byte <PlayEventRightArrow
|
|
!byte <PlayEventEsc
|
|
!byte <PlayEventCtrlR
|
|
!byte <PlayEventReturn
|
|
!byte <PlayEventQuestion
|
|
!byte <PlayEventQuestion
|
|
!byte <PlayEventLetter
|
|
|
|
kPlayKeyHandlersHi
|
|
!byte >PlayEventUpArrow
|
|
!byte >PlayEventDownArrow
|
|
!byte >PlayEventLeftArrow
|
|
!byte >PlayEventRightArrow
|
|
!byte >PlayEventEsc
|
|
!byte >PlayEventCtrlR
|
|
!byte >PlayEventReturn
|
|
!byte >PlayEventQuestion
|
|
!byte >PlayEventQuestion
|
|
!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: X = reason why event loop ended (see list above)
|
|
; all other registers & 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
|
|
ldx #kReturnToMainMenu ; caller will exit play event loop
|
|
rts
|
|
|
|
PlayEventCtrlR
|
|
ldx #kRequestedRestart ; caller will exit play event loop
|
|
rts
|
|
|
|
PlayEventReturn
|
|
ldy gSelectedLogicalColumn
|
|
beq +
|
|
jsr EraseColumnSelectionIndicator
|
|
jmp MoveToFirstColumn
|
|
+ ldx #kKeepPlaying
|
|
rts
|
|
|
|
PlayEventUpArrow
|
|
ldy gSelectedLogicalColumn
|
|
jsr ScrollPuzzleUp
|
|
bcs CantMove
|
|
jsr ScrollUp
|
|
jmp CheckAfterArrow
|
|
|
|
PlayEventDownArrow
|
|
ldy gSelectedLogicalColumn
|
|
jsr ScrollPuzzleDown
|
|
bcc +
|
|
CantMove
|
|
jsr PlayErrorSound
|
|
KeepPlaying
|
|
ldx #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
|
|
ldx #kKeepPlaying
|
|
rts
|
|
|
|
PlayEventRightArrow
|
|
ldy gSelectedLogicalColumn
|
|
jsr EraseColumnSelectionIndicator
|
|
iny
|
|
cpy puzzle_logical_width
|
|
bcc +
|
|
MoveToFirstColumn
|
|
ldy #0
|
|
+ sty gSelectedLogicalColumn
|
|
jsr DrawColumnSelectionIndicator
|
|
ldx #kKeepPlaying
|
|
rts
|
|
|
|
PlayEventQuestion
|
|
jsr HelpEventLoop
|
|
jsr Home
|
|
jsr DrawPuzzleChrome
|
|
jsr DrawPuzzle
|
|
jsr DrawColumnSelectionIndicator
|
|
ldx #kKeepPlaying
|
|
rts
|
|
|
|
- jsr PlayNextChord
|
|
PlayEventLetter
|
|
lda gLastKeyPressed
|
|
ldy gSelectedLogicalColumn
|
|
jsr FindLetterInColumn
|
|
bcc +
|
|
jsr PlayErrorSound ; didn't find letter, we're done
|
|
ldx #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 X = 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: X = play event loop code
|
|
jsr IsPuzzleComplete
|
|
bcs +
|
|
ldx #kCompletedPuzzle
|
|
rts
|
|
+ ldx #kKeepPlaying
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------
|
|
; DrawPuzzle
|
|
; draws letters in current puzzle (but not column selection indicator or
|
|
; other UI elements)
|
|
;
|
|
; in: none
|
|
; out: all registers and flags clobbered
|
|
;------------------------------------------------------------------------------
|
|
DrawPuzzle
|
|
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 #9
|
|
bne --
|
|
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 --
|
|
jsr ShowMessage
|
|
+LDADDR noMessage
|
|
jmp SetNextMessage
|
|
;
|
|
;------------------------------------------------------------------------------
|
|
; DrawPuzzleChrome
|
|
; draw all elements on puzzle screen that are not the actual puzzle
|
|
; (game title, level)
|
|
;
|
|
; in: none
|
|
; out: all registers and flags clobbered
|
|
;------------------------------------------------------------------------------
|
|
DrawPuzzleChrome
|
|
+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
|
|
DrawPuzzleChromeCommon
|
|
+PRINT_AT sTitleLine1, 0, 0
|
|
+PRINT_AT sTitleLine2, 1, 0
|
|
+PRINT_AT sTitleLine3, 2, 0
|
|
+PRINT_AT sLevel, 0, 34
|
|
rts
|
|
|
|
|
|
;------------------------------------------------------------------------------
|
|
; 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
|
|
|
|
sLevel
|
|
!byte 5
|
|
!raw "LEVEL"
|