'-------------------------------------
' Joyas: A match-three pieces game
'

' Character set redefinition
data charset() byte = $00,$18,$68,$44,$2C,$30,$00,$00, ' $06: Rotated square
data           byte = $00,$10,$38,$6C,$38,$10,$00,$00, ' $07: Diamond
data           byte = $00,$7C,$54,$7C,$54,$7C,$00,$00, ' $08: Filled Square
data           byte = $00,$64,$2C,$10,$68,$4C,$00,$00, ' $09: Cross
data           byte = $00,$38,$4C,$5C,$7C,$38,$00,$00, ' $0A: Big ball
data           byte = $00,$5C,$40,$54,$04,$74,$00,$00, ' $0B: Flag?
data           byte = $00,$00,$00,$00,$00,$00,$00,$00, ' $0C: Explosion
data           byte = $7C,$82,$82,$82,$82,$82,$7C,$00, ' $0D: Cursor
data           byte = $00,$00,$00,$00,$00,$08,$08,$00, ' .
data           byte = $00,$02,$04,$08,$10,$20,$40,$00, ' /
data           byte = $00,$1C,$26,$2A,$32,$22,$1C,$00, ' 0
data           byte = $00,$08,$18,$08,$08,$08,$3E,$00, ' 1
data           byte = $00,$1C,$22,$02,$1C,$20,$3E,$00, ' 2
data           byte = $00,$3C,$02,$0C,$02,$02,$3C,$00, ' 3
data           byte = $00,$04,$24,$24,$1E,$04,$04,$00, ' 4
data           byte = $00,$1E,$20,$3C,$02,$02,$3C,$00, ' 5
data           byte = $00,$1C,$20,$3C,$22,$22,$1C,$00, ' 6
data           byte = $00,$1E,$22,$04,$08,$10,$10,$00, ' 7
data           byte = $00,$1C,$22,$1C,$22,$22,$1C,$00, ' 8
data           byte = $00,$1C,$22,$22,$1E,$02,$1C,$00, ' 9
data           byte = $00,$00,$08,$08,$00,$08,$08,$00, ' :
data           byte = $00,$00,$08,$08,$00,$08,$08,$10, ' ;
data           byte = $00,$00,$04,$08,$10,$08,$04,$00, ' <
data           byte = $00,$00,$3E,$00,$3E,$00,$00,$00, ' =
data           byte = $00,$00,$20,$10,$08,$10,$20,$00, ' >
data           byte = $00,$1C,$22,$04,$08,$00,$08,$00, ' ?
data           byte = $00,$10,$10,$7C,$10,$10,$00,$00, ' @ : PLUS
data           byte = $00,$1C,$22,$22,$3E,$22,$22,$00, ' A
data           byte = $00,$1C,$22,$3C,$22,$22,$3C,$00, ' B
data           byte = $00,$1E,$20,$20,$20,$20,$1E,$00, ' C
data           byte = $00,$1C,$22,$22,$22,$22,$3C,$00, ' D
data           byte = $00,$1E,$20,$3C,$20,$20,$1E,$00, ' E
data           byte = $00,$1E,$20,$3C,$20,$20,$20,$00, ' F
data           byte = $00,$1C,$22,$20,$26,$22,$1C,$00, ' G
data           byte = $00,$22,$22,$3E,$22,$22,$22,$00, ' H
data           byte = $00,$0E,$38,$08,$08,$08,$3E,$00, ' I
data           byte = $00,$06,$0C,$04,$04,$24,$18,$00, ' J
data           byte = $00,$22,$24,$28,$34,$22,$22,$00, ' K
data           byte = $00,$20,$20,$20,$20,$20,$1E,$00, ' L
data           byte = $00,$36,$49,$49,$49,$49,$41,$00, ' M
data           byte = $00,$1C,$22,$22,$22,$22,$22,$00, ' N
data           byte = $00,$1C,$22,$22,$22,$22,$1C,$00, ' O
data           byte = $00,$1C,$22,$22,$3C,$20,$20,$00, ' P
data           byte = $00,$1C,$22,$22,$22,$2A,$1C,$06, ' Q
data           byte = $00,$1C,$22,$22,$3C,$24,$22,$00, ' R
data           byte = $00,$1C,$20,$1C,$02,$02,$3C,$00, ' S
data           byte = $00,$0E,$38,$08,$08,$08,$08,$00, ' T
data           byte = $00,$22,$22,$22,$22,$22,$1C,$00, ' U
data           byte = $00,$22,$22,$14,$14,$08,$08,$00, ' V
data           byte = $00,$41,$49,$49,$49,$49,$36,$00, ' W
data           byte = $00,$24,$14,$08,$14,$22,$22,$00, ' X
data           byte = $00,$22,$22,$22,$1E,$04,$18,$00, ' Y
data           byte = $00,$38,$04,$08,$10,$20,$1E,$00, ' Z
data           byte = $06,$0E,$06,$06,$06,$66,$3C,$00, ' $3B: "J"
data           byte = $3C,$66,$66,$66,$66,$66,$3C,$00, ' $3C: "O"
data           byte = $66,$66,$66,$66,$3E,$0C,$38,$00, ' $3D: "Y"
data           byte = $3C,$66,$66,$7E,$66,$66,$66,$00, ' $3E: "A"
data           byte = $3C,$66,$60,$3C,$06,$66,$3C,$00  ' $3F: "S"


data borders() byte = $D0,$68,$34,$68,$D0,$68,$34,$68, ' 1: Wall 1
data           byte = $16,$2C,$58,$2C,$16,$2C,$58,$2C, ' 2: Wall 2
data           byte = $D1,$6A,$35,$1B,$0E,$04,$00,$00, ' 3: Bottom-left
data           byte = $11,$AA,$55,$BB,$EE,$44,$00,$00, ' 4: Bottom
data           byte = $16,$AC,$58,$B0,$E0,$40,$00,$00, ' 5: Bottom-right

data           byte = $50,$68,$34,$68,$50,$68,$34,$68, ' 1: Wall 1
data           byte = $14,$2C,$58,$2C,$14,$2C,$58,$2C, ' 2: Wall 2
data           byte = $51,$6A,$35,$1B,$0E,$00,$00,$00, ' 3: Bottom-left
data           byte = $11,$AA,$55,$BB,$EE,$00,$00,$00, ' 4: Bottom
data           byte = $14,$AC,$58,$B0,$E0,$00,$00,$00, ' 5: Bottom-right

data           byte = $54,$40,$54,$40,$54,$40,$54,$40, ' 1: Wall 1
data           byte = $54,$04,$54,$04,$54,$04,$54,$04, ' 2: Wall 2
data           byte = $55,$40,$55,$40,$3F,$00,$00,$00, ' 3: Bottom-left
data           byte = $55,$00,$55,$00,$FF,$00,$00,$00, ' 4: Bottom
data           byte = $54,$04,$54,$04,$F8,$00,$00,$00  ' 5: Bottom-right

data explode() byte = $00,$00,$00,$38,$54,$38,$00,$00,
data           byte = $00,$00,$24,$40,$12,$08,$44,$00,
data           byte = $00,$42,$00,$80,$00,$01,$00,$82,
data           byte = $00,$00,$00,$00,$00,$00,$00,$00

' Our board pieces:
data pieces() byte = $06, $47, $88, $C9, $0A, $4B

' Our empty board (two lines)
data eboard() byte = $80,$40,$81,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$82,$40,$80,
data          byte = $40,$80,$81,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$82,$80,$40
data lboard() byte = $80,$40,$83,$84,$84,$84,$84,$84,$84,$84,$84,$84,$84,$85,$40,$80

' The high scores and game names
dim hiScore(6), gname$(6)

gname$(0) = "moves "
gname$(1) = "moves`"
gname$(2) = "timer "
gname$(3) = "timer`"
gname$(4) = "hard  "
gname$(5) = "hard` "

' Set graphics mode and narrow screen
graphics 17
poke 559,0 : pause 0 ' Stop DMA

' Use our own custom display list, expanded to use 13 lines in total
' Lines 1 to 13 to graphics 2
poke dpeek(560)+2, 7+64
dpoke dpeek(560)+3, dpeek(88)
for i=5 to 16
  poke dpeek(560)+i, 7
next i
' Then, end the DL
poke dpeek(560)+17,65
dpoke dpeek(560)+18,dpeek(560)
' Activate screen DMA
poke 559, 33

' Redefine character set
mset $7C00, 8, 0
move adr(charset), $7C30, 464
move adr(borders), $7C08, 40
poke 756, $7C

' Set colors
if peek($d014) = 1
  ' PAL
  se. 0,3,8
  se. 1,11,10
  se. 2,8,6
  se. 3,14,14
  tdiv = 50
else
  ' NTSC
  se. 0,4,8
  se. 1,12,10
  se. 2,9,6
  se. 3,2,14
  tdiv = 60
endif

' Memory area for the board
mainBoard = $7000 + 19
fullBoard = $7000 + 16
' Clear board, needed to detect bad moves
mset $7000, 192, 0

set = 0
screen = dpeek(88)
screenBoard = screen + 3
C = mainBoard - screenBoard

' Loop forever
do

  ' Clear board
  for i=0 to 4
    move adr(eboard), fullBoard+i*32, 32
  next i

  ' Clear the screen
  mset screen, 176, 0
  position 0, 0

  ' Get button and console key states
  but = strig(0) : cons = peek(53279)
  do
    ' Show game info
    pos. 0,1
    print #6, " {|"; chr$($FD); "~"; chr$($7F), , , "HI SCORE:"; hiScore(set); "    "
    print #6, "!BUTTON: game  ""!OPTION: exit  ""!SELECT: "; gname$(set); """";
    print #6, "#$$$$$$$$$$$$$$%";
    pos. 16, 8
    print #6, "score:moves:time";

    ' Read joystick button transition
    X = strig(0) : I = (not X) and but : but = X
    ' If button pressed, start game
    if I then exit
    ' Read console keys transitions
    X = peek(53279) : I = cons & (7-X) : cons = X
    ' Start game on START
    if I & 1 then exit
    ' Exit on OPTION
    if I & 4 then end
    ' Change graphics set on SELECT
    if I & 2
      set = (set + 1) MOD 6
      move adr(borders)+(set/2)*40,$7C08,40
    endif
  loop

  ' Disable attract mode
  poke 77,0
  ' Draw bottom of board
  move adr(lboard), screen+160, 16
  ' Init game variables
  score = 0     ' Score
  nmoves = 15   ' Remaining moves
  addMove = 2   ' Points to bonus
  crsPos = 0    ' Cursor pos
  firstMove = set & 1 ' First move does not score points in "+" games
  gtime = 0     ' Game time
  stime = 0


  ' Call game loop
  while nmoves > 0
    exec GameLoop
  wend

  if hiScore(set) < score then hiScore(set) = score

loop

'-------------------------------------
' Move the cursor and wait for play
proc MoveCursor

  ' Enable timer
  stime = stime + TIME

  ' Loop
  repeat
    ' Disable attract mode
    poke 77,0

    ' Wait for valid move
    repeat
      ' Check time
      if TIME - stime > tdiv
        stime = stime + tdiv
        inc gtime
        if set > 1
          dec nmoves
        endif
        exec pScore
        if not nmoves then exit
      endif
      ' Show cursor
      poke crsPos+screenBoard, $CD
      pause 1
      ' Remove cursor
      poke crsPos+screenBoard, peek(crsPos+mainBoard)
      pause 1
      ' Move cursor
      i = stick(0)
      nxtPos = (i=7) - (i=11) + 16 * ((i=13) - (i=14)) + crsPos
    until nxtPos <> crsPos and peek(nxtPos + mainBoard) & 63 > 5

    ' If button is pressed, return
    if not strig(0) then Exit

    ' Move cursor
    crsPos = nxtPos
  until not nmoves

  ' Disable timer
  stime = stime - TIME

endproc


'-------------------------------------
' Make pieces fall in the board
proc FallPieces

  ' Loop until there are holes in the board
  repeat

    endFall = 1

    ' Move board to screen
    move fullBoard, screen, 160

    ' Search for holes in the board
    for A=mainBoard to mainBoard + 151 step 16
      for P=A to A+9
        if peek(P) & 63 = $0C
          ' If we found a hole, fall pieces!
          i = P
          while i > mainBoard + 15
            poke i, peek(i-16)
            i = i-16
          wend
          ' Store new piece and set A to exit outer loop
          poke i, pieces(rand(6))
          A = mainBoard + 152
          endFall = 0
        endif
      next P
    next A
  until endFall

endproc

'-------------------------------------
' Search matching pieces and remove
proc MatchPieces

  ' Number of matches found
  lsize = 0

  ' Go through each line
  for A = screenBoard to screenBoard + 151 step 16
    ' And through each column
    for X=A to A+9
      P = peek(X)
      ' Test if equal to the next two pieces
      if P = peek(X+1) and P = peek(X+2)
        ' Transform screen pointer to board pointer
        Y = X + C
        ' Clean the three pieces found
        PC = (P & 192) + $0C
        poke Y, PC
        poke Y+1, PC
        poke Y+2, PC
        inc lsize
      endif

      ' Test if equal to the two pieces bellow
      IF P = peek(X + 16) and P = peek(X + 32)
        ' Transform screen pointer to board pointer
        Y = X + C
        ' Clean the three pieces found
        PC = (P & 192) + $0C
        poke Y, PC
        poke Y + 16, PC
        poke Y + 32, PC
        inc lsize
      endif
    next X
  next A

  if lsize and not firstMove
    ' Found a line, add to score and show a little animation
    score = score + lsize * lsize
    move fullBoard, screen, 160

    for i = 0 to 3
      sound 1, 80, 0, 10 - i * 3
      ' Set animation frame
      move i*8 + adr(explode), $7C60, 8
      pause 4
    next i

    if set > 1 then nmoves = nmoves + lsize
  endif

  ' Add one move at 16, 32, 64, etc. points
  if score >= addMove
    inc nmoves
    addMove = addMove * 2
  endif

  exec pScore
  sound

endproc

'-------------------------------------
' Print current score and moves left
proc pScore
  position 12, 9
  print #6, score,
  position 19, 9
  print #6, nmoves,
  ' Right justify game time
  position 7 - (gtime > 10) - (gtime > 100) - (gtime > 1000), 10
  print #6, gtime,
endproc

'-------------------------------------
' Our main game loop
proc GameLoop

  ' Update number of moves in game type 0 and 2
  if not (set & 2)
    dec nmoves
  endif

  ' Loop until no pieces are left to move
  repeat
    exec FallPieces
    exec MatchPieces
  until not lsize

  ' We finished the first move
  firstMove = 0

  ' Game over if no more moves
  if nmoves < 1 then exit

  exec MoveCursor

  ' Perform an exchange
  poke crsPos + mainBoard, peek(nxtPos + screenBoard)
  poke nxtPos + mainBoard, peek(crsPos + screenBoard)
  sound 0, 100, 10, 10
  pause 2
  sound

endproc

