% 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)"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