File "Bookworm.bas"

Path: /Bookworm/Bookworm.bas
File size: 50.38 KB
MIME-type:
Charset: utf-8

% Bookworm v0.3 for RFO-BASIC!
% v0.1 written by Nicolas Mougin in December 2015 [OPEN SOURCE] - For Mom
% v0.2 adapted in multiple languages for the guys at rfobasic.freeforums.org
% v0.3 released in March 2025. Changelog:
% - Fixed touch zones that were incorrectly shifted
% - Fixed welcome audio at first start
% - Download game resources from internet
% - Language selector in apk & editor
% - Add greek and norwegian menus
% Known limitation: pressing Home key during a game animation exits without saving progress

%TODO
% Hall of fame when game over or new game or new language

debug=0 % Activate if you want to test a new menu language

lng$="fr en de gr no ro"

GOSUB Functions % Register custom functions

isAPK=IsApk()

FILE.EXISTS fe, "bookworm.cfg"
IF fe % Get menus and game language from config file + sound setting
  TEXT.OPEN r, fid, "bookworm.cfg"
  TEXT.READLN fid, mLng$ % menus language
  TEXT.READLN fid, gLng$ % game language
  TEXT.READLN fid, e$: IF !IS_IN(e$, "01") THEN e$="0"
  mute=VAL(e$) % sound setting
  TEXT.CLOSE fid
ENDIF

GOSUB CheckResources

GOTO Initialization

%==========================================================
Functions:

FN.DEF IsApk()
  FILE.EXISTS editorMode, "../source/Bookworm.bas"
  FN.RTN !editorMode
FN.END

FN.DEF Fatal(err$)
  IF IsAPK() THEN : POPUP err$ : EXIT : ELSE : END err$ : ENDIF
FN.END

FN.DEF DL(res$)
  BYTE.OPEN r, fid, "http://mougino.free.fr/code/Bookworm/"+res$
  BYTE.COPY fid, res$
  CheckExist(res$)
FN.END

FN.DEF CheckAndDL(res$)
% Check presence of a mandatory file: if absent, download it
  FILE.EXISTS fe, res$
  IF !fe THEN DL(res$)
FN.END

FN.DEF CheckExist(file$)
% Check presence of a mandatory file: if absent, alert & quit to system
  FILE.EXISTS fe, file$
  IF !fe THEN Fatal("Fatal error:\n" + CHR$(34) + file$ + CHR$(34) + " not found!")
FN.END

FN.DEF LoadFlag(lng$)
  BUNDLE.CONTAIN 1, "lng-"+lng$, ptr
  IF !ptr
    GR.BITMAP.LOAD ptr, lng$+".jpg"
    IF ptr<0 THEN GR.BITMAP.LOAD ptr, lng$+".png"
    IF ptr<0 THEN Fatal("Missing '"+lng$+"' image")
    BUNDLE.PUT 1, "lng-"+lng$, ptr
  ENDIF
FN.END

FN.DEF BmpFlag(lng$)
  BUNDLE.GET 1, "lng-"+lng$, ptr
  FN.RTN ptr
FN.END

FN.DEF LoadLngN(lng$) % Load language name (how it is called in said language)
  BUNDLE.CONTAIN 1, "lngn-"+lng$, ptr
  IF !ptr
    CheckExist("labels_"+lng$+".txt")
    TEXT.OPEN r, fid, "labels_"+lng$+".txt"
    TEXT.READLN fid, nm$
    nm$=MID$(nm$,6) % "lang=..."
    TEXT.CLOSE fid
    BUNDLE.PUT 1, "lngn-"+lng$, nm$
  ENDIF
FN.END

FN.DEF LngN$(lng$)
  BUNDLE.GET 1, "lngn-"+lng$, nm$
  FN.RTN nm$
FN.END

FN.DEF LoadPngSeries(png$)
  DO
    GR.BITMAP.LOAD nul, png$+INT$(i)+".png"
    IF nul>0 & !ptr THEN ptr=nul
    i++
  UNTIL nul<=0
  FN.RTN ptr
FN.END

FN.DEF StrSwap(t$, pos1, pos2)
  IF pos1>pos2 THEN SWAP pos1, pos2
  c1$=MID$(t$, pos1, 1)
  c2$=MID$(t$, pos2, 1)
  t$=LEFT$(t$, pos1-1)+c2$+MID$(t$, pos1+1, pos2-pos1-1)+c1$+MID$(t$, pos2+1)
FN.END 

FN.DEF SortDesc_TagAlong(main$, sub$)
  IF LEN(main$)<>LEN(sub$) THEN FN.RTN 0
  FOR i=1 TO LEN(main$)
    FOR j=i+1 TO LEN(main$)
      IF ASCII(main$, i)<ASCII(main$, j)
        StrSwap(&main$, i, j)
        StrSwap(&sub$, i, j)
      ENDIF
    NEXT 
  NEXT 
FN.END 

FN.DEF SetScreen(orient$,w,h)
  o=(LEFT$(orient$,1)<>"v") % variable by sensor
  o+=(LEFT$(orient$,1)="p") % portrait
  GR.OPEN 255,0,0,0, ~
  1, o-1 % show status bar, orientation
  GR.SCREEN rw, rh % real width, height
  GR.STATUSBAR sbh % status bar height
  tw=w: BUNDLE.PUT 1, "tw", tw % target width
  th=h+sbh: BUNDLE.PUT 1, "th", th % target height (previous: h/tw*rw)
  y0=0: BUNDLE.PUT 1, "y0", y0 % Y-offset (previous: sbh ; before that: (th-h+sbh)/2)
  sw=rw/tw: BUNDLE.PUT 1, "sw", sw % scale for width
  sh=rh/th: BUNDLE.PUT 1, "sh", sh % scale for height
  GR.SCALE sw, sh
FN.END

FN.DEF SetColor(c$)
  GR.COLOR HEX(LEFT$(c$,2)), ~
  HEX(MID$(c$,3,2)), ~
  HEX(MID$(c$,5,2)), ~
  HEX(RIGHT$(c$,2)), 1
FN.END 

FN.DEF SetHollow(c$)
  GR.COLOR HEX(LEFT$(c$,2)), ~
  HEX(MID$(c$,3,2)), ~
  HEX(MID$(c$,5,2)), ~
  HEX(RIGHT$(c$,2)), 0
FN.END 

FN.DEF Render()
  IF BACKGROUND() THEN EXIT ELSE GR.RENDER
FN.END

FN.DEF AddToList(list, n, a$)
  FOR i=1 TO n: LIST.ADD list, a$: NEXT
FN.END

FN.DEF gPrint(x,y,size,t$,bg$,fnt)
  GR.TEXT.SETFONT fnt
  GR.TEXT.SIZE size
  GR.SET.STROKE size/15
  GR.TEXT.DRAW plain, x, y, t$
  SetHollow(bg$)
  GR.TEXT.DRAW border, x, y, t$
  FN.RTN plain
FN.END

FN.DEF Label$(key$)
  BUNDLE.GET 1, LOWER$(key$), e$
  FN.RTN e$
FN.END

FN.DEF ScrabbleVal(l$) % scrabble value for a letter
  BUNDLE.GET 1, "val("+l$+")", v
  FN.RTN v
FN.END

FN.DEF ColorVal(c$) % value for a colored tile
  FN.RTN MAX(0, VAL(c$)-1) % normal/fire=0 ; green=1 ; gold=2 ; saphire=3
FN.END

FN.DEF Score(w$, c$, lvl, b$) % score of a word w$: depends on word, its letters colors, level, and bonus word
  bgd=0 % background for score bubble: 0_normal_white/1_green/2_gold/3_saphire/4_multiple_colors/5_bonus
  mf=50+10*SQR(lvl-1) % multiplying factor (depends on level)
  FOR i=1 TO LEN(w$) % calculate score for each letter
    lv = ScrabbleVal(MID$(w$, i, 1)) % lv = letter value
    % count colored letters for final score multiplier
    cv = ColorVal(MID$(c$, i, 1)) % cv = color value
    green+=(cv=1): gold+=(cv=2): saphire+=(cv=3)
    % Update intermediate score with score for this letter
    score+=mf*lv
  NEXT
  % Add word-length bonus to final score
  score+=mf*(LEN(w$)-3)*(LEN(w$)-2)/2
  % Apply color multiplier to final score
  score*=MIN(6, 1.5^green * 2^gold * 2.5^saphire)
  IF green THEN bgd=1
  IF gold THEN bgd=2
  IF saphire THEN bgd=3
  IF green+gold+saphire>1 THEN bgd=4
  % Apply bonus if word is bonus-word
  IF w$=b$ THEN score*=4
  IF w$=b$ THEN bgd=5
  BUNDLE.PUT 1, "bgd", bgd
  FN.RTN 10*CEIL(score/10)
FN.END

RETURN % End of Functions

%==========================================================
Initialization:

BUNDLE.CREATE global % bundle index = #1
SetScreen("portrait", 640, 960)
BUNDLE.GET global, "y0", y0
BUNDLE.GET global, "sw", sw
BUNDLE.GET global, "sh", sh

% Load game fonts
FONT.LOAD fnt1, "Kraash Black.ttf"
FONT.LOAD fnt2, "lockergnome.ttf"
FONT.LOAD fnt3, "cooper-black.ttf"
FONT.LOAD fnt4, "berylium rg.ttf"

% List languages available for menus (labels) & for game (dictionaries)
LoadFlag("lng")
SPLIT lng$[], lng$
ARRAY.LENGTH nlng, lng$[]
FOR i=1 TO nlng
  LoadFlag(lng$[i])
NEXT

% Fill some arrays
ARRAY.LOAD progress[], 91, 114, 134, 152, 170, 188, 215, 229, 250, 269, 286, ~
 312, 336, 352, 370, 390, 407, 429, 444, 468, 495, 510, 530, 549, 566
ARRAY.LOAD bookpile[], 38, 44, 36, 38, 35, 30, ~
 29, 29, 46, 13, 32, 20, 38, 31, 31, 24, 26
ARRAY.LOAD vibr[], 0, 500

%==========================================================
% Create menu for resource downloader
DLinit:
GR.BITMAP.LOAD bmptsc, "titlescreen.png"
GR.BITMAP.LOAD bmptwm, "titleworm.png"
GR.BITMAP.LOAD bmpttl, "title.png"
GR.BITMAP.LOAD bmpbpl, "bookpile.png"

IF !allresok % Missing game resources: download them
  DLscreen:
  MKDIR "fr_sounds"
  MKDIR "en_sounds"
  GR.CLS
  GR.BITMAP.DRAW nul, bmptsc, 0, y0 % tsc = title screen

  GR.BITMAP.DRAW nul, bmptwm, -199, y0+529 % twm = title worm
  FOR x=-160 TO 40 STEP 20: GR.MODIFY nul, "x", x: Render(): NEXT
  GR.MODIFY nul, "x", 0: Render()
  PAUSE 400

  GR.BITMAP.DRAW nul, bmpttl, 7, 999 % ttl = bookworm title
  FOR y=967 TO -33 STEP -40: GR.MODIFY nul, "y", y0+y: Render(): NEXT
  GR.MODIFY nul, "y", y0+7: Render()

  GR.BITMAP.SIZE bmpbpl, w, h % bpl = bookpile
  GR.BITMAP.CREATE bmplng, w, h
  GR.BITMAP.DRAW nul, bmplng, 220, y0+270

  GR.TEXT.ALIGN 2 % centered
  SetColor("ffffff77"): txop1=gPrint(320,y0+880,35,"DOWNLOADING","ffb79433",fnt3)
  SetColor("ffffff77"): txop2=gPrint(320,y0+920,33,"GAME RESOURCES...","ffb79433",fnt3)
  SetColor("00000000"): GR.RECT fade,0,y0,640,y0+960: GR.HIDE fade
  SetColor("ffffff77")

  pct=0
  ARRAY.LENGTH al, resource$[]

  FOR i=1 TO al % Download all resources from internet

    % Show progress by stacking on bookpile
    IF INT(17*i/al)>pct
      yt+=bookpile[++pct]
      IF bmptmp THEN GR.BITMAP.DELETE bmptmp
      GR.BITMAP.CROP bmptmp, bmpbpl, 0, h-yt, w, yt
      GR.BITMAP.DRAWINTO.START bmplng
      GR.BITMAP.DRAW nul, bmptmp, 0, h-yt
      GR.BITMAP.DRAWINTO.END
      Render()
    ENDIF

    % For each resource download, make the bottom label blink
    FILE.EXISTS fe, resource$[i]
    IF !fe
      DL(resource$[i])
      GR.HIDE txop1: GR.HIDE txop1+1
      GR.HIDE txop2: GR.HIDE txop2+1
      Render(): PAUSE 15
      GR.SHOW txop1: GR.SHOW txop1+1
      GR.SHOW txop2: GR.SHOW txop2+1
      Render()
    ENDIF
  NEXT

  % All done :)
  GR.MODIFY txop1, "text", "DOWNLOAD COMPLETE!"
  GR.MODIFY txop1+1, "text", "DOWNLOAD COMPLETE!"
  GR.MODIFY txop2, "text", ""
  GR.MODIFY txop2+1, "text", ""
  Render(): PAUSE 1500
  GOSUB FadeOut
  GR.BITMAP.DELETE bmptmp: bmptmp=0
  GR.BITMAP.DELETE bmplng: bmplng=0
  GR.CLS
ENDIF

% Load further resources
LoadMainResources:
GR.BITMAP.LOAD bmpopb, "openbook.png"
GR.BITMAP.LOAD bmpbtn, "button.png"
bmpsnd=LoadPngSeries("snd")
SOUNDPOOL.OPEN 20
IF gLng$="en" | gLng$="no" THEN sLng$="en" ELSE sLng$="fr" % sLng = sound language
FOR i=1 TO 23
  SOUNDPOOL.LOAD nul, INT$(i)+".mp3"
  IF nul<=0 THEN SOUNDPOOL.LOAD nul, INT$(i)+".wav"
  IF nul<=0 THEN SOUNDPOOL.LOAD nul, sLng$+"_sounds/"+INT$(i)+".mp3"
  IF nul<=0 THEN Fatal("Fatal error:\nSound #" + INT$(i) + " (mp3/wav) not found!")
  IF i=1 THEN snd=nul-1
NEXT
AUDIO.LOAD sfx, "1.wav"

% At first start: choose game language
IF gLng$="" THEN GOSUB ChangeLanguage

%==========================================================
MainMenu:
GOSUB LoadLabels
DIM scrabble$[1]: DIM spare$[1]
GOSUB LoadScrabble

GR.CLS: ptrbk1=0: ptrbk2=0: ptrbk3=0: ptrdlg=0
GR.BITMAP.DRAW nul, bmptsc, 0, y0 % tsc = title screen

GR.TEXT.ALIGN 2 % centered
SetColor("ffffff77"): txop1=gPrint(320,y0+880,32,"~Open Source~","ffb79433",fnt3)
GR.TEXT.UNDERLINE 1
SetColor("ffffff77"): txop2=gPrint(320,y0+915,32,"mougino.free.fr","ffb79433",fnt3)
GR.TEXT.UNDERLINE 0

GR.BITMAP.DRAW ptrsnd, bmpsnd+1-mute, 5, y0+856 % mute/unmute symbol

IF !bmptfl THEN GR.BITMAP.SCALE bmptfl, BmpFlag(gLng$), 82, 53 % tfl = tiny flag
GR.BITMAP.DRAW ptrglg, bmptfl, 558, y0+845 % glg = game language

IF !bmpcfl THEN GR.BITMAP.SCALE bmpcfl, BmpFlag("lng"), 75, 48 % cfl = change flag
GR.BITMAP.DRAW ptrclg, bmpcfl, 565, y0+902 % clg = change language

GR.GROUP settings, txop1, txop1+1, txop2, txop2+1, ptrsnd, ptrglg, ptrclg
GR.HIDE settings

GR.BITMAP.DRAW nul, bmptwm, 0, y0+529 % twm = title worm
IF !dejavu
  FOR x=-160 TO 40 STEP 20: GR.MODIFY nul, "x", x: Render(): NEXT
  GR.MODIFY nul, "x", 0: Render()
  PAUSE 200
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+9
  PAUSE 200
ENDIF

GOSUB MakeHowtoButton
GR.BITMAP.DRAW ptrbk3, bmpbk3, 139, y0+682
IF !dejavu
  FOR y=-158 TO 682 STEP 40: GR.MODIFY ptrbk3, "y", y0+y: Render(): NEXT
ENDIF

GOSUB MakeNewGameButton
GR.BITMAP.DRAW ptrbk2, bmpbk2, 128, y0+494
IF !dejavu
  FOR y=-186 TO 494 STEP 40: GR.MODIFY ptrbk2, "y", y0+y: Render(): NEXT
ENDIF

GOSUB MakeContinueButton % contains declaration of 'savefile'
GR.BITMAP.DRAW ptrbk1, bmpbk1, 84, y0+305
IF !dejavu
  FOR y=-175 TO 305 STEP 40: GR.MODIFY ptrbk1, "y", y0+y: Render(): NEXT
ENDIF

GR.BITMAP.DRAW nul, bmpttl, 7, y0+7 % ttl = bookworm title
IF !dejavu
  FOR y=967 TO -33 STEP -40: GR.MODIFY nul, "y", y0+y: Render(): NEXT
  GR.MODIFY nul, "y", y0+7: Render()
ENDIF

IF debug % Add diagonal "debug" on title
  SetColor("ffff0000")
  GR.ROTATE.START -45,320,y0+200
  gPrint(320,y0+200,100,"~debug~","ffffffff",fnt3)
  GR.ROTATE.END
ENDIF

SetColor("00000000"): GR.RECT fade,0,y0,640,y0+960: GR.HIDE fade
SetColor("ff000000"): GR.SHOW settings
IF dejavu THEN GOSUB FadeIn ELSE Render()
dejavu=1

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MainMenuInput:
delay=-1: detectshake=0 % infinite (blocking) WaitInput and WaitRelease
GOSUB WaitInput
IF backkey | inbgnd
 IF isAPK THEN EXIT ELSE END "Closed."
ENDIF
IF touched
 % Tap on 'Continue'
 IF savefile & x>84 & y>305 & x<84+556 & y<305+189
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
  GOTO GameStart
 % Tap on 'How to play'
 ELSEIF x>139 & y>682 & x<139+501 & y<682+92
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
  GOTO HowTo
 % Tap on 'New game'
 ELSEIF x>128 & y>494 & x<128+512 & y<494+190
  startnew=1
  IF savefile
   Dlg1:
   tit$=Label$("dlg1title")
   msg$=Label$("dlg1msg")
   btn1$=Label$("dlg1btn1")
   btn2$=Label$("dlg1btn2")
   GOSUB DialogMessage
   IF debug THEN RETURN
   IF inbgnd THEN EXIT
   startnew=btn2
   IF startnew THEN FILE.DELETE nul, "bookworm.sav": GOSUB MakeContinueButton
  ENDIF
  IF startnew
   IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
   GOTO GameStart
  ENDIF
 % Type in the footer
 ELSEIF y>812
  IF x<95 % Mute button
    GOSUB ToggleMute
  ELSEIF x>558 % Flags
   GOSUB FadeOut
   GOSUB ChangeLanguage
   GOTO MainMenu
  ELSE % Middle of footer
   BROWSE "http://mougino.free.fr/code/index.php?p=Bookworm"
   IF isAPK THEN EXIT ELSE END "Done."
  ENDIF
 ENDIF
ENDIF
GOTO MainMenuInput

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MakeHowtoButton:
GR.BITMAP.LOAD tmp, "book3.png"
IF bmpbk3 THEN GR.BITMAP.DELETE bmpbk3
GR.BITMAP.CREATE bmpbk3, 501, 92
GR.BITMAP.DRAWINTO.START bmpbk3
GR.BITMAP.DRAW nul, tmp, 0, 0
SetColor("ffff00ff"): gPrint(501/2,(92+30)/2,30,Label$("howto"),"ff550055",fnt1)
GR.BITMAP.DRAWINTO.END
IF ptrbk3 THEN GR.MODIFY ptrbk3, "bitmap", bmpbk3
GR.BITMAP.DELETE tmp
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MakeNewGameButton:
GR.BITMAP.LOAD tmp, "book2.png"
IF bmpbk2 THEN GR.BITMAP.DELETE bmpbk2
GR.BITMAP.CREATE bmpbk2, 512, 190
GR.BITMAP.DRAWINTO.START bmpbk2
GR.BITMAP.DRAW nul, tmp, 0, 0
SetColor("ffffff00"): gPrint(512/2,(190+40)/2,40,Label$("newgame"),"ffb79433",fnt1)
GR.BITMAP.DRAWINTO.END
IF ptrbk2 THEN GR.MODIFY ptrbk2, "bitmap", bmpbk2
GR.BITMAP.DELETE tmp
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MakeContinueButton:
FILE.EXISTS savefile, "bookworm.sav"
GR.BITMAP.LOAD tmp, "book1.png"
IF bmpbk1 THEN GR.BITMAP.DELETE bmpbk1
GR.BITMAP.CREATE bmpbk1, 556, 189
GR.BITMAP.DRAWINTO.START bmpbk1
GR.BITMAP.DRAW nul, tmp, 0, 0
IF savefile
 SetColor("ff00ff00"): c$="ff005500"
ELSE
 SetColor("ff7a9729"): c$="ff5b721c"
ENDIF
gPrint(556/2,(189+50)/2,50,Label$("continue"),c$,fnt1)
GR.BITMAP.DRAWINTO.END
IF ptrbk1 THEN GR.MODIFY ptrbk1, "bitmap", bmpbk1
GR.BITMAP.DELETE tmp
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FadeOut:
GR.SHOW fade: FOR i=5 TO 255 STEP 25: GR.MODIFY fade,"alpha",i: Render(): PAUSE 15: NEXT
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FadeIn:
GR.SHOW fade: FOR i=250 TO 0 STEP -25: GR.MODIFY fade,"alpha",i: Render(): PAUSE 15: NEXT: GR.HIDE fade: Render()
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ToggleMute:
GOSUB FadeOut
mute=1-mute: GR.MODIFY ptrsnd, "bitmap", bmpsnd+1-mute
GOSUB FadeIn
GOSUB SaveConfig: GOSUB WaitRelease
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ChangeLanguage:
GR.CLS
GR.BITMAP.DRAW nul, bmptsc, 0, y0 % tsc = title screen
GR.BITMAP.DRAW nul, bmptwm, 0, y0+529 % twm = title worm
GR.TEXT.ALIGN 1 % left
IF gLng$<>"" THEN SetColor("ffb2ed21"): gPrint(10,y0+30,40,"<<< ","ff8ebc1a",fnt3)

FOR i=1 TO nlng
  FILE.EXISTS fe, "labels_"+lng$[i]+".txt"
  IF !fe THEN DL("labels_"+lng$[i]+".txt")
  LoadLngN(lng$[i])
  GR.BITMAP.DRAW nul, BmpFlag(lng$[i]), 140, y0+36+808*(i-1)/nlng
  IF i=1 THEN ptrlng=nul
  SetColor("ffffff77"): gPrint(265,y0+85+808*(i-1)/nlng,50,LngN$(lng$[i]),"ffb79433",fnt3)
NEXT
SetColor("00000000"): GR.RECT fade,0,y0,640,y0+960: GR.HIDE fade
SetColor("ffffff77")
GOSUB FadeIn

LangInput:
GOSUB WaitInput

IF inbgnd THEN EXIT

IF backkey | (gLng$<>"" & touched & x<100 & y<80) % Back
  GOSUB FadeOut
  IF gLng$="" THEN EXIT ELSE RETURN
ENDIF

IF touched & x>124 & y<808 % Touch in language zone
  slng=CEIL(y*nlng/808) % slng = selected language
  chln=(lng$[slng]<>gLng$) % chln = changed language
  IF debug THEN chln=1 % force new language in debug mode

  % Handle language changed
  IF chln
    FILE.EXISTS savefile, "bookworm.sav"
    IF savefile
      tit$=Label$("dlg1title")
      msg$=Label$("dlg1msg")
      btn1$=Label$("dlg1btn1")
      btn2$=Label$("dlg1btn2")
      GOSUB DialogMessage
      IF inbgnd THEN EXIT
      chln=btn2
      IF chln THEN FILE.DELETE nul, "bookworm.sav" ELSE GOSUB WaitRelease: GOTO LangInput
    ENDIF
    gLng$=lng$[slng]
    mLng$=lng$[slng]
    GOSUB NewLangAnim
    IF debug % force download freshest labels
      FILE.DELETE fd, "labels_"+gLng$+".txt"
      POPUP "Force downloading 'labels_"+gLng$+".txt'"
    ENDIF
    CheckAndDL("labels_"+gLng$+".txt")
    CheckAndDL("letters_"+gLng$+".txt")
    CheckAndDL("3_letter_words_"+gLng$+".txt")
    CheckAndDL("4_letter_words_"+gLng$+".txt")
    CheckAndDL("dict_"+gLng$+".txt")
    dico$="" % reset dictionary > will be loaded when starting new game
    IF bmptfl THEN GR.BITMAP.DELETE bmptfl: bmptfl=0 % tfl = tiny flag > reset it!
    GOSUB SaveConfig
    dejavu=0

  % User selected same language > show animation anyway
  ELSE
    GOSUB NewLangAnim
  ENDIF

  GOSUB FadeOut
  % Clean up animation bitmaps
  IF bmptmp THEN GR.BITMAP.DELETE bmptmp: bmptmp=0
  IF bmplng THEN GR.BITMAP.DELETE bmplng: bmplng=0

ELSE % Touch anywhere else
  GOSUB WaitRelease
  GOTO LangInput
ENDIF

GOSUB WaitRelease
RETURN

%==========================================================
NewLangAnim:
GR.HIDE ptrlng+3*(slng-1)
GR.BITMAP.CREATE bmplng, 112*10, 72*10
GR.BITMAP.DRAW ptrani, bmplng, 140, y0+36+808*(slng-1)/nlng
FOR i=1.5 TO 10 STEP 0.5
  IF bmptmp THEN GR.BITMAP.DELETE bmptmp
  GR.BITMAP.SCALE bmptmp, BmpFlag(gLng$), 112*i, 72*i
  GR.BITMAP.DRAWINTO.START bmplng
  GR.BITMAP.DRAW nul, bmptmp, 0, 0
  GR.BITMAP.DRAWINTO.END
  GR.MODIFY ptrani, "alpha", 255-25*i
  GR.MODIFY ptrani, "x", 140-112*i*0.8
  GR.MODIFY ptrani, "y", y0+36+808*(slng-1)/nlng-72*i*0.8
  Render(): PAUSE 20
NEXT
RETURN

%==========================================================
HowTo:
IF !bmpbbl
 GR.BITMAP.LOAD bmpbbl, "howtobubble.png"
 bmpnx=LoadPngSeries("next")
 GR.BITMAP.SCALE bmppv, bmpnx, -140, 140
 GR.BITMAP.SCALE nul, bmpnx+1, -140, 140
 bmphow=LoadPngSeries("howto")
ENDIF

IF debug % Show all DialogMessages first in debug mode
  FOR dl=1 TO 8
    GOSUB dl, Dlg1, Dlg2, Dlg3, Dlg4, Dlg5, Dlg6, Dlg7, Dlg8
    IF inbgnd THEN END "Done."
  NEXT
ENDIF

GR.CLS
% howto background, worm, and bubble
GR.BITMAP.DRAW nul, bmptsc, 0, y0
GR.BITMAP.DRAW nul, bmptwm, -199, y0+529
FOR x=-160 TO 40 STEP 20: GR.MODIFY nul, "x", x: Render(): NEXT
GR.MODIFY nul, "x", 0: Render()
GR.BITMAP.DRAW nul, bmpbbl, 134, y0+527

howtoscr=0
% howto image, next/prev buttons
GR.BITMAP.DRAW ptrhow, bmphow+howtoscr, 663, y0+37
FOR i=1 TO 16
 GR.MODIFY nul, "alpha", 16*i-1 % bubble
 GR.MODIFY ptrhow, "x", 663-i*40: Render()
NEXT
GR.BITMAP.DRAW ptrnxt, bmpnx+1, 480, y0+819
GR.BITMAP.DRAW ptrprv, bmppv, 17, y0+819
GR.HIDE ptrprv

% howto tip text
SetColor("ff000000"): GR.TEXT.SETFONT fnt4: GR.TEXT.SIZE 36
GR.TEXT.ALIGN 1 % left
GR.TEXT.DRAW txtip, 190, y0+600, ""
GR.TEXT.DRAW nul, 190, y0+658, ""
GR.TEXT.DRAW nul, 190, y0+714, ""
GR.TEXT.DRAW nul, 190, y0+772, ""

% back button and howto screen number (n/7)
SetColor("ffb2ed21"): gPrint(10,y0+30,40,"< " + Label$("back"),"ff8ebc1a",fnt3)
GR.TEXT.ALIGN 2 % centered
SetColor("ffb2ed21"): howtext = gPrint(320,y0+920,75,INT$(1+howtoscr)+"/7","ff8ebc1a",fnt3)
GOSUB HowtoFillTip

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HowToInput:
delay=300: detectshake=0 % 300 ms delay for WaitInput and WaitRelease

DO
 GR.MODIFY ptrprv, "bitmap", bmppv+1
 GR.MODIFY ptrnxt, "bitmap", bmpnx
 Render()
 GOSUB WaitInput
 IF touched | backkey | inbgnd THEN D_U.BREAK
 GR.MODIFY ptrprv, "bitmap", bmppv
 GR.MODIFY ptrnxt, "bitmap", bmpnx+1
 Render()
 GOSUB WaitInput 
UNTIL touched | backkey | inbgnd

IF inbgnd THEN EXIT
IF backkey THEN GOTO MainMenu
IF touched & x<320 & y<100 THEN
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
  GOTO MainMenu
ENDIF

DO
  GR.MODIFY ptrprv, "bitmap", bmppv+1
  GR.MODIFY ptrnxt, "bitmap", bmpnx
  Render()
  GOSUB WaitRelease
  IF !touched | backkey | inbgnd THEN D_U.BREAK
  GR.MODIFY ptrprv, "bitmap", bmppv
  GR.MODIFY ptrnxt, "bitmap", bmpnx+1
  Render()
  GOSUB WaitRelease
UNTIL !touched | backkey | inbgnd

IF inbgnd THEN EXIT
IF backkey THEN GOTO MainMenu

IF x>=320 & y>810 & howtoscr<6 % Next how-to
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
  FOR x=23 TO -577 STEP -60: GR.MODIFY ptrhow, "x", x: Render(): NEXT
  howtoscr++
  GR.MODIFY ptrhow, "bitmap", bmphow+howtoscr
  FOR x=623 TO 23 STEP -60: GR.MODIFY ptrhow, "x", x: Render(): NEXT
  GR.MODIFY howtext, "text", INT$(1+howtoscr)+"/7"
  GR.MODIFY howtext+1, "text", INT$(1+howtoscr)+"/7"
  IF howtoscr=1 THEN GR.SHOW ptrprv
  IF howtoscr=6 THEN GR.HIDE ptrnxt
  GOSUB HowtoFillTip
ELSEIF x<320 & y>810 & howtoscr>0 % Previous how-to
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
  FOR x=23 TO 623 STEP 60: GR.MODIFY ptrhow, "x", x: Render(): NEXT
  howtoscr--
  GR.MODIFY ptrhow, "bitmap", bmphow+howtoscr
  FOR x=-577 TO 23 STEP 60: GR.MODIFY ptrhow, "x", x: Render(): NEXT
  GR.MODIFY howtext, "text", INT$(1+howtoscr)+"/7"
  GR.MODIFY howtext+1, "text", INT$(1+howtoscr)+"/7"
  IF howtoscr=0 THEN GR.HIDE ptrprv
  IF howtoscr=5 THEN GR.SHOW ptrnxt
  GOSUB HowtoFillTip
ENDIF
GOTO HowToInput

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HowtoFillTip:
FOR i=0 TO 3: GR.MODIFY txtip+i , "text", "": NEXT
SPLIT msg$[], Label$("tip"+INT$(howtoscr+1)), "\n"
ARRAY.LENGTH al, msg$[]
FOR i=1 TO al: GR.MODIFY txtip+i-1 , "text", msg$[i]: NEXT
ARRAY.DELETE msg$[]
Render()
IF !mute
  IF howtoscr=0
    SOUNDPOOL.PLAY sid, snd+1: PAUSE 300
    FOR i=1 TO 3: SOUNDPOOL.PLAY sid, snd+19,0.99,0.99: PAUSE 100: NEXT
  ELSEIF howtoscr=1
    SOUNDPOOL.PLAY sid, snd+14
  ELSEIF howtoscr=2
    SOUNDPOOL.PLAY sid, snd+10
  ELSEIF howtoscr=3
    SOUNDPOOL.PLAY sid, snd+11
  ELSEIF howtoscr=4
    SOUNDPOOL.PLAY sid, snd+6,0.99,0.99
  ELSEIF howtoscr=5
    SOUNDPOOL.PLAY sid, snd+2,0.99,0.99
  ELSEIF howtoscr=6
    SOUNDPOOL.PLAY sid, snd+6,0.99,0.99
  ENDIF
ENDIF
RETURN

%==========================================================
GameStart:
debug=0 % disable debug mode when playing

% Load resources at first run
IF !bmpbrd
  GR.BITMAP.LOAD bmpbrd, "board.png"
  GR.BITMAP.LOAD bmpflm, "flames.png"
  bmpwrm=LoadPngSeries("worm")
  bmptil=LoadPngSeries("tile")
  bmpbul=LoadPngSeries("bubble")
  GR.BITMAP.SCALE bmptiny, bmptil, 60, 60
  FOR i=1 TO 6: GR.BITMAP.SCALE nul, bmptil+i, 60, 60: NEXT
  DIM tiltyp[7,7]
  DIM tapped[7,7]
  DIM bonusword$[1]
  anim=10 % animation steps
ENDIF

% Load savefile / or start from zero
IF shaken
  ARRAY.SHUFFLE scrabble$[]: ARRAY.COPY scrabble$[50], spare$[]
ELSE % continue / new game
  SENSORS.OPEN 1 % acceleration and orientation sensor
  FILE.EXISTS savefile, "bookworm.sav"
  IF savefile % continue
    GOSUB LoadGame
  ELSE % new game
    ARRAY.SHUFFLE scrabble$[]: ARRAY.COPY scrabble$[50], spare$[]
    score=0: lvl=1
    ARRAY.DELETE tiltyp[]: DIM tiltyp[7,7]
    bonusletters=3
    bonusidx=0 % no bonus word (yet) at level 1
  ENDIF
  GOSUB LoadBonusWords    
ENDIF
pvlvl=1000*(lvl^2-1) % opening score for this level
nxlvl=1000*((lvl+1)^2-1) % score to obtain to reach next level
progress=1+MIN(24, INT(24*(score-pvlvl)/(nxlvl-pvlvl)))

% fade-out title screen (or previous game screen if shaken)
GOSUB FadeOut
IF !LEN(dico$)
  CheckExist("dict_"+gLng$+".txt")
  GRABFILE dico$, "dict_"+gLng$+".txt"
ENDIF

% prepare game screen
GR.CLS: ptrhot=0: ptrsel=0: ptrdlg=0
SetColor("ff000000")
GR.BITMAP.DRAW nul, bmpbrd, 0, y0
GR.BITMAP.DRAW ptrwrm, bmpwrm, 3, y0

% bubble
GR.BITMAP.DRAW ptrbul, bmpbul, 128, y0+10: GR.HIDE ptrbul
SetColor("ffffdd72"): txmot = gPrint(312,y0+43,30,"DAFT","ff000000",fnt2)
SetColor("fffca726"): txworth = gPrint(312,y0+102,60,"+520","ff000000",fnt3)
SetColor("ff006104"): GR.TEXT.SIZE 20: GR.TEXT.SETFONT fnt4: GR.TEXT.BOLD 1
GR.TEXT.DRAW txsubmit, 312, y0+145, Label$("submit")
GR.GROUP txgo, txmot, txmot+1, txworth, txworth+1, txsubmit
SetColor("ff000000"): GR.TEXT.SETFONT fnt4: GR.TEXT.SIZE 36
SPLIT msg$[], Label$("hint1"), "\n"
ARRAY.LENGTH al, msg$[]: IF al<>3 THEN Fatal("Malformed label: hint1 - must be 3 lines")
GR.TEXT.DRAW txhint, 312, y0+58, msg$[1]
GR.TEXT.DRAW nul, 312, y0+98, msg$[2]
GR.TEXT.DRAW nul, 312, y0+138, msg$[3]
ARRAY.DELETE msg$[]
GR.GROUP txnogo, txhint, txhint+1, txhint+2

% bonus word
SetColor("ffe4cb33"): GR.TEXT.SIZE 25: GR.TEXT.SETFONT fnt3: GR.TEXT.BOLD 0
e$=Label$("bonus"): i=IS_IN(" ", e$)
GR.TEXT.DRAW nul, 552, y0+43, LEFT$(e$, i-1)
GR.TEXT.DRAW nul, 552, y0+65, MID$(e$, i+1)
IF bonusidx THEN bonus$=bonusword$[bonusidx]
SetColor("fff8ff01"): txbonus = gPrint(554,y0+120,30,bonus$,"ff000000",fnt2)
IF !bonusreminder THEN bonusreminder=5 % reminder for bonus word after 5 submits

% bottom bar: score, level
SetColor("ff000000"): GR.RECT ptrprogress, progress[progress], y0+862, 567, y0+957
SetColor("ffffc641"): txscore = gPrint(320,y0+930,60,INT$(score),"ff000000",fnt2)
SetColor("ffb4f200"): GR.TEXT.SIZE 14: GR.TEXT.SETFONT "Normal": GR.TEXT.BOLD 1
GR.TEXT.DRAW nul, 606, y0+900, Label$("level")
SetColor("ffb4f200"): GR.TEXT.SIZE 20
GR.TEXT.DRAW txlvl, 606, y0+926, INT$(lvl): GR.TEXT.BOLD 0

% hot layer and selection layer
IF !bmphot THEN GR.BITMAP.CREATE bmphot, 7*90, 7*91+40
GOSUB ResetHot
IF !bmpsel THEN GR.BITMAP.CREATE bmpsel, 7*90, 7*91+40
GOSUB ResetSel

% fade-in game screen
SetColor("ff000000"): GR.RECT fade,0,y0,640,y0+960
GOSUB FadeIn
IF !shaken & !mute THEN SOUNDPOOL.PLAY sid, snd+13

% cascade tiles
danger=0
FOR i=0 to 48
  y=1+MOD(i,7): x=1+FLOOR(i/7)
  GR.BITMAP.DRAW nul, bmptil+tiltyp[x,y], 4+(x-1)*90, y0+173+(7-y)*91+40*MOD(x-1,2)
  GR.HIDE nul: IF i=0 THEN ptrtil=nul % pointer to first tile (bottom left)
  IF y=1 & tiltyp[x,y]=1 THEN danger=1 % fire tile at bottom of board
NEXT
GR.TEXT.SETFONT "Normal": GR.TEXT.SIZE 50
FOR i=0 to 48
  IF !MOD(i,2) & !mute THEN SOUNDPOOL.PLAY sid, snd+8,0.99,0.99
  GR.SHOW ptrtil+i
  y=1+MOD(i,7): x=1+FLOOR(i/7)
  e$=scrabble$[i+1]: IF e$="Q" THEN e$="Qu"
  GR.TEXT.DRAW nul, 49+(x-1)*90, y0+237+(7-y)*91+40*MOD(x-1,2), e$
  IF i=0 THEN txletr=nul % pointer to first letter (bottom left)
  Render()
NEXT
GR.BITMAP.DRAW ptrtiny, bmptiny, 0, y0: GR.HIDE ptrtiny
GR.BITMAP.DRAW ptrhot, bmphot, 4, y0+173
GR.MODIFY ptrhot, "alpha", 150
GR.BITMAP.DRAW ptrsel, bmpsel, 4, y0+173
GR.MODIFY ptrsel, "alpha", 150
Render()

% After shuffle, make fire tiles fall by 1 row + create 2 new fire tiles at the top
IF shaken
  GOSUB ConsumeHotTiles: Render()
  IF !gameover
    PAUSE 400
    x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
    GOSUB CreateFireTile: Render()
    x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
    GOSUB CreateFireTile: Render()
  ENDIF
ENDIF

SetColor("ff000000"): GR.RECT fade,0,y0,640,y0+960: GR.HIDE fade
SetColor("ff0000ff"): GR.SET.STROKE 10 % for word-lines
SENSORS.READ 1, s20, s21, s22

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GameInput:

IF gameover
  gameover=0
  GR.HIDE txgo: GR.HIDE txnogo
  GR.MODIFY txworth, "text", Label$("fire"): GR.MODIFY txworth+1, "text", Label$("fire")
  GR.SHOW txworth: GR.SHOW txworth+1
  GR.BITMAP.DRAW ptrflm, bmpflm, 0, y0+1122
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+5
  FOR y=1122 TO -162 STEP -10
    GR.MODIFY ptrflm, "y", y0+y: Render()
  NEXT
  GR.MODIFY ptrwrm, "bitmap", bmpwrm+3
  IF bmpsel THEN GR.BITMAP.DELETE bmpsel
  GR.BITMAP.CREATE bmpsel, 7*90, 7*91+40
  GR.BITMAP.DRAWINTO.START bmpsel
  SetColor("ff555555")
  GR.RECT nul, 0, 0, 7*90, 7*91+40
  GR.BITMAP.DRAWINTO.END
  GR.MODIFY ptrsel, "bitmap", bmpsel
  GR.SHOW ptrsel
  PAUSE 500
  FOR i=250 TO 0 STEP -10: GR.MODIFY ptrflm,"alpha",i: Render(): NEXT
  PAUSE 1500
  FILE.DELETE nul, "bookworm.sav": savefile=0
  SENSORS.CLOSE
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+16
  Dlg2:
  tit$=Label$("dlg2title")
  dsz=60 % dialog size for message
  msg$="\n" + Label$("dlg2msg1") + " " + INT$(score) + "\n\n"
  msg$+=Label$("dlg2msg2") + " " + INT$(lvl)
  btn1$=Label$("dlg2btn1")
  btn2$=Label$("dlg2btn2")
  GOSUB DialogMessage
  IF debug THEN RETURN
  IF btn2 THEN GOTO GameStart ELSE EXIT
ENDIF

% If we warned the user of a fire tile at bottom:
% next action can bring a game over...
IF danger=2 THEN danger=1

delay=-1: detectshake=1 % infinite (blocking) WaitInput and WaitRelease
GOSUB WaitInput         % + report *shaken* state of phone

IF inbgnd
  GOSUB SaveGame: EXIT

ELSEIF shaken
  Dlg3:
  tit$=Label$("dlg3title")
  msg$=Label$("dlg3msg")
  btn1$=Label$("dlg3btn1")
  btn2$=Label$("dlg3btn2")
  GOSUB DialogMessage
  IF debug THEN RETURN
  IF inbgnd: GOSUB SaveGame: EXIT: ENDIF
  SENSORS.READ 1, s10, s11, s12
  s20=s10: s21=s11: s22=s12
  shaken=btn2 
  IF shaken 
    IF !mute THEN SOUNDPOOL.PLAY sid, snd+11
    FOR x=1 TO 7
      FOR y=1 TO 7
        IF tiltyp[x,y]>1 THEN tiltyp[x,y]=0 % remove tiles 2_green/3_gold/4_saphire
      NEXT
    NEXT
    GOTO GameStart
  ENDIF

ELSEIF backkey | (touched & x<=80 & y>=851)
  SENSORS.CLOSE
  GOSUB SaveGame
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+23,0.99,0.99
  GOTO MainMenu

ELSEIF touched & y>=851 % bottom bar
  IF LEN(mot$)
    IF !mute THEN SOUNDPOOL.PLAY sid, snd+20
    GOSUB ResetSel: Render()
  ENDIF

ELSEIF touched & y<=172 % upper bar
  IF x>142 & x<480
    GOSUB Submit
  ELSEIF x>=480 & bonusidx
    Dlg4:
    tit$=Label$("dlg4title")
    msg$=Label$("dlg4msg")
    btn1$=Label$("dlg4btn1")
    btn2$=Label$("dlg4btn2")
    GOSUB DialogMessage
    IF debug THEN RETURN
    IF inbgnd: GOSUB SaveGame: EXIT: ENDIF
    IF btn2
      GOSUB NextBonusWord
      GOSUB ConsumeHotTiles: Render()
      IF !gameover
        PAUSE 400
        x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
        GOSUB CreateFireTile: Render()
      ENDIF
    ENDIF
  ENDIF

ELSEIF touched % in the board
  x=CEIL(x/90): x=MIN(7, MAX(1, x))
  y=8-CEIL((y-173-40*MOD(x-1,2))/91)
  IF y>=1 & y<=7
    IF !tapped[x,y]
      IF !mute THEN SOUNDPOOL.PLAY sid, snd+4
      % Update selection
      newsel=!((x=lastx & ABS(y-lasty)=1) | (ABS(x-lastx)=1 & y-lasty>=-MOD(x,2) & y-lasty<=MOD(x+1,2)))
      IF newsel THEN GOSUB ResetSel
      GR.BITMAP.DRAWINTO.START bmpsel
      GR.BITMAP.DRAW nul, bmptil+5, (x-1)*90, (7-y)*91+40*MOD(x-1,2)
      IF !newsel
        GR.LINE nul, (x-1)*90+45, (7-y)*91+40*MOD(x-1,2)+45, ~
         (lastx-1)*90+45, (7-lasty)*91+40*MOD(lastx-1,2)+45
      ENDIF
      GR.BITMAP.DRAWINTO.END
      % Register touched tile
      tapped[x,y]=1
      lastx=x: lasty=y
      lastx$+=INT$(x): lasty$+=INT$(y)
      color$+=INT$(tiltyp[x,y])
      e$=scrabble$[7*(x-1)+y]
      IF e$="Q" THEN
        e$="QU"
        lastx$+=INT$(x): lasty$+=INT$(y)
        color$+=INT$(tiltyp[x,y])
      ENDIF
      % Update word
      mot$+=e$
      GR.MODIFY txmot, "text", mot$: GR.MODIFY txmot+1, "text", mot$
      IF LEN(mot$)=2
        SPLIT msg$[], Label$("hint2"), "\n"
        ARRAY.LENGTH al, msg$[]: IF al<>3 THEN Fatal("Malformed label: hint2 - must be 3 lines")
        GR.MODIFY txhint, "text", msg$[1]
        GR.MODIFY txhint+1, "text", msg$[2]
        GR.MODIFY txhint+2, "text", msg$[3]
        ARRAY.DELETE msg$[]
      ELSEIF LEN(mot$)>=3
        GR.HIDE txnogo
        GR.SHOW txgo
      ENDIF
      % Check word validity
      worth=0
      IF LEN(mot$)>=3 % test word validity against dico
        IF IS_IN("\n"+mot$+"\n", dico$) % valid
          worth=Score(mot$, color$, lvl, bonus$)
          GR.MODIFY txworth, "text", "+"+INT$(worth)
          GR.MODIFY txworth+1, "text", "+"+INT$(worth)
          GR.SHOW txworth: GR.SHOW txworth+1
          GR.MODIFY txsubmit, "text", Label$("submit")
          BUNDLE.GET global, "bgd", bgd
          IF bgd THEN
            GR.MODIFY ptrbul, "bitmap", bmpbul+bgd-1
            GR.SHOW ptrbul
          ELSE
            GR.HIDE ptrbul
          ENDIF
        ELSE % not valid
          GR.HIDE ptrbul
          GR.HIDE txworth: GR.HIDE txworth+1
          GR.MODIFY txsubmit, "text", Label$("invalid")
        ENDIF
      ENDIF
      Render()
    ENDIF
  ENDIF
ENDIF
GOTO GameInput

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ResetHot:
IF bmphot THEN GR.BITMAP.DELETE bmphot
GR.BITMAP.CREATE bmphot, 7*90, 7*91+40
IF ptrhot THEN GR.MODIFY ptrhot, "bitmap", bmphot
FOR hx=1 TO 7
  FOR hy=2 TO 7
    IF tiltyp[hx,hy]=1 % 1_fire
      GR.BITMAP.DRAWINTO.START bmphot
      GR.BITMAP.DRAW nul, bmptil+6, (hx-1)*90, (8-hy)*91+40*MOD(hx-1,2)
      GR.BITMAP.DRAWINTO.END
    ENDIF
  NEXT
NEXT
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ResetSel:
mot$="": color$="": lastx$="": lasty$=""
lastx=99: lasty=99: worth=0
ARRAY.DELETE tapped[]: DIM tapped[7,7]
IF bmpsel THEN GR.BITMAP.DELETE bmpsel
GR.BITMAP.CREATE bmpsel, 7*90, 7*91+40
IF ptrsel THEN GR.MODIFY ptrsel, "bitmap", bmpsel
GR.HIDE ptrbul
GR.HIDE txgo
SPLIT msg$[], Label$("hint1"), "\n"
ARRAY.LENGTH al, msg$[]: IF al<>3 THEN Fatal("Malformed label: hint1 - must be 3 lines")
GR.MODIFY txhint, "text", msg$[1]
GR.MODIFY txhint+1, "text", msg$[2]
GR.MODIFY txhint+2, "text", msg$[3]
ARRAY.DELETE msg$[]
GR.SHOW txnogo
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CreateFireTile:
IF !mute
  VIBRATE vibr[], -1
  SOUNDPOOL.PLAY sid, snd+17,0.99,0.99
ENDIF
tiltyp[x,y]=1: GR.MODIFY ptrtil+7*(x-1)+y-1, "bitmap", bmptil+1
GOSUB ResetHot
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CreateGreenTile:
IF !mute THEN SOUNDPOOL.PLAY sid, snd+21,0.99,0.99
tiltyp[x,y]=2: GR.MODIFY ptrtil+7*(x-1)+y-1, "bitmap", bmptil+2
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CreateGoldTile:
IF !mute THEN SOUNDPOOL.PLAY sid, snd+2,0.99,0.99
tiltyp[x,y]=3: GR.MODIFY ptrtil+7*(x-1)+y-1, "bitmap", bmptil+3
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CreateSaphireTile:
IF !mute THEN SOUNDPOOL.PLAY sid, snd+6,0.99,0.99
tiltyp[x,y]=4: GR.MODIFY ptrtil+7*(x-1)+y-1, "bitmap", bmptil+4
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Submit:
IF worth % valid word
  AUDIO.STOP: IF !mute THEN AUDIO.PLAY sfx
  GR.MODIFY ptrwrm, "bitmap", bmpwrm+1 % show swallowing worm
  GR.HIDE ptrhot: GR.HIDE ptrsel: GR.SHOW ptrtiny % hide hot+selection, show tiny tile
  submit++

  % Register longer/highest word for end of level
  IF LEN(mot$)>LEN(longest$) THEN longest$=mot$
  IF worth>highest
    highest=worth
    highest$=mot$
  ENDIF

  % Is submitted word equal to the bonus word ?
  changebonus=(mot$=bonus$)

  % Remove the "U" in "QU" in submitted word
  q=IS_IN("Q", mot$)
  WHILE q
    mot$=LEFT$(mot$, q)+MID$(mot$, q+2)
    lastx$=LEFT$(lastx$, q)+MID$(lastx$, q+2)
    lasty$=LEFT$(lasty$, q)+MID$(lasty$, q+2)
    q=IS_IN("Q", mot$, q+1)
  REPEAT

  % Start animation to update score
  scoranim=0
  TIMER.SET 40

  % Swallow each letter
  crunch=0: colored=0
  FOR i=1 TO LEN(mot$)
    x=VAL(MID$(lastx$,i,1)): y=VAL(MID$(lasty$,i,1))
    GR.HIDE ptrtil+7*(x-1)+y-1 % hide tile
    GR.HIDE txletr+7*(x-1)+y-1 % and letter
    GR.MODIFY ptrtiny, "bitmap", bmptiny+tiltyp[x,y]
    % Cancel danger when fire tile at bottom is swallowed: 
    IF y=1 & tiltyp[x,y]=1 & danger THEN danger=0
    % Count colored tiles: 2_green/3_gold/4_saphire
    IF tiltyp[x,y]>1 THEN colored++
    FOR j=0 TO anim % animate swallow
      IF mute THEN LET done=0 ELSE AUDIO.ISDONE done
      IF done & !MOD(j,5)
        SOUNDPOOL.PLAY sid, snd+19,0.99,0.99
        crunch++
      ENDIF
      xx=68+(anim-j)*(4+(x-1)*90-68)/anim
      yy=y0+68+(anim-j)*(173+(8-y)*91+40*MOD(x-1,2)-68-y0)/anim
      GR.MODIFY ptrtiny, "x", xx, "y", yy
      Render()
    NEXT
  NEXT
  IF !crunch & !mute
    FOR i=1 TO LEN(mot$): SOUNDPOOL.PLAY sid, snd+19,0.99,0.99: PAUSE 50: NEXT
  ENDIF
  GR.HIDE ptrtiny % hide tiny tile
  GR.MODIFY ptrwrm, "bitmap", bmpwrm+2*(danger>0) % back to normal/danger worm

  % Replace swallowed letters
  SortDesc_TagAlong(&lasty$, &lastx$)
  FOR i=1 TO LEN(mot$)
    x=VAL(MID$(lastx$,i,1)): y=VAL(MID$(lasty$,i,1))
    GR.SHOW ptrtil+7*(x-1)+y-1: GR.SHOW txletr+7*(x-1)+y-1
    % make all tiles in the column fall and replace top tile with a spare
    swallowed$=MID$(mot$,i,1)
    GOSUB FallAndFill: Render()
  NEXT
  ARRAY.COPY spare$[], scrabble$[50]

  % Consume all hot tiles which are under fire tiles (if any)
  GOSUB ConsumeHotTiles: Render()
  IF gameover THEN RETURN

  % Punish short words with a fire tile
  % 3-letter-long words -> probability of fire tile 1:3
  IF LEN(mot$)<=3 & !FLOOR(3*RND())
    PAUSE 400
    x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
    GOSUB CreateFireTile: Render()

  % 4-letter-long words -> probability of fire tile 1:4
  ELSEIF LEN(mot$)=4 & !FLOOR(4*RND())
    PAUSE 400
    x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
    GOSUB CreateFireTile: Render()

  % ..reward long words (5 letters or more) with a gold tile, probability 1:2
  ELSEIF LEN(mot$)>=5 & !FLOOR(2*RND())
    PAUSE 400
    x=0: y=0: GOSUB FindFreeTile % find free tile anywhere in the board
    GOSUB CreateGoldTile: Render()
  ENDIF

  % Reward multi-colored word with a saphire tile (systematically)
  IF colored>1
    PAUSE 400
    x=0: y=0: GOSUB FindFreeTile % find free tile anywhere in the board
    GOSUB CreateSaphireTile: Render()
  ENDIF

  % Randomly create green tile(s) (probability 1:5)
  IF !FLOOR(5*RND())
    PAUSE 400
    x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
    GOSUB CreateGreenTile: Render()
    IF !FLOOR(2*RND()) % 1 time out of 2, create a second green tile
      x=0: y=7: GOSUB FindFreeTile % find free tile at the top row
      GOSUB CreateGreenTile: Render()
    ENDIF
  ENDIF

  % Finish
  GR.SHOW ptrsel: GOSUB ResetSel
  GR.SHOW ptrhot: GOSUB ResetHot
  Render()

  % Change bonus word ?
  IF changebonus
    submit=0 % reset reminder for bonus word
    % Switch to 4-letter words after level 20 and 10 or more bonus words found:
    IF lvl>=20 & bonusidx>=10 & bonusletters=3 
      ARRAY.LENGTH al, bonusword$[]
      bonusidx=al+1
    ENDIF
    GOSUB NextBonusWord
  ENDIF

  % Activate bonus words after first "submit" of level 2
  IF lvl=2 & !bonusidx
    GOSUB NextBonusWord
    submit=0 % reset reminder
    Dlg5:
    tit$=Label$("dlg5title")
    msg$=Label$("dlg5msg")
    btn1$=Label$("dlg5btn")
    btn2$=""
    GOSUB DialogMessage
    IF debug THEN RETURN
    IF inbgnd: GOSUB SaveGame: EXIT: ENDIF
  ENDIF

  % Next level ?
  IF progress=25
    PAUSE 200
    IF !mute THEN SOUNDPOOL.PLAY sid, snd+12
    Dlg6:
    e$=Label$("dlg6title"): i=IS_IN(" ", e$)
    tit$=LEFT$(e$, i) + INT$(lvl) + MID$(e$, i)
    msg$=Label$("dlg6msg1") + "\n" + highest$ + " (" + INT$(highest) + " " + Label$("dlg6msg2") + ")\n\n"
    msg$+=Label$("dlg6msg3") + "\n" + longest$ + " (" + INT$(LEN(longest$)) + " " + Label$("dlg6msg4") + ")"
    btn1$=Label$("dlg6btn")
    btn2$=""
    GOSUB DialogMessage
    IF debug THEN RETURN
    highest=0: highest$="": longest$=""
    lvl++
    IF inbgnd: GOSUB SaveGame: EXIT: ENDIF
    pvlvl=1000*(lvl^2-1) % opening score for this level
    nxlvl=1000*((lvl+1)^2-1) % score to obtain to reach next level
    progress=1+MIN(24, INT(24*(score-pvlvl)/(nxlvl-pvlvl)))
    GR.MODIFY ptrprogress, "left", progress[progress]
    GR.MODIFY txlvl, "text", INT$(lvl)
    GOSUB HappyWorm
    IF lvl>=5 % after level 5, reward player with a mix of green/gold tiles
      PAUSE 400
      FOR nt=1 TO FLOOR(2*RND()+1)
        x=0: y=0: GOSUB FindFreeTile % find free tile anywhere in the board
        IF !FLOOR(2*RND()) THEN GOSUB CreateGreenTile ELSE GOSUB CreateGoldTile
        Render()
      NEXT
    ENDIF

  ELSEIF bonusidx & submit>=bonusreminder % reminder for bonus word every N submit (N=5,10,15,20)
    submit=0 % reset reminder
    bonusreminder=MIN(20, bonusreminder+5) % increase time for next reminder, up to 20 max
    Dlg7:
    tit$=Label$("dlg7title")
    msg$=Label$("dlg7msg")
    btn1$=Label$("dlg7btn")
    btn2$=""
    GOSUB DialogMessage
    IF debug THEN RETURN
    IF inbgnd: GOSUB SaveGame: EXIT: ENDIF
  ENDIF

ELSEIF LEN(mot$) % invalid word
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+20
  GOSUB ResetSel
  Render()
ENDIF
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NextBonusWord:
% Cycle through the list of bonus words
ARRAY.LENGTH al, bonusword$[]
IF !bonusidx
  bonusletters=3
  bonusidx=2
  GOSUB LoadBonusWords
ELSEIF bonusidx>=al
  bonusletters++
  IF bonusletters>4 THEN bonusletters=3
  bonusidx=1
  GOSUB LoadBonusWords
ELSE
  bonusidx++
ENDIF
bonus$=bonusword$[bonusidx]
% Animate bonus word change
PAUSE 300
IF !mute THEN SOUNDPOOL.PLAY sid, snd+1
GR.MODIFY ptrwrm, "bitmap", bmpwrm+1
GR.MODIFY txbonus, "text", bonus$: GR.MODIFY txbonus+1, "text", bonus$
FOR x=104 TO 554 STEP (554-104)/anim
  GR.MODIFY txbonus, "x", x
  GR.MODIFY txbonus+1, "x", x
  Render()
NEXT
GR.MODIFY ptrwrm, "bitmap", bmpwrm+2*(danger>0): Render()
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LoadBonusWords:
CheckExist(INT$(bonusletters)+"_letter_words_"+gLng$+".txt")
GRABFILE bonus$, INT$(bonusletters)+"_letter_words_"+gLng$+".txt"
ARRAY.DELETE bonusword$[]
SPLIT bonusword$[], bonus$, "\n"
bonus$=""
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HappyWorm:
% Animation when changing level
FOR i=1 TO 2
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+21,0.99,0.99
  GR.MODIFY ptrwrm, "bitmap", bmpwrm+1: Render(): PAUSE 100
  GR.MODIFY ptrwrm, "bitmap", bmpwrm+2*(danger>0): Render(): PAUSE 100
NEXT
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FindFreeTile:
% find a free tile in row y or column x
security=0
IF !y
  IF !x THEN x=FLOOR(7*RND()+1)
  y=FLOOR(7*RND()+1)
  WHILE security<8 & tiltyp[x,y]: y=1+MOD(y+3+INT(2*RND()),7): security++: REPEAT
ELSEIF !x
  x=FLOOR(7*RND()+1)
  WHILE security<8 & tiltyp[x,y]: x=1+MOD(x+3+INT(2*RND()),7): security++: REPEAT
ENDIF
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ONTIMER:
% Thread routine to update the score after submitting a word
tmpscore=10*INT((score+0.5*worth*scoranim/anim)/10)
GR.MODIFY txscore, "text", INT$(tmpscore)
GR.MODIFY txscore+1, "text", INT$(tmpscore)
progress=1+MIN(24, INT(24*(tmpscore-pvlvl)/(nxlvl-pvlvl)))
GR.MODIFY ptrprogress, "left", progress[progress]
IF MOD(scoranim,6)=0
  GR.MODIFY txscore, "y", y0+925
  GR.MODIFY txscore+1, "y", y0+925
ELSEIF MOD(scoranim,6)=1
  GR.MODIFY txscore, "y", y0+930
  GR.MODIFY txscore+1, "y", y0+930
ENDIF
Render()
scoranim++
IF scoranim>2*anim % reached last animation step
  TIMER.CLEAR
  score+=worth
ENDIF
TIMER.RESUME

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FallAndFill:
% Routine when tile @(x,y) disappears
% 1. make all tiles above fall
FOR yy=y TO 6
  scrabble$[7*(x-1)+yy]=scrabble$[7*(x-1)+yy+1]
  e$=scrabble$[7*(x-1)+yy]: IF e$="Q" THEN e$="Qu"
  GR.MODIFY txletr+7*(x-1)+yy-1, "text", e$
  tiltyp[x,yy]=tiltyp[x,yy+1]
  GR.MODIFY ptrtil+7*(x-1)+yy-1, "bitmap", bmptil+tiltyp[x,yy]
NEXT
IF !danger & y=1 & tiltyp[x,y]=1 THEN danger=3
% 2. then fill empty location at the top with a spare tile
ARRAY.SHUFFLE spare$[] % shuffle spare tiles
scrabble$[7*(x-1)+7]=spare$[7*(x-1)+7]
e$=scrabble$[7*(x-1)+7]: IF e$="Q" THEN e$="Qu"
GR.MODIFY txletr+7*(x-1)+6, "text", e$
tiltyp[x,7]=0
GR.MODIFY ptrtil+7*(x-1)+6, "bitmap", bmptil+tiltyp[x,7]
% 3. finally, push the swallowed letter back into the spare tiles
spare$[7*(x-1)+7]=swallowed$ % (at the place where we pulled one)
% Warn user when a hot tile has reached the bottom of the board
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConsumeHotTiles:
consumed=0: gameover=0
FOR x=1 TO 7
  IF tiltyp[x,1]=1 & danger=1 THEN gameover=1
  FOR y=1 TO 6
    IF tiltyp[x,y+1]=1
      IF !consumed: consumed=1: PAUSE 500: ENDIF
      IF y=1 & !danger THEN danger=3
      IF !mute
        VIBRATE vibr[], -1
        SOUNDPOOL.PLAY sid, snd+3,0.99,0.99
      ENDIF
      FOR j=1 TO anim % make fire tile fall
        GR.MODIFY ptrtil+7*(x-1)+y, "y", y0+173+(6-y)*91+40*MOD(x-1,2)+j*91/anim
        GR.MODIFY txletr+7*(x-1)+y, "y", y0+237+(6-y)*91+40*MOD(x-1,2)+j*91/anim
        Render()
      NEXT
      GR.MODIFY ptrtil+7*(x-1)+y, "y", y0+173+(6-y)*91+40*MOD(x-1,2)
      GR.MODIFY txletr+7*(x-1)+y, "y", y0+237+(6-y)*91+40*MOD(x-1,2)
      swallowed$=scrabble$[7*(x-1)+y]
      GOSUB FallAndFill
      Render()
    ENDIF
  NEXT
NEXT
GR.SHOW ptrhot: GOSUB ResetHot
% Warn user when a hot tile has reached the bottom of the board
IF danger=3 THEN GOSUB ShowDanger
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ShowDanger:
danger=2
PAUSE 300
i=10+FLOOR(3*RND()): IF i>10 THEN i+=3
IF !mute THEN SOUNDPOOL.PLAY sid, snd+i
GR.MODIFY ptrwrm, "bitmap", bmpwrm+2: Render()
Dlg8:
tit$=Label$("dlg8title")
msg$=Label$("dlg8msg")
btn1$=Label$("dlg8btn")
btn2$=""
GOSUB DialogMessage
IF debug THEN RETURN
IF inbgnd: GOSUB SaveGame: EXIT: ENDIF
RETURN

%==========================================================
WaitInput:
touched=0: shaken=0: backkey=0: inbgnd=0
c0=CLOCK()
DO
 PAUSE 10
 GR.TOUCH touched, x, y
 IF detectshake
  SENSORS.READ 1, s10, s11, s12
  shaken=( (s10-s20)^2 ~
          +(s11-s21)^2 ~
          +(s12-s22)^2 ~
          >100 )
 ENDIF
UNTIL touched | shaken | backkey | inbgnd ~
 | (delay>0 & CLOCK()-c0>delay)
IF shaken
 s20=s10: s21=s11: s22=s12
ELSEIF touched
 x/=sw: y=(y-y0)/sh
ENDIF
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
WaitRelease:
touched=1: backkey=0: inbgnd=0
c0=CLOCK()
DO
 PAUSE 10
 GR.TOUCH touched, x, y
 x/=sw: y=(y-y0)/sh
UNTIL !touched | backkey | inbgnd ~
 | (delay>0 & CLOCK()-c0>delay)
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ONBACKKEY:
backkey=1
BACK.RESUME

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ONBACKGROUND:
inbgnd=1
BACKGROUND.RESUME

%==========================================================
CheckResources:
LIST.CREATE s, resource
TEXT.OPEN r, fid, "all.res"
IF fid<0 THEN Fatal("Missing 'all.res' file")
allresok=1
DO
  TEXT.READLN fid, e$: e$=TRIM$(e$)
  IF e$="EOF" THEN D_U.BREAK
  AddToList(resource, 1, e$)
  FILE.EXISTS fe, e$: allresok*=fe
UNTIL 0
TEXT.CLOSE fid
LIST.SIZE resource, ls
ARRAY.DELETE resource$[]
LIST.TOARRAY resource, resource$[]
LIST.CLEAR resource
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LoadLabels:
CheckAndDL("labels_"+mLng$+".txt")
TEXT.OPEN r, fid, "labels_"+mLng$+".txt"
DO % Load menus labels
  TEXT.READLN fid, e$: e$=TRIM$(e$)
  IF e$="EOF" THEN D_U.BREAK
  i=IS_IN("=", e$)
  IF i & LEFT$(e$,1)<>"#" THEN BUNDLE.PUT global, LOWER$(LEFT$(e$, i-1)), REPLACE$(MID$(e$, i+1), "\\n", "\n")
UNTIL 0
TEXT.CLOSE fid
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LoadScrabble:
LIST.CREATE s, scrabble
CheckAndDL("letters_"+gLng$+".txt")
TEXT.OPEN r, fid, "letters_"+gLng$+".txt"
DO
  TEXT.READLN fid, e$: e$=TRIM$(e$)
  IF e$="EOF" THEN D_U.BREAK
  i=IS_IN(" ", e$)
  IF i & LEFT$(e$,1)<>"#"
    AddToList(scrabble, VAL(LEFT$(e$, i-1)), MID$(e$, i+1, 1))
    BUNDLE.PUT global, "val("+MID$(e$, i+1, 1)+")", VAL(TRIM$(MID$(e$, i+2)))
  ENDIF
UNTIL 0
TEXT.CLOSE fid
LIST.SIZE scrabble, ls
IF ls<49*2
  Fatal("Not enough scrabble tiles!\n(at least 98 are necessary)")
ENDIF
ARRAY.DELETE scrabble$[]
LIST.TOARRAY scrabble, scrabble$[]
LIST.CLEAR scrabble
% First 49 elements of scrabble$[] are the 49 tiles of the board,
% starting bottom left, going up, then right.
% The remaining is used as spare tiles for when user submits a word
ARRAY.DELETE spare$[]
DIM spare$[ls-49]
ARRAY.COPY scrabble$[50], spare$[]
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SaveConfig:
TEXT.OPEN w, fid, "bookworm.cfg"
TEXT.WRITELN fid, mLng$ % menus language
TEXT.WRITELN fid, gLng$ % game language
TEXT.WRITELN fid, INT$(mute)
TEXT.CLOSE fid
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SaveGame:
TEXT.OPEN w, fid, "bookworm.sav"
TEXT.WRITELN fid, INT$(lvl)
TEXT.WRITELN fid, INT$(score)
TEXT.WRITELN fid, INT$(bonusletters)
TEXT.WRITELN fid, INT$(bonusidx)
JOIN scrabble$[], scrabble$, ","
TEXT.WRITELN fid, scrabble$
tt$=""
FOR sgx=1 TO 7
  FOR sgy=1 TO 7
    tt$+=INT$(tiltyp[sgx,sgy])+","
  NEXT
NEXT
TEXT.WRITELN fid, LEFT$(tt$, -1)
TEXT.WRITELN fid, INT$(highest)
TEXT.WRITELN fid, highest$
TEXT.WRITELN fid, longest$
TEXT.CLOSE fid
POPUP Label$("saved")
RETURN

% - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LoadGame:
TEXT.OPEN r, fid, "bookworm.sav"
IF !fid THEN RETURN
TEXT.READLN fid, lg$: lvl=VAL(lg$)
TEXT.READLN fid, lg$: score=VAL(lg$)
TEXT.READLN fid, lg$: bonusletters=VAL(lg$)
TEXT.READLN fid, lg$: bonusidx=VAL(lg$)
TEXT.READLN fid, scrabble$
ARRAY.DELETE scrabble$[]
SPLIT scrabble$[], scrabble$, ","
ARRAY.COPY scrabble$[50], spare$[]
TEXT.READLN fid, tiltyp$
SPLIT tiltyp$[], tiltyp$, ","
FOR lgx=1 TO 7
  FOR lgy=1 TO 7
    tiltyp[lgx,lgy]=VAL(tiltyp$[7*(lgx-1)+lgy])
  NEXT
NEXT
ARRAY.DELETE tiltyp$[]
TEXT.READLN fid, lg$: highest=VAL(lg$)
TEXT.READLN fid, highest$
TEXT.READLN fid, longest$
TEXT.CLOSE fid
RETURN

%==========================================================
DialogMessage:
% Create empty book
IF bmpdlg THEN GR.BITMAP.DELETE bmpdlg
GR.BITMAP.CREATE bmpdlg, 590, 637
GR.BITMAP.DRAWINTO.START bmpdlg
GR.BITMAP.DRAW nul, bmpopb, 0, 0
% Write title
GR.TEXT.ALIGN 2 % centered
SetColor("fff8ff01"): gPrint(295,100,50,tit$,"ff000000",fnt3)
IF debug
  GR.TEXT.ALIGN 1 % left
  SetColor("ffff01f8")
  gPrint(20,35,25,"(Dlg#"+INT$(dl)+")","ff000000",fnt3)
  GR.TEXT.ALIGN 2 % centered
ENDIF
SetColor("ff000000"): GR.TEXT.SETFONT fnt4
IF !dsz THEN dsz=44
GR.TEXT.SIZE dsz
GR.TEXT.BOLD 1
SPLIT msg$[], msg$, "\n"
ARRAY.LENGTH al, msg$[]
% Then write message
dy=130-20*(LEN(msg$)>99)
FOR di=1 TO al
  IF !LEN(msg$[di]) THEN dy=100
  GR.TEXT.DRAW nul, 295, dy+70*di, msg$[di]
NEXT
ARRAY.DELETE msg$[]
SetColor("ff7f0000")
GR.TEXT.SETFONT "Normal": GR.TEXT.SIZE 26
% Finally write button(s)
IF LEN(btn2$)
  GR.BITMAP.DRAW nul, bmpbtn, 36, 520
  GR.TEXT.DRAW nul, 155, 560, btn1$
  GR.BITMAP.DRAW nul, bmpbtn, 316, 520
  GR.TEXT.DRAW nul, 435, 560, btn2$
ELSE
  GR.BITMAP.DRAW nul, bmpbtn, 175, 520
  GR.TEXT.DRAW nul, 294, 560, btn1$
ENDIF
GR.TEXT.BOLD 0
GR.BITMAP.DRAWINTO.END
% Update/create the layer in the display list
IF ptrdlg
  GR.MODIFY ptrdlg, "bitmap", bmpdlg, "y", y0+973
  GR.SHOW ptrdlg
ELSE
  GR.BITMAP.DRAW ptrdlg, bmpdlg, 24, y0+973
ENDIF
% Make the dialog appear from bottom of screen
IF !mute THEN SOUNDPOOL.PLAY sid, snd+18,0.99,0.99
FOR di=973 TO 173 STEP -80
  GR.MODIFY ptrdlg, "y", y0+di: Render()
NEXT
btn1=0: btn2=0: dsz=0
delay=-1: detectshake=0 % infinite (blocking) WaitInput and WaitRelease
% Wait for good user feedback: back key or home key or touch on button(s)
DO
  GOSUB WaitInput
  IF touched
    IF LEN(btn2$) & x>24+36 & x<24+36+234 & y>173+520 & y<173+520+100 THEN btn1=1
    IF LEN(btn2$) & x>24+316 & x<24+316+234 & y>173+520 & y<173+520+100 THEN btn2=1
    IF !LEN(btn2$) & x>24+175 & x<24+175+234 & y>173+520 & y<173+520+100 THEN btn1=1
  ENDIF
UNTIL backkey | inbgnd | btn1 | btn2
IF debug & backkey THEN END "Done."
% Make the dialog disappear to bottom of screen
IF !inbgnd
  IF !mute THEN SOUNDPOOL.PLAY sid, snd+18,0.99,0.99
  GOSUB WaitRelease
  FOR di=173 TO 973 STEP 80
    GR.MODIFY ptrdlg, "y", y0+di: Render()
  NEXT
  GR.HIDE ptrdlg: Render()
ENDIF
SetColor("ff0000ff"): GR.SET.STROKE 10 % for word-lines
RETURN