;======================================================== ; 4 Card Keno - OpenWindowedScreen Edition ; PureBasic 6.x - hardware-accelerated rendering ;======================================================== ; ==================================================================================== ; DPI AWARENESS - must be absolute first code before any Windows init. ; Prevents DWM from scaling the window and leaving white gaps at right/bottom. ; Uses Define (not Protected) because this is module-level code. ; CompilerIf keeps it Windows-only; Linux/macOS compile past it entirely. ; ==================================================================================== CompilerIf #PB_OS_Windows Prototype.i Proto_SetDpiCtx(ctx.i) Prototype.i Proto_SetDpiAware() Define _dpiLib.i = OpenLibrary(#PB_Any, "User32.dll") If _dpiLib Define.Proto_SetDpiCtx _fCtx = GetFunction(_dpiLib, "SetProcessDpiAwarenessContext") Define.Proto_SetDpiAware _fAware = GetFunction(_dpiLib, "SetProcessDPIAware") If _fCtx _fCtx(-4) ; DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ElseIf _fAware _fAware() ; Vista+ fallback EndIf CloseLibrary(_dpiLib) EndIf CompilerEndIf ; ==================================================================================== ; Get current DPI scaling factor (100% = 1.0, 125% = 1.25, 150% = 1.5, etc.) ; Reliable cross-platform version ; ==================================================================================== Procedure.f GetDpiScale() CompilerIf #PB_OS_Windows Protected hdc.i = GetDC_(0) If hdc Protected dpi.i = GetDeviceCaps_(hdc, #LOGPIXELSX) ReleaseDC_(0, hdc) If dpi > 0 ProcedureReturn dpi / 96.0 EndIf EndIf CompilerEndIf ProcedureReturn 1.0 EndProcedure ; ==================================================================================== ; SOUND INIT (module-level, before any procedures) ; ==================================================================================== If Not InitSound() MessageRequester("Keno","Warning: Sound system could not be initialized." + #CRLF$ + "Sounds will be disabled.", #PB_MessageRequester_Warning) EndIf Global gSoundOn.i = 1 Global gMelodyThread.i = 0 Global NewMap PreloadedBeeps.i() ; ==================================================================================== ; SOUND PROCEDURES ; ==================================================================================== Procedure.i CreateBeepSound(frequency.i, duration.i) Protected rate.i=44100, channels.i=1, bits.i=16, amplitude.f=18000.0 Protected samples.i=(rate*duration)/1000, dataSize.i=samples*(bits/8) Protected totalSize.i=44+dataSize Protected *buffer=AllocateMemory(totalSize) If *buffer=0 : ProcedureReturn 0 : EndIf PokeS(*buffer+0,"RIFF",4,#PB_Ascii) : PokeL(*buffer+4,totalSize-8) PokeS(*buffer+8,"WAVE",4,#PB_Ascii) : PokeS(*buffer+12,"fmt ",4,#PB_Ascii) PokeL(*buffer+16,16) : PokeW(*buffer+20,1) : PokeW(*buffer+22,channels) PokeL(*buffer+24,rate): PokeL(*buffer+28,rate*channels*bits/8) PokeW(*buffer+32,channels*bits/8) : PokeW(*buffer+34,bits) PokeS(*buffer+36,"data",4,#PB_Ascii) : PokeL(*buffer+40,dataSize) Protected *data=*buffer+44 Protected angleStep.f=2.0*#PI/(rate/frequency), angle.f=0.0 For i.i=0 To samples-1 PokeW(*data+i*2, Int(Sin(angle)*amplitude)) angle + angleStep Next Protected snd=CatchSound(#PB_Any,*buffer,totalSize) FreeMemory(*buffer) ProcedureReturn snd EndProcedure Procedure InitSounds() PreloadedBeeps("50_10") = CreateBeepSound(50,10) PreloadedBeeps("700_80") = CreateBeepSound(700,80) PreloadedBeeps("900_80") = CreateBeepSound(900,80) PreloadedBeeps("1200_140") = CreateBeepSound(1200,140) PreloadedBeeps("1200_60") = CreateBeepSound(1200,60) PreloadedBeeps("392_200") = CreateBeepSound(392,200) PreloadedBeeps("523_200") = CreateBeepSound(523,200) PreloadedBeeps("659_200") = CreateBeepSound(659,200) PreloadedBeeps("784_400") = CreateBeepSound(784,400) PreloadedBeeps("880_400") = CreateBeepSound(880,400) PreloadedBeeps("587_200") = CreateBeepSound(587,200) PreloadedBeeps("494_200") = CreateBeepSound(494,200) PreloadedBeeps("523_600") = CreateBeepSound(523,600) EndProcedure Procedure PlayBeep(f.i,d.i) : If gSoundOn : PlaySound(PreloadedBeeps(Str(f)+"_"+Str(d))) : EndIf : EndProcedure Procedure PlayTone(f.i,d.i) : If gSoundOn : PlaySound(PreloadedBeeps(Str(f)+"_"+Str(d))) : Delay(d) : EndIf : EndProcedure Procedure PlayTick() : PlayBeep(50,10) : EndProcedure Procedure PlayMediumWin() PlayTone(700,80):Delay(5):PlayTone(900,80):PlayTone(1200,140) EndProcedure Procedure PlayWhoopee() PlayTone(392,200):PlayTone(523,200):PlayTone(659,200):PlayTone(784,400) PlayTone(880,400):PlayTone(784,400):Delay(100) PlayTone(392,200):PlayTone(523,200):PlayTone(659,200):PlayTone(784,400) PlayTone(880,400):PlayTone(784,400):Delay(100) PlayTone(659,200):PlayTone(659,200):PlayTone(587,200):PlayTone(587,200) PlayTone(523,200):PlayTone(494,200):PlayTone(523,600) EndProcedure ;Procedure _TickThread(unused.i) : PlayTick() : EndProcedure Procedure _WhoopeeThread(unused.i) : PlayWhoopee() : gMelodyThread=0 : EndProcedure Procedure _MediumWinThread(unused.i) : PlayMediumWin() : gMelodyThread=0 : EndProcedure ; ==================================================================================== ; CONSTANTS ; ==================================================================================== #NUMS=80 : #ROWS=8 : #COLS=10 : #CARDS=4 #MAX_SEL=12 : #DRAW_COUNT=20 #TopBarH=120 : #BallColW=110 : #TabBarH=52 #Margin=12 : #LeftW=270 Enumeration 1 #winMain EndEnumeration ; ==================================================================================== ; COLOR MACROS ; ==================================================================================== Macro CBlue() : RGB(0,90,200) : EndMacro Macro CGreen() : RGB(40,180,60) : EndMacro Macro CWhite() : RGB(255,255,255) : EndMacro Macro CBorder() : RGB(0,0,0) : EndMacro Macro CXColor() : RGB(255,230,0) : EndMacro Macro CSel() : RGB(140,20,160) : EndMacro Macro CSelText() : RGB(255,220,255) : EndMacro Macro CGold() : RGB(255,215,0) : EndMacro Macro CBg() : RGB(172,176,185) : EndMacro Macro CBlueHi() : RGB(80,160,255) : EndMacro Macro CBlueSh() : RGB(0,45,110) : EndMacro Macro CGreenHi() : RGB(110,230,130) : EndMacro Macro CGreenSh() : RGB(15,100,30) : EndMacro Macro CDrawn() : RGB(210,10,10) : EndMacro Macro CDrawnHi() : RGB(255,100,100) : EndMacro Macro CDrawnSh() : RGB(100,0,0) : EndMacro Macro CSelHi() : RGB(200,80,220) : EndMacro Macro CSelSh() : RGB(70,0,85) : EndMacro Macro CTopBar() : RGB(160, 120, 0) : EndMacro ; ==================================================================================== ; STRUCTURES ; ==================================================================================== Structure CardState selected.b[#NUMS+1] hit.b[#NUMS+1] selCount.i hitCount.i EndStructure Structure KRect x.i : y.i : w.i : h.i EndStructure Structure FallingBall active.i : idx.i : y.f destY.i : destX.i : phase.i : speed.f EndStructure Structure RECT_ALT left.l top.l right.l bottom.l EndStructure ; ==================================================================================== ; GAME STATE GLOBALS ; ==================================================================================== Global Dim cards.CardState(#CARDS) Global Dim drawn.b(#NUMS) Global Dim dupCount.b(#NUMS) Global Dim drawnCount.i(#NUMS) Global Dim drawSequence.i(20) Global Dim cardWin.i(#CARDS) Global Dim cardSetNet.i(#CARDS) Global Dim cardSetHits.i(#CARDS) Global Dim cardSetLosses.i(#CARDS) Global Dim fallingBalls.FallingBall(20) Global Dim payTableGen.i(12,12) Global Dim payTableCas.i(12,12) Global Dim payTable.i(12,12) Global pickCount.i = #DRAW_COUNT Global progressiveJackpot.l = 25000 Global progressiveSeed.l = 25000 Global progressiveGrowth.l = 2 Global gSetWinAmt.i=0, gSetLossAmt.i=0 Global totalWinsGen.i=0, totalLossesGen.i=0 Global totalWinsCas.i=0, totalLossesCas.i=0 Global balance.i=1000, drawCost.i=0, runCount.i=0 Global betAmount.i=1 Global saveFile.s Global drawSeqLen.i=0, drawSeqPos.i=0 Global gFallCount.i=0 Global gCurrentDelayMs.i=550 Global gDrawing.i=0, gAutoRun.i=0 Global gCurrentTab.i=0, gPayoutMode.i=0 Global gJackpotCard.i=0 Global gPayTableVisible.i=0,gAboutVisible.i=0 Global gShowProgressive.i=0,gHeatMapOn.i=0 Global gConsolationMsg.s="" Global gSaveDirty.i=0, gSaveNeeded.i=0 Global gCryptOK.i=0 Global gSpeedVal.i=3, gBetVal.i=1 ;Global gLeftBtnDown.i=0, gRightBtnDown.i=0 ; ==================================================================================== ; FONT GLOBALS ; ==================================================================================== Global fontNum.i, fontStats.i, fontTab.i, fontBalls.i Global fontOverlay.i, fontOverlayBold.i ; ==================================================================================== ; ASTERISK CACHE ; ==================================================================================== Global gAsteriskWhite.i=0, gAsteriskBlack.i=0, gAsteriskSize.i=0 ; ==================================================================================== ; BALL BASKET IMAGE CACHE ; ==================================================================================== Global gBallBasketImg.i=0, gBallCachedW.i=0, gBallCachedH.i=0 ; ==================================================================================== ; LAYOUT GLOBALS ; ==================================================================================== Global gWinW.i=1280, gWinH.i=780 Global gPanelX.i, gPanelW.i Global gPageY.i, gPageH.i ;Global gBtnW.i, gBtnH.i ;Global gCellW.f, gCellH.f Global gBallX.i, gBallY.i, gBallW.i, gBallH.i Global gStatsX.i, gStatsY.i,gStatsW.i,gStatsH.i Global gRDraw.KRect, gRNew.KRect, gRPay.KRect Global gRSpeed.KRect, gRBet.KRect, gRSound.KRect Global gRAutoPick.KRect, gRAbout.KRect Global gRTabBar.KRect, gRGrid.KRect ; ==================================================================================== ; INPUT STATE GLOBALS ; ==================================================================================== Global gPrevLeft.i=0, gPrevRight.i=0 ;Global gSpaceHeld.i=0, gSpaceFirstMs.i=0, gSpaceDrawFired.i=0 Global gDrawBtnHeld.i=0, gDrawBtnMs.i=0, gHoldDetected.i=0 Global gSpaceResult.i=0 ; ==================================================================================== ; TIMING GLOBALS ; ==================================================================================== Global gDrawLastMs.i=0 Global gPauseActive.i=0, gPauseMs.i=0 Global gFallActive.i=0, gFallLastMs.i=0 Global gSaveMs.i=0 Global gNotifyMs.i=0 Global gQuit.i=0 ; ==================================================================================== ; FORWARD DECLARES ; ==================================================================================== Declare UpdateLayout() Declare RenderFrame() Declare DoDraw() Declare CalcPayouts() Declare SaveGame() Declare RequestSave() Declare UpdateDupCounts() Declare UpdateProgressiveFlag() Declare ShowTab(tabIndex.i) Declare RebuildAsterisk(w.i, h.i) Declare RebuildBallBasket(pw.i, ph.i) Declare.i SafeMessageRequester(title.s, msg.s, flags.i) Declare.s SafeInputRequester(title.s, prompt.s, defVal.s) Declare DoAutoPick() Declare SpaceKeyTick(keyDown.i, nowMs.i) Declare DoAutoPickRandom() ; ==================================================================================== ; DIALOG WRAPPERS ; ==================================================================================== Procedure.i SafeMessageRequester(title.s, msg.s, flags.i) ProcedureReturn MessageRequester(title, msg, flags) EndProcedure Procedure.s SafeInputRequester(title.s, prompt.s, defVal.s) ProcedureReturn InputRequester(title, prompt, defVal) EndProcedure ; ==================================================================================== ; SAVE / LOAD ; ==================================================================================== Procedure RequestSave() gSaveNeeded = 1 gSaveMs = ElapsedMilliseconds() EndProcedure Procedure SaveGame() Protected f, c, n, checksum.l f = CreateFile(#PB_Any, saveFile) If f WriteLong(f,$4B454E4F) : WriteLong(f,7) WriteLong(f,balance) : WriteLong(f,totalWinsGen) WriteLong(f,totalLossesGen) : WriteLong(f,totalWinsCas) WriteLong(f,totalLossesCas) : WriteLong(f,betAmount) WriteLong(f,gPayoutMode) : WriteLong(f,progressiveJackpot) WriteLong(f,gSpeedVal) : WriteLong(f,gSoundOn) For c=1 To #CARDS For n=1 To #NUMS : WriteByte(f,cards(c)\selected[n]) : Next Next checksum=(balance+totalWinsGen+totalLossesGen+totalWinsCas+totalLossesCas+ betAmount+gPayoutMode+progressiveJackpot+gSpeedVal+gSoundOn)!$4B454E4F WriteLong(f,checksum) CloseFile(f) EndIf EndProcedure Procedure LoadGame() Protected f,c,n,magic.l,ver.l,checksum.l,expected.l Protected sizeV7.l=4+4+4+4+4+4+4+4+4+4+4+4+(#CARDS*#NUMS)+4 f=ReadFile(#PB_Any,saveFile) If f Protected fsz=Lof(f) If fsz=sizeV7 magic=ReadLong(f) : ver=ReadLong(f) If magic=$4B454E4F And ver=7 balance=ReadLong(f) : totalWinsGen=ReadLong(f) totalLossesGen=ReadLong(f) : totalWinsCas=ReadLong(f) totalLossesCas=ReadLong(f) : betAmount=ReadLong(f) gPayoutMode=ReadLong(f) : progressiveJackpot=ReadLong(f) gSpeedVal=ReadLong(f) : gSoundOn=ReadLong(f) For c=1 To #CARDS cards(c)\selCount=0 For n=1 To #NUMS cards(c)\selected[n]=ReadByte(f) If cards(c)\selected[n] : cards(c)\selCount+1 : EndIf Next Next checksum=ReadLong(f) expected=(balance+totalWinsGen+totalLossesGen+totalWinsCas+totalLossesCas+ betAmount+gPayoutMode+progressiveJackpot+gSpeedVal+gSoundOn)!$4B454E4F CloseFile(f) If checksum=expected If balance<1 : balance=1000 : EndIf If progressiveJackpot5 : gSpeedVal=3 : EndIf If gSoundOn<>0 And gSoundOn<>1 : gSoundOn=1 : EndIf ProcedureReturn EndIf Else : CloseFile(f) : EndIf Else : CloseFile(f) : EndIf EndIf balance=1000 : totalWinsGen=0 : totalLossesGen=0 totalWinsCas=0 : totalLossesCas=0 : betAmount=1 gPayoutMode=0 : progressiveJackpot=progressiveSeed gSpeedVal=3 : gSoundOn=1 For c=1 To #CARDS cards(c)\selCount=0 For n=1 To #NUMS : cards(c)\selected[n]=0 : Next Next EndProcedure ; ==================================================================================== ; PAY TABLE ; ==================================================================================== Procedure ApplyPayTable() Protected s,h For s=1 To 12 For h=0 To 12 If gPayoutMode=0 : payTable(s,h)=payTableGen(s,h) Else : payTable(s,h)=payTableCas(s,h) EndIf Next Next EndProcedure Procedure InitPayTable() payTableGen(1,1)=3 payTableGen(2,2)=12 payTableGen(3,2)=2 : payTableGen(3,3)=42 payTableGen(4,2)=2 : payTableGen(4,3)=10 : payTableGen(4,4)=100 payTableGen(5,3)=5 : payTableGen(5,4)=35 : payTableGen(5,5)=450 payTableGen(6,3)=3 : payTableGen(6,4)=15 : payTableGen(6,5)=100 : payTableGen(6,6)=1600 payTableGen(7,3)=2 : payTableGen(7,4)=10 : payTableGen(7,5)=50 : payTableGen(7,6)=400 : payTableGen(7,7)=7000 payTableGen(8,4)=5 : payTableGen(8,5)=25 : payTableGen(8,6)=150 : payTableGen(8,7)=1500 : payTableGen(8,8)=25000 payTableGen(9,4)=2 : payTableGen(9,5)=10 : payTableGen(9,6)=75 : payTableGen(9,7)=500 : payTableGen(9,8)=5000 : payTableGen(9,9)=50000 payTableGen(10,5)=5 : payTableGen(10,6)=40 : payTableGen(10,7)=200 : payTableGen(10,8)=2000 : payTableGen(10,9)=20000: payTableGen(10,10)=200000 payTableGen(11,5)=3 : payTableGen(11,6)=20 : payTableGen(11,7)=100 : payTableGen(11,8)=1000 : payTableGen(11,9)=10000: payTableGen(11,10)=100000: payTableGen(11,11)=500000 payTableGen(12,6)=10 : payTableGen(12,7)=50 : payTableGen(12,8)=500 : payTableGen(12,9)=5000 : payTableGen(12,10)=50000:payTableGen(12,11)=250000:payTableGen(12,12)=1000000 payTableCas(1,1)=3 payTableCas(2,2)=12 payTableCas(3,2)=1 : payTableCas(3,3)=42 payTableCas(4,2)=1 : payTableCas(4,3)=4 : payTableCas(4,4)=120 payTableCas(5,3)=2 : payTableCas(5,4)=20 : payTableCas(5,5)=750 payTableCas(6,3)=1 : payTableCas(6,4)=7 : payTableCas(6,5)=80 : payTableCas(6,6)=1500 payTableCas(7,4)=2 : payTableCas(7,5)=20 : payTableCas(7,6)=400 : payTableCas(7,7)=7000 payTableCas(8,5)=10 : payTableCas(8,6)=80 : payTableCas(8,7)=1500 : payTableCas(8,8)=10000 payTableCas(9,5)=5 : payTableCas(9,6)=50 : payTableCas(9,7)=300 : payTableCas(9,8)=4000 : payTableCas(9,9)=25000 payTableCas(10,5)=2 : payTableCas(10,6)=20 : payTableCas(10,7)=132 : payTableCas(10,8)=1000 : payTableCas(10,9)=4500 : payTableCas(10,10)=100000 payTableCas(11,6)=10 : payTableCas(11,7)=60 : payTableCas(11,8)=250 : payTableCas(11,9)=2500 : payTableCas(11,10)=25000:payTableCas(11,11)=125000 payTableCas(12,6)=5 : payTableCas(12,7)=25 : payTableCas(12,8)=150 : payTableCas(12,9)=1000 : payTableCas(12,10)=5000 :payTableCas(12,11)=25000:payTableCas(12,12)=150000 ApplyPayTable() EndProcedure ; ==================================================================================== ; CARD / GAME STATE HELPERS ; ==================================================================================== Procedure ResetCard(c.i) Protected n For n=1 To #NUMS : cards(c)\selected[n]=0 : cards(c)\hit[n]=0 : Next cards(c)\selCount=0 : cards(c)\hitCount=0 EndProcedure Procedure ResetAll() Protected c,n For n=1 To #NUMS : drawn(n)=0 : dupCount(n)=0 : Next drawCost=0 : runCount=0 : gSetWinAmt=0 : gSetLossAmt=0 For c=1 To #CARDS ResetCard(c) : cardWin(c)=0 : cardSetNet(c)=0 cardSetHits(c)=0 : cardSetLosses(c)=0 Next gShowProgressive=0 EndProcedure Procedure.i PickRandomNum(maxN.i) Protected r.i If gCryptOK : r=CryptRandom(maxN-1)+1 : Else : r=Random(maxN-1)+1 : EndIf If r<1 : r=1 : EndIf : If r>maxN : r=maxN : EndIf ProcedureReturn r EndProcedure Procedure.i CellNumberFromXY(x.i, y.i, w.i, h.i) Protected cellW.f=w/#COLS, cellH.f=h/#ROWS Protected col.i=Int(x/cellW), row.i=Int(y/cellH) If col<0 Or col>=#COLS Or row<0 Or row>=#ROWS : ProcedureReturn 0 : EndIf ProcedureReturn row*#COLS+col+1 EndProcedure Procedure UpdateDupCounts() Protected n,c,count For n=1 To #NUMS count=0 For c=1 To #CARDS : If cards(c)\selected[n] : count+1 : EndIf : Next dupCount(n)=count Next EndProcedure Procedure UpdateProgressiveFlag() Protected c gShowProgressive=0 For c=1 To #CARDS If cards(c)\selCount>=10 : gShowProgressive=1 : Break : EndIf Next EndProcedure Procedure ToggleSelection(cardIndex.i, num.i) If num<1 Or num>#NUMS : ProcedureReturn : EndIf If cards(cardIndex)\selected[num] cards(cardIndex)\selected[num]=0 : cards(cardIndex)\selCount-1 If cards(cardIndex)\hit[num] cards(cardIndex)\hit[num]=0 : cards(cardIndex)\hitCount-1 If cards(cardIndex)\hitCount<0 : cards(cardIndex)\hitCount=0 : EndIf EndIf Else If cards(cardIndex)\selCount<#MAX_SEL cards(cardIndex)\selected[num]=1 : cards(cardIndex)\selCount+1 If drawn(num) : cards(cardIndex)\hit[num]=1 : cards(cardIndex)\hitCount+1 : EndIf EndIf EndIf UpdateDupCounts() UpdateProgressiveFlag() EndProcedure Procedure.i IsAnyHit(n.i) Protected c For c=1 To #CARDS : If cards(c)\selected[n] : ProcedureReturn 1 : EndIf : Next ProcedureReturn 0 EndProcedure Procedure.i CheckBalance(cost.i) If balance=1 And gHeatMapOn : gHeatMapOn=0 : EndIf EndProcedure Procedure CloseOverlayIfOpen() gPayTableVisible=0 : gAboutVisible=0 EndProcedure ; ==================================================================================== ; AUTO-PICK ; ==================================================================================== Procedure DoAutoPick() If gCurrentTab<1 Or gCurrentTab>#CARDS SafeMessageRequester("Auto-Pick","Please select a Card 1-4 tab first.",#PB_MessageRequester_Ok) ProcedureReturn EndIf Protected cardIdx=gCurrentTab, spotsNeeded=cards(cardIdx)\selCount If spotsNeeded<1 : spotsNeeded=8 : EndIf If spotsNeeded>#MAX_SEL : spotsNeeded=#MAX_SEL : EndIf ResetCard(cardIdx) Dim apUsed.b(#NUMS) Protected picked=0, n, bestN, bestC, totalDrawn=0 For n=1 To #NUMS : totalDrawn+drawnCount(n) : Next If totalDrawn=0 Repeat n=PickRandomNum(#NUMS) If Not apUsed(n) apUsed(n)=1 : cards(cardIdx)\selected[n]=1 : cards(cardIdx)\selCount+1 : picked+1 EndIf Until picked>=spotsNeeded Else While pickedbestC : bestC=drawnCount(n) : bestN=n : EndIf Next If bestN=0 : Break : EndIf apUsed(bestN)=1 : cards(cardIdx)\selected[bestN]=1 : cards(cardIdx)\selCount+1 : picked+1 Wend EndIf UpdateDupCounts() : UpdateProgressiveFlag() gSaveDirty=1 EndProcedure Procedure DoAutoPickRandom() If gCurrentTab<1 Or gCurrentTab>#CARDS SafeMessageRequester("Auto-Pick","Please select a Card 1-4 tab first.",#PB_MessageRequester_Ok) ProcedureReturn EndIf Protected cardIdx=gCurrentTab, spotsNeeded=cards(cardIdx)\selCount If spotsNeeded<1 : spotsNeeded=8 : EndIf If spotsNeeded>#MAX_SEL : spotsNeeded=#MAX_SEL : EndIf ResetCard(cardIdx) Dim apUsed.b(#NUMS) Protected picked=0, n Repeat n=PickRandomNum(#NUMS) If Not apUsed(n) apUsed(n)=1 : cards(cardIdx)\selected[n]=1 : cards(cardIdx)\selCount+1 : picked+1 EndIf Until picked>=spotsNeeded UpdateDupCounts() : UpdateProgressiveFlag() gSaveDirty=1 EndProcedure ; ==================================================================================== ; CALC PAYOUTS ; ==================================================================================== Procedure CalcPayouts() Protected c,spots,hits,anyWin=0,fullMatch=0,progressiveHit=0 gJackpotCard=0 For c=1 To #CARDS spots=cards(c)\selCount : hits=cards(c)\hitCount If spots>=1 And spots<=12 And hits>=0 And hits<=spots cardWin(c)=payTable(spots,hits)*betAmount Else cardWin(c)=0 EndIf If gPayoutMode=0 : totalWinsGen+cardWin(c) : Else : totalWinsCas+cardWin(c) : EndIf gSetWinAmt+cardWin(c) : balance+cardWin(c) If spots>0 cardSetNet(c)+cardWin(c)-betAmount If cardWin(c)>0 : cardSetHits(c)+1 : EndIf EndIf If cardWin(c)>0 : anyWin=1 : ElseIf cards(c)\selCount>0 : cardSetLosses(c)+1 : EndIf If spots>=1 And hits=spots fullMatch=1 If spots>=5 : gJackpotCard=c : EndIf If spots>=10 : progressiveHit=1 : EndIf EndIf Next If fullMatch If gMelodyThread=0 Or IsThread(gMelodyThread)=0 gMelodyThread=CreateThread(@_WhoopeeThread(),0) EndIf ElseIf anyWin If gMelodyThread=0 Or IsThread(gMelodyThread)=0 gMelodyThread=CreateThread(@_MediumWinThread(),0) EndIf EndIf If progressiveHit Protected oldJackpot.l=progressiveJackpot, wonAmount.l=oldJackpot+cardWin(gJackpotCard) balance+oldJackpot If gPayoutMode=0 : totalWinsGen+oldJackpot : Else : totalWinsCas+oldJackpot : EndIf progressiveJackpot=progressiveSeed gAutoRun=0 : gPauseActive=0 ShowTab(gJackpotCard) SafeMessageRequester("PROGRESSIVE JACKPOT HIT!!!", "Card "+Str(gJackpotCard)+" matched ALL "+Str(cards(gJackpotCard)\selCount)+" numbers!"+Chr(10)+Chr(10)+ "You win the PROGRESSIVE JACKPOT of $"+Str(oldJackpot)+"!"+Chr(10)+ "Total payout this draw: $"+Str(wonAmount), #PB_MessageRequester_Ok) ElseIf gJackpotCard>0 gAutoRun=0 : gPauseActive=0 ShowTab(gJackpotCard) SafeMessageRequester("JACKPOT!", "Card "+Str(gJackpotCard)+" matched ALL "+Str(cards(gJackpotCard)\selCount)+" numbers!"+Chr(10)+Chr(10)+ "Payout: $"+Str(cardWin(gJackpotCard))+Chr(10)+"Auto-draw has been stopped.", #PB_MessageRequester_Ok) EndIf If anyWin=0 Protected activeCards=0, allActive=1, anyHits=0, nn, distinctCount=0 For c=1 To #CARDS If cards(c)\selCount>0 activeCards+1 If cards(c)\selCount<7 : allActive=0 : EndIf EndIf anyHits+cards(c)\hitCount Next If activeCards=0 : allActive=0 : EndIf For nn=1 To #NUMS For c=1 To #CARDS If cards(c)\selected[nn] : distinctCount+1 : Break : EndIf Next Next If allActive And anyHits=0 And distinctCount>=15 Protected consolePer.i=1000/activeCards, consoleRem.i=1000-(consolePer*activeCards) For c=1 To #CARDS If cards(c)\selCount>=7 Protected cAmt.i=consolePer If consoleRem>0 : cAmt+1 : consoleRem-1 : EndIf cardWin(c)+cAmt : cardSetNet(c)+cAmt : cardSetHits(c)+1 balance+cAmt : gSetWinAmt+cAmt If gPayoutMode=0 : totalWinsGen+cAmt : Else : totalWinsCas+cAmt : EndIf EndIf Next gConsolationMsg="$"+Str(consolePer)+"/card consolation paid" gNotifyMs=ElapsedMilliseconds() RequestSave() EndIf EndIf EndProcedure ; ==================================================================================== ; DO DRAW ; ==================================================================================== Procedure DoDraw() Protected n,i,r,c If gDrawing : ProcedureReturn : EndIf ShowTab(0) Protected anySelected=0 For c=1 To #CARDS : If cards(c)\selCount>0 : anySelected=1 : Break : EndIf : Next If anySelected=0 SafeMessageRequester("No Numbers Chosen", "Please select numbers on at least one card before drawing.", #PB_MessageRequester_Ok) ShowTab(1) : ProcedureReturn EndIf drawCost=0 For c=1 To #CARDS : cardWin(c)=0 : If cards(c)\selCount>0 : drawCost+betAmount : EndIf : Next If CheckBalance(drawCost)=0 : ProcedureReturn : EndIf balance-drawCost : gSetLossAmt+drawCost If gPayoutMode=0 : totalLossesGen+drawCost : Else : totalLossesCas+drawCost : EndIf Protected qualifyCount=0 For c=1 To #CARDS : If cards(c)\selCount>=10 : qualifyCount+1 : EndIf : Next If qualifyCount>0 : progressiveJackpot+(progressiveGrowth*qualifyCount) : EndIf For n=1 To #NUMS : drawn(n)=0 : Next For c=1 To #CARDS cards(c)\hitCount=0 For n=1 To #NUMS : cards(c)\hit[n]=0 : Next Next drawSeqLen=#DRAW_COUNT : drawSeqPos=0 For i=1 To 20 : fallingBalls(i)\active=0 : Next gFallCount=0 : gFallActive=0 For i=0 To drawSeqLen-1 Repeat : r=PickRandomNum(#NUMS) : Until drawn(r)=0 drawn(r)=1 : drawSequence(i)=r Next For n=1 To #NUMS : drawn(n)=0 : Next Select gSpeedVal Case 1 : gCurrentDelayMs=1250 Case 2 : gCurrentDelayMs=900 Case 3 : gCurrentDelayMs=550 Case 4 : gCurrentDelayMs=250 Case 5 : gCurrentDelayMs=80 EndSelect gConsolationMsg="" gDrawing=1 gDrawLastMs=ElapsedMilliseconds()-gCurrentDelayMs-1 EndProcedure ; ==================================================================================== ; REVEAL NEXT BALL ; ==================================================================================== Procedure RevealNextBall() Protected n,c Protected ph=gBallH, slotH=(ph-#TopBarH)/20 Protected r2=slotH/2-1 : If r2<2 : r2=2 : EndIf Protected rmarg=r2+2 Protected lx=rmarg+r2, rx=gBallW-rmarg-r2 If drawSeqPos>=drawSeqLen gDrawing=0 runCount+1 CalcPayouts() RequestSave() : gSaveDirty=0 If gAutoRun If gSpeedVal>=4 DoDraw() Else gPauseActive=1 : gPauseMs=ElapsedMilliseconds() EndIf EndIf ProcedureReturn EndIf n=drawSequence(drawSeqPos) drawn(n)=1 : drawnCount(n)+1 : drawSeqPos+1 Protected hitThisBall=0 For c=1 To #CARDS If cards(c)\selected[n] cards(c)\hit[n]=1 : cards(c)\hitCount+1 : hitThisBall=1 EndIf Next ;If hitThisBall : CreateThread(@_TickThread(),0) : EndIf If hitThisBall : PlayBeep(50,10) : EndIf Protected fallSlot=drawSeqPos Protected destY=ph-(fallSlot-1)*slotH-slotH/2 Protected destX : If fallSlot%2=1 : destX=lx : Else : destX=rx : EndIf fallingBalls(drawSeqPos)\active=1 : fallingBalls(drawSeqPos)\idx=drawSeqPos fallingBalls(drawSeqPos)\y=#TopBarH : fallingBalls(drawSeqPos)\destY=destY fallingBalls(drawSeqPos)\destX=destX : fallingBalls(drawSeqPos)\phase=0 Protected fallDist.f=destY-(#TopBarH+4) Protected ticksPerInterval.f=gCurrentDelayMs/16.0 If ticksPerInterval<1 : ticksPerInterval=1 : EndIf Protected spd.f=fallDist/ticksPerInterval If spd<2.0 : spd=2.0 : EndIf : If spd>40.0 : spd=40.0 : EndIf fallingBalls(drawSeqPos)\speed=spd gFallCount+1 : gFallActive=1 : gFallLastMs=ElapsedMilliseconds() EndProcedure ; ==================================================================================== ; ADVANCE FALL ; ==================================================================================== Procedure AdvanceFall() Protected i, stillActive=0 For i=1 To 20 If fallingBalls(i)\active fallingBalls(i)\phase+1 Protected s.f=fallingBalls(i)\speed If s<=0.1 : s=8.0 : EndIf fallingBalls(i)\y+s If fallingBalls(i)\y>=fallingBalls(i)\destY fallingBalls(i)\y=fallingBalls(i)\destY fallingBalls(i)\active=0 : gFallCount-1 Else stillActive+1 EndIf EndIf Next If stillActive=0 : gFallActive=0 : EndIf EndProcedure ; ==================================================================================== ; RUN TIMERS ; ==================================================================================== Procedure RunTimers() Protected now=ElapsedMilliseconds() If gDrawing And now-gDrawLastMs>=gCurrentDelayMs gDrawLastMs=now RevealNextBall() EndIf If gFallActive And now-gFallLastMs>=16 gFallLastMs=now AdvanceFall() EndIf If gPauseActive And now-gPauseMs>=1500 gPauseActive=0 If gAutoRun : DoDraw() : EndIf EndIf If gSaveNeeded And now-gSaveMs>=3000 gSaveNeeded=0 : SaveGame() EndIf If gConsolationMsg<>"" And now-gNotifyMs>=5000 gConsolationMsg="" EndIf EndProcedure ; ==================================================================================== ; ASTERISK IMAGES ; ==================================================================================== Procedure.i RenderOneAsterisk(sz.i, col.l) If sz<=0 : ProcedureReturn 0 : EndIf Protected img=CreateImage(#PB_Any,sz,sz,32,#PB_Image_Transparent) If StartVectorDrawing(ImageVectorOutput(img)) Protected cx.d=sz/2, cy.d=sz/2, ra.d=sz/2-2, thk.d=sz/9, k VectorSourceColor(col) AddPathCircle(cx,cy,thk*0.7) : FillPath() For k=0 To 5 Protected ang.d=k*#PI/6 Protected dx1.d=Cos(ang)*ra, dy1.d=Sin(ang)*ra MovePathCursor(cx-dx1,cy-dy1) : AddPathLine(cx+dx1,cy+dy1) StrokePath(thk,#PB_Path_RoundEnd|#PB_Path_RoundCorner) Next StopVectorDrawing() EndIf ProcedureReturn img EndProcedure Procedure BuildAsteriskImage(sz.i) If sz<=0 : ProcedureReturn : EndIf If gAsteriskWhite<>0 And gAsteriskSize=sz : ProcedureReturn : EndIf If gAsteriskWhite<>0 : FreeImage(gAsteriskWhite) : EndIf If gAsteriskBlack<>0 : FreeImage(gAsteriskBlack) : EndIf gAsteriskWhite=RenderOneAsterisk(sz,RGBA(255,255,255,255)) gAsteriskBlack=RenderOneAsterisk(sz,RGBA(0,0,0,255)) gAsteriskSize=sz EndProcedure Procedure RebuildAsterisk(w.i, h.i) Protected cellW.f = w/#COLS Protected cellH.f = h/#ROWS Protected astSize = Int(cellW) If Int(cellH) maxFixed : scaleFixed = maxFixed : EndIf ; Free old fonts If fontNum : FreeFont(fontNum) : EndIf If fontStats : FreeFont(fontStats) : EndIf If fontTab : FreeFont(fontTab) : EndIf If fontBalls : FreeFont(fontBalls) : EndIf If fontOverlay : FreeFont(fontOverlay) : EndIf If fontOverlayBold : FreeFont(fontOverlayBold) : EndIf ; Cross-platform font names CompilerIf #PB_OS_Windows Protected fontName.s = "Segoe UI" CompilerElse Protected fontName.s = "Sans" ; works on Linux and macOS CompilerEndIf fontNum = LoadFont(#PB_Any, fontName, Int(24*scale), #PB_Font_Bold) fontStats = LoadFont(#PB_Any, fontName, Int(11*scaleFixed)) fontTab = LoadFont(#PB_Any, fontName, Int(16*scaleFixed), #PB_Font_Bold) fontBalls = LoadFont(#PB_Any, fontName, Int(14*scale), #PB_Font_Bold) fontOverlay = LoadFont(#PB_Any, fontName, Int(12*scale)) fontOverlayBold = LoadFont(#PB_Any, fontName, Int(13*scale), #PB_Font_Bold) EndProcedure ; ==================================================================================== ; BALL BASKET IMAGE ; ==================================================================================== Procedure RebuildBallBasket(pw.i, ph.i) If pw<=0 Or ph<=0 : ProcedureReturn : EndIf If gBallBasketImg<>0 : FreeImage(gBallBasketImg) : gBallBasketImg=0 : EndIf Protected baskH=#TopBarH, slotH=(ph-baskH)/20 Protected r=slotH/2-1 : If r<2 : r=2 : EndIf Protected margin2=r+2, leftX=margin2+r, rightX=pw-margin2-r, centX=pw/2 Protected wi,i,ox,oy,slot,lbl.s gBallBasketImg=CreateImage(#PB_Any,pw,ph,32) If gBallBasketImg=0 : ProcedureReturn : EndIf If StartDrawing(ImageOutput(gBallBasketImg)) Box(0,0,pw,ph,RGB(150,150,160)) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(155,155,155)) Box(4,4,pw-8,baskH-10) FrontColor(RGB(85,85,85)) For wi=1 To 3 : LineXY(4+wi*(pw-8)/4,4,4+wi*(pw-8)/4,baskH-6) : Next For wi=1 To 2 : LineXY(4,4+wi*(baskH-10)/3,pw-4,4+wi*(baskH-10)/3) : Next FrontColor(RGB(170,170,170)) : Box(4,4,pw-8,baskH-10) DrawingFont(FontID(fontBalls)) DrawingMode(#PB_2DDrawing_Transparent) Protected bth.i = TextHeight("M") lbl="BALLS" : FrontColor(RGB(0,0,0)) DrawText(centX-TextWidth(lbl)/2, 4, lbl) lbl="v v v" : FrontColor(RGB(255,210,0)) Protected caretY.i = 4+bth+3 If caretY+bth > baskH-4 : caretY=baskH-bth-4 : EndIf DrawText(centX-TextWidth(lbl)/2, caretY, lbl) DrawingFont(FontID(fontStats)) For i=1 To 20 slot=i : oy=ph-(slot-1)*slotH-slotH/2 If slot%2=1 : ox=leftX : Else : ox=rightX : EndIf DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(38,38,48)) : Circle(ox,oy,r) Next StopDrawing() EndIf gBallCachedW=pw : gBallCachedH=ph EndProcedure ; ==================================================================================== ; LAYOUT - computes all positions from window size ; ==================================================================================== Procedure UpdateLayout() ; gWinW = WindowWidth(#winMain) ; gWinH = WindowHeight(#winMain) Protected btnH = #TopBarH - 2*#Margin Protected by = (#TopBarH - btnH) / 2 ; vertical centre of buttons in top bar Protected panelX=#Margin+#LeftW+#Margin Protected panelW=gWinW-panelX-#BallColW Protected pageY=#TopBarH+#TabBarH Protected pageH=gWinH-pageY-#Margin If panelW<200 : panelW=200 : EndIf If pageH<200 : pageH=200 : EndIf gPanelX=panelX : gPanelW=panelW gPageY=pageY : gPageH=pageH gBtnH=btnH gBallX=gWinW-#BallColW : gBallY=#TopBarH gBallW=#BallColW : gBallH=gWinH-#TopBarH-#Margin gStatsX=0 : gStatsY=#TopBarH : gStatsW=panelX : gStatsH=gWinH-#TopBarH-#Margin Protected gap=2 Protected bw=(gWinW-2*#Margin-7*gap)/8 gRDraw\x=#Margin+0*(bw+gap) : gRDraw\y=by : gRDraw\w=bw : gRDraw\h=btnH gRNew\x=#Margin+1*(bw+gap) : gRNew\y=by : gRNew\w=bw : gRNew\h=btnH gRPay\x=#Margin+2*(bw+gap) : gRPay\y=by : gRPay\w=bw : gRPay\h=btnH gRSpeed\x=#Margin+3*(bw+gap) : gRSpeed\y=by : gRSpeed\w=bw : gRSpeed\h=btnH gRBet\x=#Margin+4*(bw+gap) : gRBet\y=by : gRBet\w=bw : gRBet\h=btnH gRSound\x=#Margin+5*(bw+gap) : gRSound\y=by : gRSound\w=bw : gRSound\h=btnH gRAutoPick\x=#Margin+6*(bw+gap) : gRAutoPick\y=by: gRAutoPick\w=bw : gRAutoPick\h=btnH Protected lastX=#Margin+7*(bw+gap) gRAbout\x=lastX : gRAbout\y=by : gRAbout\w=gWinW-#Margin-lastX : gRAbout\h=btnH ;gBtnW=bw gRTabBar\x=panelX : gRTabBar\y=#TopBarH : gRTabBar\w=panelW : gRTabBar\h=#TabBarH gRGrid\x=panelX : gRGrid\y=pageY : gRGrid\w=panelW : gRGrid\h=pageH RebuildAsterisk(panelW, pageH) If gBallCachedW<>gBallW Or gBallCachedH<>gBallH RebuildBallBasket(gBallW, gBallH) EndIf EndProcedure ; ==================================================================================== ; DRAWING UTILITIES ; ==================================================================================== Procedure DrawBevel(x.i,y.i,w.i,h.i,hiColor.i,shColor.i) Protected b=3 DrawingMode(#PB_2DDrawing_Default) FrontColor(hiColor) : Box(x,y,w,b) : Box(x,y,b,h) FrontColor(shColor) : Box(x,y+h-b,w,b) : Box(x+w-b,y,b,h) EndProcedure Procedure DrawHugeX(x.i,y.i,w.i,h.i) Protected mx=w*0.05, my=h*0.05 Protected x1=x+mx, y1=y+my, x2=x+w-mx, y2=y+h-my, k DrawingMode(#PB_2DDrawing_Default) FrontColor(CXColor()) For k=-5 To 5 LineXY(x1,y1+k,x2,y2+k) : LineXY(x1+k,y1,x2+k,y2) LineXY(x2,y1+k,x1,y2+k) : LineXY(x2+k,y1,x1+k,y2) Next EndProcedure Procedure DrawBtnBase(x.i,y.i,w.i,h.i,bg.i,hiC.i,shC.i) DrawingMode(#PB_2DDrawing_Default) FrontColor(bg) : Box(x,y,w,h) FrontColor(hiC): Box(x,y,w,3) : Box(x,y,3,h) FrontColor(shC): Box(x,y+h-3,w,3): Box(x+w-3,y,3,h) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(0,0,0)) : Box(x,y,w,h) EndProcedure ; ==================================================================================== ; DRAW GRID ; ==================================================================================== Procedure DrawGrid_Screen(gx.i, gy.i, gw.i, gh.i, mode.i, cardIndex.i) Protected cellW.f=gw/#COLS, cellH.f=gh/#ROWS Protected heatMaxC=1, heatMinC=0, heatRangeF.f=1.0 If mode=0 And gHeatMapOn Protected hn, heatTempMin=2000000000 For hn=1 To #NUMS If drawnCount(hn)>heatMaxC : heatMaxC=drawnCount(hn) : EndIf If drawnCount(hn)>0 And drawnCount(hn)heatMinC : heatRangeF=heatMaxC-heatMinC : EndIf EndIf DrawingMode(#PB_2DDrawing_Default) Box(gx,gy,gw,gh,RGB(20,30,70)) DrawingFont(FontID(fontNum)) Protected row,col,n,x,y,bg,tcol Protected isSel,isHit,isDup,isDrawn Protected s.s,tw,th,tx.i,ty.i For row=0 To #ROWS-1 For col=0 To #COLS-1 n=row*#COLS+col+1 x=gx+Int(col*cellW) : y=gy+Int(row*cellH) If mode=0 And gHeatMapOn DrawingMode(#PB_2DDrawing_Default) Box(x,y,Int(cellW)+1,Int(cellH)+1,CBlue()) Protected cellDrawn.f=drawnCount(n) Protected heatRatioF.f If cellDrawn=0.0 : heatRatioF=0.0 ElseIf heatRangeF>0.0 : heatRatioF=0.15+((cellDrawn-heatMinC)/heatRangeF)*0.85 Else : heatRatioF=1.0 : EndIf If heatRatioF>1.0 : heatRatioF=1.0 : EndIf Protected heatAlpha.i=Int(heatRatioF*215) If heatAlpha>0 DrawingMode(#PB_2DDrawing_AlphaBlend) FrontColor(RGBA(255,130,0,heatAlpha)) Box(x,y,Int(cellW)+1,Int(cellH)+1) EndIf DrawingMode(#PB_2DDrawing_Default) DrawBevel(x,y,Int(cellW)+1,Int(cellH)+1,CBlueHi(),CBlueSh()) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(CBorder()) : Box(x,y,Int(cellW)+1,Int(cellH)+1) s=Str(n) : tw=TextWidth(s) : th=TextHeight(s) tx=x+(Int(cellW)-tw)/2 : ty=y+(Int(cellH)-th)/2 DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(0,0,0)) : DrawText(tx+1,ty+1,s) FrontColor(CWhite()) : DrawText(tx,ty,s) Else If mode=1 isSel=cards(cardIndex)\selected[n] : isHit=cards(cardIndex)\hit[n] isDup=0 : isDrawn=drawn(n) Else If dupCount(n)>0 : isSel=1 : Else : isSel=0 : EndIf If dupCount(n)>1 : isDup=1 : Else : isDup=0 : EndIf isDrawn=drawn(n) If isDrawn And isSel : isHit=1 : Else : isHit=0 : EndIf EndIf If isHit : bg=CGreen() : tcol=CWhite() ElseIf isDrawn: bg=CDrawn() : tcol=CWhite() ElseIf isSel : bg=CSel() : tcol=CSelText() Else : bg=CBlue() : tcol=CWhite() EndIf DrawingMode(#PB_2DDrawing_Default) Box(x,y,Int(cellW)+1,Int(cellH)+1,bg) If isHit : DrawBevel(x,y,Int(cellW)+1,Int(cellH)+1,CGreenHi(),CGreenSh()) ElseIf isDrawn : DrawBevel(x,y,Int(cellW)+1,Int(cellH)+1,CDrawnHi(),CDrawnSh()) ElseIf isSel : DrawBevel(x,y,Int(cellW)+1,Int(cellH)+1,CSelSh(),CSelHi()) Else : DrawBevel(x,y,Int(cellW)+1,Int(cellH)+1,CBlueHi(),CBlueSh()) EndIf DrawingMode(#PB_2DDrawing_Outlined) FrontColor(CBorder()) : Box(x,y,Int(cellW)+1,Int(cellH)+1) If Not (mode=0 And isDup And isSel) s=Str(n) : tw=TextWidth(s) : th=TextHeight(s) tx=x+(Int(cellW)-tw)/2 : ty=y+(Int(cellH)-th)/2 DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(0,0,0)) : DrawText(tx+1,ty+1,s) FrontColor(tcol) : DrawText(tx,ty,s) EndIf If isHit DrawHugeX(x,y,Int(cellW),Int(cellH)) If Not (mode=0 And isDup And isSel) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(0,0,0)) : DrawText(tx,ty,s) EndIf EndIf If mode=0 And isDup And isSel And gAsteriskWhite<>0 Protected ax=x+(Int(cellW)-gAsteriskSize)/2 Protected ay=y+(Int(cellH)-gAsteriskSize)/2 DrawingMode(#PB_2DDrawing_AlphaBlend) If isHit : DrawImage(ImageID(gAsteriskBlack),ax,ay) Else : DrawImage(ImageID(gAsteriskWhite),ax,ay) EndIf EndIf EndIf Next Next EndProcedure ; ==================================================================================== ; DRAW TAB BAR ; ==================================================================================== Procedure DrawTabBar_Screen() Protected pw=gRTabBar\w, ph=#TabBarH Protected tx2=gRTabBar\x, ty2=gRTabBar\y Protected tabW=pw/5, i, tx, bg, fg, lbl.s, tw, th DrawingMode(#PB_2DDrawing_Default) FrontColor(RGB(40,40,52)) : Box(tx2,ty2,pw,ph) DrawingFont(FontID(fontTab)) For i=0 To 4 tx=tx2+i*tabW If i=gCurrentTab : bg=RGB(255,215,0) : fg=RGB(20,20,20) Else : bg=RGB(65,65,82) : fg=RGB(195,195,210) EndIf DrawingMode(#PB_2DDrawing_Default) FrontColor(bg) : Box(tx+1,ty2+2,tabW-2,ph-2) If i=gCurrentTab : FrontColor(RGB(255,255,170)) Else : FrontColor(RGB(95,95,110)) EndIf Box(tx+1,ty2+2,tabW-2,2) : Box(tx+1,ty2+2,2,ph-2) If i=gCurrentTab : FrontColor(RGB(170,145,0)) : Box(tx+1,ty2+ph-4,tabW-2,4) Else : FrontColor(RGB(28,28,38)) : Box(tx+1,ty2+ph-2,tabW-2,2) EndIf Box(tx+tabW-3,ty2+2,2,ph-2) Select i Case 0 : lbl="Main" : Case 1 : lbl="Card 1" Case 2 : lbl="Card 2" : Case 3 : lbl="Card 3" Default: lbl="Card 4" EndSelect DrawingMode(#PB_2DDrawing_Transparent) FrontColor(fg) tw=TextWidth(lbl) : th=TextHeight(lbl) If i=0 And gHeatMapOn DrawText(tx+(tabW-tw)/2-10,(ty2+(ph-th)/2),lbl) DrawingMode(#PB_2DDrawing_Default) FrontColor(RGB(255,120,0)) Circle(tx+(tabW-tw)/2+tw+4,ty2+ph/2,6) DrawingMode(#PB_2DDrawing_AlphaBlend) FrontColor(RGBA(255,220,100,160)) Circle(tx+(tabW-tw)/2+tw+2,ty2+ph/2-2,3) Else DrawText(tx+(tabW-tw)/2,(ty2+(ph-th)/2),lbl) EndIf Next EndProcedure ; ==================================================================================== ; DRAW BALL COLUMN ; ==================================================================================== Procedure DrawBallCanvas_Screen() Protected px=gBallX, py=gBallY, pw=gBallW, ph=gBallH Protected baskH=#TopBarH, slotH=(ph-baskH)/20 Protected r=slotH/2-1 : If r<2 : r=2 : EndIf Protected margin2=r+2, leftX=px+margin2+r, rightX=px+pw-margin2-r, centX=px+pw/2 Protected i, ox, oy, n, slot, ns.s, nw, nh If gBallBasketImg<>0 DrawImage(ImageID(gBallBasketImg),px,py) Else DrawingMode(#PB_2DDrawing_Default) Box(px,py,pw,ph,RGB(150,150,160)) EndIf DrawingFont(FontID(fontStats)) Protected restCount=drawSeqPos For i=1 To 20 : If fallingBalls(i)\active : restCount-1 : EndIf : Next If restCount<0 : restCount=0 : EndIf For i=1 To 20 slot=i : oy=py+ph-(slot-1)*slotH-slotH/2 If slot%2=1 : ox=leftX : Else : ox=rightX : EndIf If i<=restCount n=drawSequence(i-1) If IsAnyHit(n) DrawingMode(#PB_2DDrawing_Default) : FrontColor(RGB(40,180,60)) : Circle(ox,oy,r) DrawingMode(#PB_2DDrawing_AlphaBlend) : FrontColor(RGBA(255,255,255,90)): Circle(ox-r/3,oy-r/3,r/3) DrawingMode(#PB_2DDrawing_Transparent): FrontColor(RGB(255,255,255)) Else DrawingMode(#PB_2DDrawing_Default) : FrontColor(RGB(210,10,10)) : Circle(ox,oy,r) DrawingMode(#PB_2DDrawing_AlphaBlend) : FrontColor(RGBA(255,255,255,70)): Circle(ox-r/3,oy-r/3,r/3) DrawingMode(#PB_2DDrawing_Transparent): FrontColor(RGB(255,255,255)) EndIf ns=Str(n) : nw=TextWidth(ns) : nh=TextHeight(ns) DrawText(ox-nw/2,oy-nh/2,ns) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(65,65,65)) : Circle(ox,oy,r) If i>1 Protected prevOy=py+ph-(i-2)*slotH-slotH/2 Protected prevOx : If (i-1)%2=1 : prevOx=leftX : Else : prevOx=rightX : EndIf DrawingMode(#PB_2DDrawing_Default) FrontColor(RGB(45,45,58)) : LineXY(prevOx,prevOy,ox,oy) EndIf EndIf Next Protected fbIdx For fbIdx=1 To 20 If Not fallingBalls(fbIdx)\active : Continue : EndIf n=drawSequence(fallingBalls(fbIdx)\idx-1) Protected foy.i=Int(fallingBalls(fbIdx)\y)+py Protected fallRange.f=fallingBalls(fbIdx)\destY-baskH Protected fallDone.f=fallingBalls(fbIdx)\y-baskH Protected fProgress.f If fallRange>0 : fProgress=fallDone/fallRange : Else : fProgress=1.0 : EndIf If fProgress<0.0 : fProgress=0.0 : EndIf If fProgress>1.0 : fProgress=1.0 : EndIf Protected swingAmp=(fallingBalls(fbIdx)\destX+px)-centX Protected phase6=fallingBalls(fbIdx)\phase%6, phase6f.f=phase6-3 Protected wiggleX.i=Int(swingAmp*fProgress)+Int((1.0-fProgress)*swingAmp*0.3*phase6f/3.0) Protected fox=centX+wiggleX Protected t For t=3 To 1 Step -1 Protected trailY=foy-t*(slotH/3) If trailY>py+baskH And r-t>=1 DrawingMode(#PB_2DDrawing_AlphaBlend) FrontColor(RGBA(140,140,160,50)) : Circle(fox,trailY,r-t) EndIf Next If IsAnyHit(n) DrawingMode(#PB_2DDrawing_Default) : FrontColor(RGB(40,180,60)) : Circle(fox,foy,r) DrawingMode(#PB_2DDrawing_AlphaBlend) : FrontColor(RGBA(255,255,255,90)): Circle(fox-r/3,foy-r/3,r/3) DrawingMode(#PB_2DDrawing_Transparent): FrontColor(RGB(255,255,255)) Else DrawingMode(#PB_2DDrawing_Default) : FrontColor(RGB(210,10,10)) : Circle(fox,foy,r) DrawingMode(#PB_2DDrawing_AlphaBlend) : FrontColor(RGBA(255,255,255,70)): Circle(fox-r/3,foy-r/3,r/3) DrawingMode(#PB_2DDrawing_Transparent): FrontColor(RGB(255,255,255)) EndIf ns=Str(n) : nw=TextWidth(ns) : nh=TextHeight(ns) DrawText(fox-nw/2,foy-nh/2,ns) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(255,255,60)) : Circle(fox,foy,r) Next EndProcedure ; ==================================================================================== ; DRAW STATS PANEL ; Dynamic lh prevents text rows from overwriting each other at any DPI/font size. ; ==================================================================================== Procedure DrawStatsPanel_Screen() Protected pw=gStatsW, ph=gStatsH Protected sx=gStatsX, sy=gStatsY Protected mx=12, winStr.s Protected spots, hits, win, c DrawingMode(#PB_2DDrawing_Default) Box(sx,sy,pw,ph,RGB(192,192,192)) Box(sx,sy,pw,52,RGB(150,150,160)) ; Set font first so TextHeight() returns physical pixel height DrawingFont(FontID(fontStats)) Protected th = TextHeight("M") ; actual glyph height in px Protected fixedPx = 218 ; sum of all non-lh vertical elements Protected maxLh = (ph - fixedPx) / 18 ; max lh that fits all 18 text rows Protected lh = th + 2 ; preferred: glyph + 2px breathing room If lh > maxLh : lh = maxLh : EndIf If lh < th : lh = th : EndIf Protected ty=sy DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,255,220)) : DrawText(sx+mx,sy+7,"CARD STATS") FrontColor(RGB(200,220,255)) : DrawText(sx+mx,sy+27,"Bet/Card: $"+Str(betAmount)) ty=sy+58 FrontColor(RGB(30,30,30)) DrawText(sx+mx,ty,"Card") : DrawText(sx+mx+80,ty,"Picks") DrawText(sx+mx+152,ty,"Hits"): DrawText(sx+mx+200,ty,"Win") ty+lh For c=1 To #CARDS spots=cards(c)\selCount : hits=cards(c)\hitCount : win=cardWin(c) If c%2=0 DrawingMode(#PB_2DDrawing_Default) FrontColor(RGB(178,178,185)) : Box(sx,ty-2,pw,lh+2) DrawingMode(#PB_2DDrawing_Transparent) EndIf FrontColor(RGB(20,20,100)) : DrawText(sx+mx,ty,"Card "+Str(c)) FrontColor(RGB(40,40,40)) : DrawText(sx+mx+80,ty,Str(spots)+"/12") If hits>0 : FrontColor(RGB(0,100,0)) : Else : FrontColor(RGB(60,60,60)) : EndIf DrawText(sx+mx+152,ty,Str(hits)) If win>0 FrontColor(RGB(160,0,0)) : winStr="$"+Str(win) Else FrontColor(RGB(110,110,110)) : winStr="-" EndIf DrawText(sx+mx+200,ty,winStr) ty+lh Next ty+6 DrawingMode(#PB_2DDrawing_Default) Box(sx,ty,pw,50,RGB(150,150,160)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,255,220)) : DrawText(sx+mx,ty+6,"THIS SET WIN/LOSS") FrontColor(RGB(200,220,255)) : DrawText(sx+mx,ty+27,"(per card)") ty+54 Protected setC, netStr.s For setC=1 To #CARDS If setC%2=0 DrawingMode(#PB_2DDrawing_Default) FrontColor(RGB(178,178,185)) : Box(sx,ty-2,pw,lh+2) DrawingMode(#PB_2DDrawing_Transparent) EndIf FrontColor(RGB(20,20,100)) : DrawText(sx+mx,ty,"Card "+Str(setC)+":") If cardSetNet(setC)>0 FrontColor(RGB(20,100,20)) : netStr="+$"+Str(cardSetNet(setC)) ElseIf cardSetNet(setC)<0 FrontColor(RGB(150,0,0)) : netStr="-$"+Str(-cardSetNet(setC)) Else FrontColor(RGB(80,80,80)) : netStr="$0" EndIf DrawText(sx+mx+70,ty,netStr) Protected wlStr.s=Str(cardSetHits(setC))+"W " Protected wlX.i=sx+pw-mx-TextWidth(wlStr+Str(cardSetLosses(setC))+"L")-4 If wlX0 : FrontColor(RGB(0,100,0)) ElseIf gSetProfit<0 : FrontColor(RGB(160,0,0)) Else : FrontColor(RGB(20,20,20)) EndIf DrawText(sx+mx,ty,"Profit:") If gSetProfit>0 : DrawText(sx+mx+80,ty,"+$"+Str(gSetProfit)) ElseIf gSetProfit<0 : DrawText(sx+mx+80,ty,"-$"+Str(-gSetProfit)) Else : DrawText(sx+mx+80,ty,"$0") EndIf ty+lh FrontColor(RGB(20,20,80)) : DrawText(sx+mx,ty,"Runs:") : DrawText(sx+mx+80,ty,Str(runCount)) ty+lh+6 DrawingMode(#PB_2DDrawing_Default) Box(sx,ty,pw,34,RGB(150,150,160)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,255,220)) : DrawText(sx+mx,ty+9,"BALANCE") ty+38 FrontColor(RGB(20,80,20)) : DrawText(sx+mx,ty,"$"+Str(balance)) ty+lh+6 DrawingMode(#PB_2DDrawing_Default) Box(sx,ty,pw,34,RGB(150,150,160)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,255,220)) : DrawText(sx+mx,ty+9,"LIFETIME") ty+38 FrontColor(RGB(0,100,0)) : DrawText(sx+mx,ty,"GEN W:") FrontColor(RGB(20,80,20)) : DrawText(sx+mx+90,ty,"$"+Str(totalWinsGen)) ty+lh FrontColor(RGB(160,0,0)) : DrawText(sx+mx,ty,"GEN L:") DrawText(sx+mx+90,ty,"$"+Str(totalLossesGen)) ty+lh+4 FrontColor(RGB(0,100,0)) : DrawText(sx+mx,ty,"CAS W:") FrontColor(RGB(20,80,20)) : DrawText(sx+mx+90,ty,"$"+Str(totalWinsCas)) ty+lh FrontColor(RGB(160,0,0)) : DrawText(sx+mx,ty,"CAS L:") DrawText(sx+mx+90,ty,"$"+Str(totalLossesCas)) ty+lh If gShowProgressive ty+6 Protected progH=lh*2+10 DrawingMode(#PB_2DDrawing_Default) Box(sx,ty,pw,progH,RGB(40,30,10)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(CGold()) DrawText(sx+mx, ty+4, "PROGRESSIVE JACKPOT") DrawText(sx+mx, ty+lh+6, "$"+Str(progressiveJackpot)) EndIf If gConsolationMsg<>"" ty+8 Protected consH=lh*2+8 DrawingMode(#PB_2DDrawing_Default) FrontColor(RGB(70,44,0)) : Box(sx,ty,pw,consH) FrontColor(CGold()) : Box(sx,ty,pw,2) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(CGold()) : DrawText(sx+mx, ty+4, "CONSOLATION") FrontColor(RGB(255,235,160)) : DrawText(sx+mx, ty+lh+6, gConsolationMsg) EndIf EndProcedure ; ==================================================================================== ; DRAW BUTTONS ; ==================================================================================== Procedure DrawDrawButton_Screen() Protected x=gRDraw\x, y=gRDraw\y, w=gRDraw\w, h=gRDraw\h Protected lbl.s, lbl2.s, bg, hiC, shC, tw, th If gAutoRun bg=RGB(180,35,35) : hiC=RGB(230,90,90) : shC=RGB(100,10,10) lbl="STOP AUTO" : lbl2="(click to stop)" ElseIf gDrawing bg=RGB(70,70,90) : hiC=RGB(110,110,130) : shC=RGB(30,30,45) lbl="Drawing..." : lbl2="" ElseIf gDrawBtnHeld bg=RGB(30,55,130) : hiC=RGB(50,80,160) : shC=RGB(10,25,70) lbl="Draw Numbers" : lbl2="Hold 1s = AUTO" Else bg=RGB(40,80,190) : hiC=RGB(80,130,240) : shC=RGB(15,40,110) lbl="Draw Numbers" : lbl2="Hold 1s = AUTO" EndIf DrawBtnBase(x,y,w,h,bg,hiC,shC) DrawingFont(FontID(fontStats)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,255,255)) tw=TextWidth(lbl) : DrawText(x+(w-tw)/2, y+h/2-TextHeight(lbl)-1, lbl) If lbl2<>"" FrontColor(RGB(180,210,255)) tw=TextWidth(lbl2) : DrawText(x+(w-tw)/2, y+h/2+1, lbl2) EndIf EndProcedure Procedure DrawNewButton_Screen() Protected x=gRNew\x, y=gRNew\y, w=gRNew\w, h=gRNew\h DrawBtnBase(x,y,w,h,RGB(50,110,50),RGB(90,170,90),RGB(20,55,20)) DrawingFont(FontID(fontStats)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(220,255,220)) Protected lbl.s="New Game", tw=TextWidth(lbl) DrawText(x+(w-tw)/2, y+h/2-TextHeight(lbl)-2, lbl) FrontColor(RGB(180,230,180)) lbl="(Clears Cards)" : tw=TextWidth(lbl) DrawText(x+(w-tw)/2, y+h/2+4, lbl) EndProcedure Procedure DrawPayButton_Screen() Protected x=gRPay\x, y=gRPay\y, w=gRPay\w, h=gRPay\h DrawBtnBase(x,y,w,h,RGB(100,70,0),RGB(175,135,30),RGB(45,28,0)) DrawingFont(FontID(fontStats)) DrawingMode(#PB_2DDrawing_Transparent) Protected th=TextHeight("A"), gap=(h-th*3)/4 FrontColor(RGB(255,225,120)) Protected lbl.s="Payout Table", tw=TextWidth(lbl) DrawText(x+(w-tw)/2, y+gap, lbl) Protected modeStr.s, modeCol.i If gPayoutMode=0 : modeStr="GENEROUS" : modeCol=RGB(120,255,120) Else : modeStr="CASINO" : modeCol=RGB(255,120,120) EndIf FrontColor(modeCol) : tw=TextWidth(modeStr) : DrawText(x+(w-tw)/2, y+gap*2+th, modeStr) FrontColor(RGB(200,175,80)) lbl="right-click: swap" : tw=TextWidth(lbl) : DrawText(x+(w-tw)/2, y+gap*3+th*2, lbl) EndProcedure Procedure DrawSpeedControl_Screen() Protected x=gRSpeed\x, y=gRSpeed\y, w=gRSpeed\w, h=gRSpeed\h DrawBtnBase(x,y,w,h,RGB(45,45,75),RGB(85,85,130),RGB(15,15,30)) DrawingFont(FontID(fontStats)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(220,230,255)) Protected lbl.s="Speed", tw=TextWidth(lbl) DrawText(x+(w-tw)/2, y+6, lbl) FrontColor(RGB(180,180,220)) DrawText(x+8, y+6, "<") : DrawText(x+w-16, y+6, ">") Protected barH=14, barY=y+(h-barH)/2+2, segW=(w-16)/5, segX, i For i=0 To 4 segX=x+8+i*segW DrawingMode(#PB_2DDrawing_Default) If i") Protected barH=14, barY=y+(h-barH)/2+2, segW=(w-16)/10, segX, i For i=0 To 9 segX=x+8+i*segW DrawingMode(#PB_2DDrawing_Default) If i=8 : FrontColor(RGB(255,80,80)) ElseIf i>=6 : FrontColor(RGB(255,140,50)) ElseIf i>=3 : FrontColor(RGB(255,220,80)) Else : FrontColor(RGB(120,220,120)) EndIf Else FrontColor(RGB(50,35,15)) EndIf Box(segX+1,barY,segW-2,barH) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(25,18,5)) : Box(segX+1,barY,segW-2,barH) Next DrawingMode(#PB_2DDrawing_Transparent) Protected vlbl.s="$"+Str(gBetVal)+" per Card", tw2=TextWidth(vlbl) FrontColor(RGB(255,240,200)) ;DrawText(x+(w-tw2)/2, y+h-27, vlbl) DrawText(x+(w-tw2)/2, y+h-TextHeight("A")-6, vlbl) EndProcedure Procedure DrawSoundButton_Screen() Protected x=gRSound\x, y=gRSound\y, w=gRSound\w, h=gRSound\h Protected bg, hiC, shC If gSoundOn : bg=RGB(35,95,35) : hiC=RGB(80,170,80) : shC=RGB(10,50,10) Else : bg=RGB(95,35,35) : hiC=RGB(170,80,80) : shC=RGB(50,10,10) EndIf DrawBtnBase(x,y,w,h,bg,hiC,shC) DrawingFont(FontID(fontStats)) Protected th=TextHeight("A"), iconR=10 Protected gap=(h-th*2-iconR*2)/4 Protected titleY=y+gap Protected iconY=y+gap*2+th+iconR Protected lblY=y+gap*3+th+iconR*2 DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,255,255)) Protected tw=TextWidth("Sound") : DrawText(x+(w-tw)/2, titleY, "Sound") DrawingMode(#PB_2DDrawing_Default) If gSoundOn FrontColor(RGB(255,255,100)) : Circle(x+w/2,iconY,iconR) FrontColor(RGB(255,255,255)) : Circle(x+w/2,iconY,5) Else FrontColor(RGB(180,60,60)) : Circle(x+w/2,iconY,iconR) DrawingMode(#PB_2DDrawing_Outlined) FrontColor(RGB(255,255,255)) : LineXY(x+w/2-iconR,iconY-6,x+w/2+iconR,iconY+8) EndIf DrawingMode(#PB_2DDrawing_Transparent) If gSoundOn : FrontColor(RGB(220,255,220)) : tw=TextWidth("ON") : DrawText(x+(w-tw)/2, lblY, "ON") Else : FrontColor(RGB(255,220,220)) : tw=TextWidth("MUTED") : DrawText(x+(w-tw)/2, lblY, "MUTED") EndIf EndProcedure Procedure DrawAutoPickButton_Screen() Protected x=gRAutoPick\x, y=gRAutoPick\y, w=gRAutoPick\w, h=gRAutoPick\h Protected bg, hiC, shC, spots.i If gCurrentTab>=1 And gCurrentTab<=#CARDS bg=RGB(80,50,0) : hiC=RGB(180,130,30) : shC=RGB(35,20,0) spots=cards(gCurrentTab)\selCount : If spots=0 : spots=8 : EndIf Else bg=RGB(50,50,50) : hiC=RGB(85,85,85) : shC=RGB(22,22,22) spots=0 EndIf DrawBtnBase(x,y,w,h,bg,hiC,shC) DrawingFont(FontID(fontStats)) DrawingMode(#PB_2DDrawing_Transparent) Protected th=TextHeight("A"), gap=(h-th*3)/4 Protected ty=y+gap Protected t1.s, tw.i If gCurrentTab>=1 And gCurrentTab<=#CARDS t1="Pick: Card "+Str(gCurrentTab)+" ("+Str(spots)+")" Else t1="Auto-Pick" EndIf FrontColor(RGB(255,215,0)) tw=TextWidth(t1) : DrawText(x+(w-tw)/2, ty, t1) FrontColor(RGB(255,200,80)) Protected t2.s="L-Click: Hot #s" tw=TextWidth(t2) : DrawText(x+(w-tw)/2, ty+gap+th, t2) FrontColor(RGB(180,200,255)) Protected t3.s="R-Click: Rndm #s" tw=TextWidth(t3) : DrawText(x+(w-tw)/2, ty+gap*2+th*2, t3) EndProcedure Procedure DrawAboutButton_Screen() Protected x=gRAbout\x, y=gRAbout\y, w=gRAbout\w, h=gRAbout\h DrawBtnBase(x,y,w,h,RGB(60,40,90),RGB(120,90,180),RGB(25,15,50)) DrawingFont(FontID(fontStats)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(230,210,255)) Protected lbl.s="About", lbl2.s="& Rules" Protected th=TextHeight("A") Protected tw=TextWidth(lbl) : DrawText(x+(w-tw)/2, y+h/2-th-2, lbl) tw=TextWidth(lbl2) : DrawText(x+(w-tw)/2, y+h/2+4, lbl2) EndProcedure ; ==================================================================================== ; DRAW PAYOUT TABLE OVERLAY ; ==================================================================================== Procedure DrawPayTable_Screen() If Not gPayTableVisible : ProcedureReturn : EndIf Protected ox=gRGrid\x, oy=gRGrid\y, ow=gRGrid\w, oh=gRGrid\h Protected lh=(oh-26)/14 : If lh<16:lh=16:EndIf : If lh>38:lh=38:EndIf Protected headerH=lh, subtitleH=lh, footerH=26 Protected fontSize=(lh*32)/100 : If fontSize<9:fontSize=9:EndIf : If fontSize>18:fontSize=18:EndIf Protected rs=fontSize*5 : If rs<50:rs=50:EndIf Protected col=(ow-rs)/12 : If col<40:col=40:EndIf DrawingMode(#PB_2DDrawing_Default) Box(ox,oy,ow,oh,RGB(18,18,32)) FrontColor(RGB(255,215,0)) Box(ox,oy,ow,2):Box(ox,oy+oh-2,ow,2):Box(ox,oy,2,oh):Box(ox+ow-2,oy,2,oh) Box(ox,oy,ow,headerH,RGB(40,40,65)) DrawingFont(FontID(fontOverlay)) Protected textH=TextHeight("A") DrawingMode(#PB_2DDrawing_Transparent) Protected hty=(headerH-textH)/2 FrontColor(RGB(255,215,0)) DrawText(ox+(rs-TextWidth("Picks"))/2, oy+hty, "Picks") Protected hits,tx,spots,pay,rty,payStr.s For hits=1 To 12 tx=ox+rs+(hits-1)*col FrontColor(RGB(160,195,255)) Protected hlbl.s=Str(hits)+" hit" DrawText(tx+(col-TextWidth(hlbl))/2, oy+hty, hlbl) Next DrawingMode(#PB_2DDrawing_Default) Box(ox,oy+headerH,ow,subtitleH,RGB(30,30,52)) DrawingMode(#PB_2DDrawing_Transparent) Protected sty=oy+headerH+(subtitleH-textH)/2 FrontColor(RGB(255,215,0)) DrawText(ox+rs, sty, "Bet/Card: $"+Str(betAmount)) Protected modeStr.s, modeCol.i If gPayoutMode=0 : modeStr="GENEROUS payouts" : modeCol=RGB(120,255,120) Else : modeStr="CASINO payouts" : modeCol=RGB(255,120,120) EndIf FrontColor(modeCol) : DrawText(ox+ow-TextWidth(modeStr)-10, sty, modeStr) For spots=1 To 12 Protected sty2=oy+headerH+subtitleH+(spots-1)*lh DrawingMode(#PB_2DDrawing_Default) If spots%2=0 : FrontColor(RGB(26,26,44)) : Else : FrontColor(RGB(20,20,36)) : EndIf Box(ox,sty2,ow,lh) FrontColor(RGB(70,70,100)) : Box(ox,sty2,3,lh) rty=sty2+(lh-textH)/2 DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,215,0)) Protected snlbl.s=Str(spots) DrawText(ox+(rs-TextWidth(snlbl))/2, rty, snlbl) For hits=1 To spots pay=payTable(spots,hits)*betAmount tx=ox+rs+(hits-1)*col If pay>0 If pay>=1000000 : payStr="$"+Str(pay/1000000)+"M" ElseIf pay>=10000 : payStr="$"+Str(pay/1000)+"K" ElseIf pay>=1000 : payStr="$"+Str(pay/1000)+"K" Else : payStr="$"+Str(pay) EndIf If pay>=100000 : FrontColor(RGB(255,80,80)) ElseIf pay>=10000 : FrontColor(RGB(255,140,50)) ElseIf pay>=1000 : FrontColor(RGB(255,210,60)) ElseIf pay>=100 : FrontColor(RGB(100,220,100)) Else : FrontColor(RGB(170,170,210)) EndIf Else FrontColor(RGB(55,55,75)) : payStr="-" EndIf DrawText(tx+(col-TextWidth(payStr))/2, rty, payStr) Next Next DrawingMode(#PB_2DDrawing_Default) Box(ox,oy+oh-footerH,ow,footerH,RGB(40,40,65)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(140,155,175)) DrawText(ox+8, oy+oh-footerH+(footerH-textH)/2, "K=thousands M=millions. Click anywhere to close.") EndProcedure ; ==================================================================================== ; DRAW ABOUT OVERLAY ; ==================================================================================== Procedure DrawAbout_Screen() If Not gAboutVisible : ProcedureReturn : EndIf Protected ox=gRGrid\x, oy=gRGrid\y, ow=gRGrid\w, oh=gRGrid\h Protected mx=18, ty=oy Protected headerH=52, footerH=28 ; 33 text lines + fixed overhead: header(52)+footer(28)+ty-start(6)+intro-gap(6)+8 section-gaps*4(32) = 124 Protected lh=(oh-124)/33 If lh<13 : lh=13 : EndIf If lh>24 : lh=24 : EndIf ; Background and border DrawingMode(#PB_2DDrawing_Default) Box(ox,oy,ow,oh,RGB(22,22,45)) FrontColor(RGB(180,140,230)) Box(ox,oy,ow,2) : Box(ox,oy+oh-2,ow,2) : Box(ox,oy,2,oh) : Box(ox+ow-2,oy,2,oh) ; Header Box(ox,oy,ow,headerH,RGB(50,30,85)) DrawingFont(FontID(fontOverlayBold)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(255,220,140)) DrawText(ox+mx, oy+7, "4 Card Keno Deconstructed by PureBasic 6.40") DrawText(ox+mx, oy+30, "About & Rules") DrawingFont(FontID(fontOverlay)) ty=oy+headerH+6 ; Intro FrontColor(RGB(255,255,200)) DrawText(ox+mx,ty,"4 Card Keno Deconstructed by PureBasic — fully standalone, no external dependencies.") : ty+lh+6 ; HOW TO PLAY FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"HOW TO PLAY") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Pick 1-12 numbers on up to 4 cards. Main tab shows all cards combined.") : ty+lh DrawText(ox+mx,ty,"- Set Bet/Card ($1-$10) and Speed. Click Draw Numbers — 20 balls drawn each round.") : ty+lh DrawText(ox+mx,ty,"- Press SPACE to draw. Hold SPACE or Draw button 1 second to auto-run. Press again to stop.") : ty+lh ty+4 ; CELL COLORS FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"CELL COLORS") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Blue=idle, Purple=your pick, Red=drawn, Green=HIT. Yellow X marks every hit cell.") : ty+lh DrawText(ox+mx,ty,"- Main tab: white asterisk = number on 2+ cards. Black asterisk = that number was also drawn.") : ty+lh DrawText(ox+mx,ty,"- After a draw you may click any cell on any card to add or remove picks.") : ty+lh ty+4 ; AUTO-PICK & HEAT MAP FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"AUTO-PICK & HEAT MAP") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- L-Click Auto-Pick: fills active card with hottest numbers from draw history.") : ty+lh DrawText(ox+mx,ty,"- R-Click Auto-Pick: fills active card with random numbers (same spot count).") : ty+lh DrawText(ox+mx,ty,"- Heat Map: right-click Main tab to toggle. Blue=cold, orange=hot. Dot on tab = active.") : ty+lh ty+4 ; COPY CARD FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"COPY CARD") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Right-click any Card 1-4 tab to copy its picks to another card.") : ty+lh ty+4 ; WINS, JACKPOTS & PAYOUTS FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"WINS, JACKPOTS & PAYOUTS") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Payout Table: click to view. Right-click button to toggle GENEROUS / CASINO modes.") : ty+lh DrawText(ox+mx,ty,"- Lifetime stats: GEN W/L for Generous mode, CAS W/L for Casino mode.") : ty+lh DrawText(ox+mx,ty,"- Jackpot: all picks on a card drawn — auto-run stops, pop-up shown. 5+ spots = fanfare.") : ty+lh DrawText(ox+mx,ty,"- Progressive: 10-12 spot full match wins the growing pot ($25K seed, +$2/qualifying card).") : ty+lh DrawText(ox+mx,ty,"- Consolation: all cards 7+ picks, 15+ distinct numbers, zero hits = $1,000 free.") : ty+lh ty+4 ; CONTROLS FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"CONTROLS") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Speed 1-5: Slowest to Fastest. At Fast & Fastest, no pause between auto-run games.") : ty+lh DrawText(ox+mx,ty,"- Bet/Card $1-$10: click left half = decrease, right half = increase.") : ty+lh DrawText(ox+mx,ty,"- Sound toggles audio. ESC saves and exits. SPACE = draw (hold 1s = auto-run).") : ty+lh ty+4 ; NEW GAME FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"NEW GAME") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- YES = clear picks only. NO = full reset to $1,000, all stats wiped. CANCEL = Cancel.") : ty+lh ty+4 ; BALANCE & STATS FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"BALANCE & STATS") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Start with $1,000. If balance drops below draw cost, add $1,000 or save-and-quit.") : ty+lh DrawText(ox+mx,ty,"- Stats panel: per-card picks/hits/win, set net W/L, balance, runs, lifetime totals.") : ty+lh ty+4 ; PERSISTENCE FrontColor(RGB(160,220,255)) : DrawText(ox+mx,ty,"PERSISTENCE") : ty+lh FrontColor(RGB(220,220,230)) DrawText(ox+mx,ty,"- Saves to keno_save.dat: picks, balance, stats, speed, bet, sound, progressive pot.") : ty+lh DrawText(ox+mx,ty,"- Auto-saved after each draw (3-second debounce) and on exit, jackpot, or settings change.") : ty+lh ; Footer DrawingMode(#PB_2DDrawing_Default) Box(ox,oy+oh-footerH,ow,footerH,RGB(50,30,85)) DrawingMode(#PB_2DDrawing_Transparent) FrontColor(RGB(200,180,240)) DrawText(ox+mx, oy+oh-footerH+(footerH-TextHeight("A"))/2, "Click anywhere to close.") EndProcedure ; ==================================================================================== ; RENDER FRAME ; ==================================================================================== Procedure RenderFrame() ClearScreen(CBg()) If StartDrawing(ScreenOutput()) DrawingMode(#PB_2DDrawing_Default) Box(0,0,gWinW,#TopBarH,CTopBar()) DrawDrawButton_Screen() DrawNewButton_Screen() DrawPayButton_Screen() DrawSpeedControl_Screen() DrawBetControl_Screen() DrawSoundButton_Screen() DrawAutoPickButton_Screen() DrawAboutButton_Screen() DrawStatsPanel_Screen() DrawTabBar_Screen() If gCurrentTab=0 DrawGrid_Screen(gRGrid\x, gRGrid\y, gRGrid\w, gRGrid\h, 0, 0) Else DrawGrid_Screen(gRGrid\x, gRGrid\y, gRGrid\w, gRGrid\h, 1, gCurrentTab) EndIf DrawBallCanvas_Screen() DrawPayTable_Screen() DrawAbout_Screen() ; Fill bottom margin strip to prevent black bleed-through FrontColor(CBg()) : Box(0, gWinH-#Margin, gWinW, #Margin) StopDrawing() EndIf FlipBuffers() EndProcedure ; ==================================================================================== ; INPUT HIT-TEST HELPERS ; ==================================================================================== Procedure.i InRect(rx.i,ry.i,rw.i,rh.i, mx.i,my.i) If mx>=rx And mx=ry And my4:t=4:EndIf ProcedureReturn t EndProcedure Procedure HandleLeftClick(mx.i, my.i) If gPayTableVisible And InRect(gRGrid\x,gRGrid\y,gRGrid\w,gRGrid\h,mx,my) gPayTableVisible=0 : ProcedureReturn EndIf If gAboutVisible And InRect(gRGrid\x,gRGrid\y,gRGrid\w,gRGrid\h,mx,my) gAboutVisible=0 : ProcedureReturn EndIf If gPayTableVisible Or gAboutVisible : gPayTableVisible=0 : gAboutVisible=0 : ProcedureReturn : EndIf If InRect(gRDraw\x,gRDraw\y,gRDraw\w,gRDraw\h, mx,my) CloseOverlayIfOpen() If gAutoRun gAutoRun=0 : gPauseActive=0 ElseIf Not gDrawing DoDraw() EndIf ProcedureReturn EndIf If InRect(gRNew\x,gRNew\y,gRNew\w,gRNew\h, mx,my) Protected choice2=SafeMessageRequester("New Game", "YES - Clear card selections only."+Chr(10)+" (Balance and lifetime stats kept.)"+Chr(10)+Chr(10)+ "NO - Full wipe: reset balance to $1000, clear all stats."+Chr(10)+Chr(10)+ "CANCEL - Return to game, change nothing.", #PB_MessageRequester_YesNoCancel) Select choice2 Case #PB_MessageRequester_Yes gAutoRun=0 : gPauseActive=0 : ResetAll() : SaveGame() ShowTab(1) Case #PB_MessageRequester_No gAutoRun=0 : gPauseActive=0 If FileSize(saveFile)>0 : DeleteFile(saveFile) : EndIf balance=1000 : totalWinsGen=0 : totalLossesGen=0 totalWinsCas=0 : totalLossesCas=0 : betAmount=1 : gBetVal=1 gPayoutMode=0 : progressiveJackpot=progressiveSeed : gSpeedVal=3 ResetAll() : Dim drawnCount.i(#NUMS) ApplyPayTable() : UpdateProgressiveFlag() : ShowTab(1) EndSelect ProcedureReturn EndIf If InRect(gRPay\x,gRPay\y,gRPay\w,gRPay\h, mx,my) gAboutVisible=0 gAutoRun=0 : gPauseActive=0 gPayTableVisible=1-gPayTableVisible ProcedureReturn EndIf If InRect(gRSpeed\x,gRSpeed\y,gRSpeed\w,gRSpeed\h, mx,my) CloseOverlayIfOpen() If mx1 : gSpeedVal-1 : EndIf Else : If gSpeedVal<5 : gSpeedVal+1 : EndIf EndIf ProcedureReturn EndIf If InRect(gRBet\x,gRBet\y,gRBet\w,gRBet\h, mx,my) CloseOverlayIfOpen() If mx1 : gBetVal-1 : EndIf Else : If gBetVal<10 : gBetVal+1 : EndIf EndIf betAmount=gBetVal : ProcedureReturn EndIf If InRect(gRSound\x,gRSound\y,gRSound\w,gRSound\h, mx,my) CloseOverlayIfOpen() : gSoundOn=1-gSoundOn : ProcedureReturn EndIf If InRect(gRAutoPick\x,gRAutoPick\y,gRAutoPick\w,gRAutoPick\h, mx,my) CloseOverlayIfOpen() gAutoRun=0 : gPauseActive=0 DoAutoPick() : ProcedureReturn EndIf If InRect(gRAbout\x,gRAbout\y,gRAbout\w,gRAbout\h, mx,my) gPayTableVisible=0 gAutoRun=0 : gPauseActive=0 gAboutVisible=1-gAboutVisible : ProcedureReturn EndIf If InRect(gRTabBar\x,gRTabBar\y,gRTabBar\w,gRTabBar\h, mx,my) CloseOverlayIfOpen() ShowTab(TabFromX(mx)) : ProcedureReturn EndIf If gCurrentTab>=1 And gCurrentTab<=#CARDS If InRect(gRGrid\x,gRGrid\y,gRGrid\w,gRGrid\h, mx,my) Protected localX=mx-gRGrid\x, localY=my-gRGrid\y Protected num=CellNumberFromXY(localX,localY,gRGrid\w,gRGrid\h) If num ToggleSelection(gCurrentTab,num) gSaveDirty=1 EndIf EndIf EndIf EndProcedure Procedure HandleRightClick(mx.i, my.i) If InRect(gRPay\x,gRPay\y,gRPay\w,gRPay\h, mx,my) gPayoutMode=1-gPayoutMode ApplyPayTable() RequestSave() ProcedureReturn EndIf If InRect(gRAutoPick\x,gRAutoPick\y,gRAutoPick\w,gRAutoPick\h, mx,my) CloseOverlayIfOpen() gAutoRun=0 : gPauseActive=0 DoAutoPickRandom() : ProcedureReturn EndIf If gCurrentTab=0 And InRect(gRGrid\x,gRGrid\y,gRGrid\w,gRGrid\h, mx,my) gHeatMapOn=1-gHeatMapOn : ProcedureReturn EndIf If InRect(gRTabBar\x,gRTabBar\y,gRTabBar\w,gRTabBar\h, mx,my) Protected srcTab=TabFromX(mx) If srcTab>=1 And srcTab<=#CARDS Protected destStr.s=SafeInputRequester("Copy Card "+Str(srcTab), "Copy Card "+Str(srcTab)+"'s picks to which card? (1-4, not "+Str(srcTab)+")", "") Protected destTab=Val(destStr) If destTab>=1 And destTab<=#CARDS And destTab<>srcTab Protected cn ResetCard(destTab) For cn=1 To #NUMS If cards(srcTab)\selected[cn] cards(destTab)\selected[cn]=1 : cards(destTab)\selCount+1 EndIf Next UpdateDupCounts() : UpdateProgressiveFlag() ShowTab(destTab) : gSaveDirty=1 ElseIf destStr<>"" And (destTab<1 Or destTab>#CARDS Or destTab=srcTab) SafeMessageRequester("Copy Card","Invalid destination.",#PB_MessageRequester_Ok) EndIf EndIf EndIf EndProcedure ; ==================================================================================== ; SPACEBAR HOLD DETECTOR ; Cross-platform: C statics on C backend, PureBasic Static on ASM backend. ; Returns via gSpaceResult: 0=nothing, 1=tap (draw once), 2=held 1s (auto-run) ; ==================================================================================== Procedure SpaceKeyTick(keyDown.i, nowMs.i) CompilerIf #PB_Compiler_Backend = #PB_Backend_C !static int sk_held=0, sk_fired=0, sk_startMs=0; !int kd=(int)p_keydown, now=(int)p_nowms; !v_gSpaceResult=0; !if(kd){if(!sk_held){sk_held=1;sk_startMs=now;sk_fired=0;} !else if(!sk_fired&&(now-sk_startMs)>=1000){sk_fired=1;v_gSpaceResult=2;}} !else{if(sk_held&&!sk_fired)v_gSpaceResult=1;sk_held=0;sk_fired=0;} CompilerElse ; ASM backend fallback Static sk_held.i=0, sk_fired.i=0, sk_startMs.i=0 gSpaceResult=0 If keyDown If Not sk_held sk_held=1 : sk_startMs=nowMs : sk_fired=0 ElseIf Not sk_fired And (nowMs-sk_startMs)>=1000 sk_fired=1 : gSpaceResult=2 EndIf Else If sk_held And Not sk_fired : gSpaceResult=1 : EndIf sk_held=0 : sk_fired=0 EndIf CompilerEndIf EndProcedure ; ==================================================================================== ; HANDLE INPUT ; Mouse position via WindowMouseX/Y (no InitMouse needed, no screen capture). ; Button state via GetAsyncKeyState_ on Windows (CompilerIf keeps cross-platform). ; ==================================================================================== Procedure HandleInput() Protected mx = WindowMouseX(#winMain) Protected my = WindowMouseY(#winMain) Protected leftBtn.i = 0 Protected rightBtn.i = 0 CompilerIf #PB_OS_Windows If GetAsyncKeyState_(1) & $8000 : leftBtn = 1 : EndIf If GetAsyncKeyState_(2) & $8000 : rightBtn = 1 : EndIf CompilerElse ExamineMouse() leftBtn = MouseButton(#PB_MouseButton_Left) rightBtn = MouseButton(#PB_MouseButton_Right) CompilerEndIf ; Left click (transition: up->down) If leftBtn And Not gPrevLeft If InRect(gRDraw\x,gRDraw\y,gRDraw\w,gRDraw\h,mx,my) And Not gDrawing And Not gAutoRun gDrawBtnHeld=1 : gDrawBtnMs=ElapsedMilliseconds() : gHoldDetected=0 Else gDrawBtnHeld=0 HandleLeftClick(mx,my) EndIf EndIf ; Draw button hold: 1 second triggers auto-run If gDrawBtnHeld And leftBtn If ElapsedMilliseconds()-gDrawBtnMs>=1000 And Not gHoldDetected gHoldDetected=1 : gDrawBtnHeld=0 If Not gAutoRun gAutoRun=1 If gSoundOn : PlayBeep(1200,60) : EndIf If Not gDrawing : DoDraw() : EndIf EndIf EndIf EndIf ; Draw button released without reaching hold threshold If Not leftBtn And gPrevLeft And gDrawBtnHeld gDrawBtnHeld=0 If Not gHoldDetected If gAutoRun : gAutoRun=0 : gPauseActive=0 ElseIf Not gDrawing : CloseOverlayIfOpen() : DoDraw() EndIf EndIf gHoldDetected=0 EndIf ; Right click (transition: up->down) If rightBtn And Not gPrevRight HandleRightClick(mx,my) EndIf gPrevLeft=leftBtn : gPrevRight=rightBtn ; Keyboard ExamineKeyboard() ; Spacebar: tap = single draw, hold 1s = auto-run (mirrors draw button exactly) SpaceKeyTick(KeyboardPushed(#PB_Key_Space), ElapsedMilliseconds()) Select gSpaceResult Case 1 ; released before 1 second = single draw CloseOverlayIfOpen() If gAutoRun gAutoRun=0 : gPauseActive=0 ElseIf Not gDrawing DoDraw() EndIf Case 2 ; held >= 1 second = start auto-run CloseOverlayIfOpen() If Not gAutoRun gAutoRun=1 If gSoundOn : PlayBeep(1200,60) : EndIf If Not gDrawing : DoDraw() : EndIf EndIf EndSelect ; ESC = save and quit If KeyboardReleased(#PB_Key_Escape) If gSaveDirty Or gSaveNeeded : SaveGame() : EndIf SaveGame() gQuit=1 EndIf EndProcedure ; ==================================================================================== ; BUILD GUI ; ==================================================================================== Procedure BuildGUI() ; Open at minimum window bounds + 2px per dimension ; Keeps initial size in sync with WindowBounds minimum — never starts clamped Protected scale.f = GetDpiScale() Protected minW.i = Int(1000 * scale) Protected minH.i = Int(700 * scale) Protected finalW.i = minW + 2 Protected finalH.i = minH + 52 ; Open window directly at the correct scaled size (avoids resize rounding issues) OpenWindow(#winMain, 0, 0, finalW, finalH, "4 Card Keno Deconstructed by PureBasic 6.40", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered) WindowBounds(#winMain, minW, finalH, #PB_Default, #PB_Default) CompilerIf #PB_OS_Windows Protected r.RECT_ALT GetClientRect_(WindowID(#winMain), @r) gWinW = r\right - r\left gWinH = r\bottom - r\top CompilerElse gWinW = WindowWidth(#winMain) gWinH = WindowHeight(#winMain) CompilerEndIf If Not InitSprite() MessageRequester("Error", "InitSprite() failed - cannot open screen.", 0) : End EndIf If Not InitKeyboard() MessageRequester("Error", "InitKeyboard() failed.", 0) : End EndIf CompilerIf Not #PB_OS_Windows If Not InitMouse() MessageRequester("Error", "InitMouse() failed.", 0) : End EndIf CompilerEndIf If Not OpenWindowedScreen(WindowID(#winMain), 0, 0, gWinW, gWinH, #False, 0, 0, #PB_Screen_WaitSynchronization) MessageRequester("Error", "OpenWindowedScreen() failed.", 0) : End EndIf SetWindowColor(#winMain, CBg()) ; === Rest of your code (fonts, sounds, etc.) === RebuildFonts() InitSounds() saveFile = GetPathPart(ProgramFilename()) + "keno_save.dat" If OpenCryptRandom() : gCryptOK = 1 : EndIf InitPayTable() ResetAll() LoadGame() ApplyPayTable() UpdateDupCounts() UpdateProgressiveFlag() gBetVal = betAmount : If gBetVal < 1 Or gBetVal > 10 : gBetVal = 1 : EndIf UpdateLayout() RebuildBallBasket(gBallW, gBallH) RenderFrame() ShowTab(1) Protected anyLoaded = 0, i For i = 1 To #CARDS If cards(i)\selCount > 0 : anyLoaded = 1 : Break : EndIf Next If anyLoaded : ShowTab(0) : EndIf If anyLoaded = 0 SafeMessageRequester("Welcome to Keno!", "Pick 1-12 numbers on one or more cards, then click Draw Numbers." + Chr(10) + Chr(10) + " - 20 balls are drawn each round." + Chr(10) + " - Each active card costs $" + Str(betAmount) + " per draw." + Chr(10) + " - Hold Draw button 1 second to start auto-drawing." + Chr(10) + "Current balance: $" + Str(balance), #PB_MessageRequester_Ok) EndIf EndProcedure ; ==================================================================================== ; MAIN LOOP ; ==================================================================================== BuildGUI() Repeat Repeat Define ev = WindowEvent() Select ev Case #PB_Event_CloseWindow gQuit = 1 CompilerIf #PB_OS_Windows Define r.RECT_ALT GetClientRect_(WindowID(#winMain), @r) Define nw = r\right - r\left Define nh = r\bottom - r\top CompilerElse Define nw = WindowWidth(#winMain) Define nh = WindowHeight(#winMain) CompilerEndIf If nw > 0 And nh > 0 And (nw <> gWinW Or nh <> gWinH) CloseScreen() OpenWindowedScreen(WindowID(#winMain), 0, 0, nw, nh, #False, 0, 0, #PB_Screen_WaitSynchronization) gWinW = nw : gWinH = nh UpdateLayout() RebuildFonts() SetWindowColor(#winMain, CBg()) ; ? add this line EndIf EndSelect Until ev = 0 Or gQuit If gQuit : Break : EndIf RunTimers() HandleInput() If gQuit : Break : EndIf RenderFrame() ForEver ; ==================================================================================== ; CLEAN EXIT ; ==================================================================================== If gSaveDirty Or gSaveNeeded : SaveGame() : EndIf SaveGame() If gCryptOK : CloseCryptRandom() : EndIf CloseScreen() End ; IDE Options = PureBasic 6.40 (Windows - x64) ; CursorPosition = 829 ; FirstLine = 805 ; Folding = ---------------- ; EnableXP ; DPIAware