diff --git a/build/graphics/poker.exe b/build/graphics/poker.exe index fd5115f..dfd7820 100644 Binary files a/build/graphics/poker.exe and b/build/graphics/poker.exe differ diff --git a/casino3d.inc b/casino3d.inc index 96f7064..549c02a 100644 --- a/casino3d.inc +++ b/casino3d.inc @@ -149,11 +149,15 @@ sz3DWindowTitle db "Assembler Casino - 2.5D",0 szTexturesRelPath db "assets\textures\casino3d-hd.bin",0 szTexturesFullPath db 260 dup(0) -soundCoin db "assets\audio\Çâîí ïàäàþùåé ìîíåòû.wav",0 +soundCoin db "assets\audio\Çâóê ìîíåòû áðîøåííîé â âîçäóõ è óïàâøåé íà ïîâåðõíîñòü.wav",0 +soundCoinDing db "assets\audio\Çâóê ìîíåòû â èãðå Ultrakill.wav",0 soundCards db "assets\audio\playing_cards_deck_flick_through_001_20485.wav",0 soundRoulette db "assets\audio\roulette-wheel-throw-2.wav",0 -soundJackpot db "assets\audio\Äæåêïîò.wav",0 -soundClick db "assets\audio\b05e1ac8be34248.wav",0 +soundJackpot db "assets\audio\Äæýê ïîò.wav",0 +soundVictory db "assets\audio\victory-tf2.wav",0 +soundClick db "assets\audio\revolverdryfirec.wav",0 +soundShuffle db "assets\audio\moving-stone.wav",0 +soundLose db "assets\audio\Ïðîèãðûø.wav",0 playerX dd (16 * FP_1 + FP_HALF) playerY dd (16 * FP_1 + FP_HALF) @@ -288,65 +292,81 @@ coin3d_result dd 1 ; 1 = coin3d_streak dd 0 coin3d_win dd 0 -coinRxAnimTable db 22, 18, 12, 4, 12, 18, 22, 18, 12, 4 +coinRxAnimTable db 26, 22, 16, 6, 16, 22, 26, 22, 16, 6 -textCoinTitle db "ÌÎÍÅÒÊÀ (ÎÐ¨Ë / ÐÅØÊÀ)",0 +; --- Native 2.5D Coin Flip --- +textCoinTitle db "* * * Ç Î Ë Î Ò À ß Ì Î Í Å Ò À * * *",0 textCoinHeads db "ÎШË",0 textCoinTails db "ÐÅØÊÀ",0 textCoinStreak db "ÑÅÐÈß: ",0 textCoinSide1 db "[1] ÎШË",0 textCoinSide2 db "[2] ÐÅØÊÀ",0 -textCoinFlipping db "ÌÎÍÅÒÀ  ÂÎÇÄÓÕÅ...",0 -textCoinWin db "ÂÛÈÃÐÛØ! +$",0 -textCoinLose db "ÏÐÎÈÃÐÛØ!",0 -textCoinControls1 db "[1/2] ÑÒÎÐÎÍÀ [ÏÐÎÁÅË] ÁÐÎÑÎÊ",0 -textCoinControls2 db "[ÂÂÅÐÕ/ÂÍÈÇ] ÑÒÀÂÊÀ [ESC] ÂÛÕÎÄ",0 +textCoinFlipping db "ÌÎÍÅÒÀ  ÏÎ˨ÒÅ...",0 +textCoinWin db "ÂÛÈÃÐÛØ: +$",0 +textCoinLose db "ÍÅ ÓÃÀÄÀË!",0 +textCoinKey1 db "1:ÎШË",0 +textCoinKey2 db "2:ÐÅØÊÀ",0 +textCoinKeySpace db "ÏÐÎÁÅË:ÁÐÎÑÎÊ",0 +textCoinStreakFmt db "ÑÅÐÈß: [X] [X] [X] [ ] [ ] x4!",0 ; --- Native 2.5D Dice Game --- dice3d_bet dd 50 dice3d_choice dd 1 ; 1 = ׸ò, 2 = Íå÷åò dice3d_state dd 0 ; 0 = Idle, 1 = Rolling, 2 = Result dice3d_timer dd 0 -dice3d_val1 dd 3 -dice3d_val2 dd 4 -dice3d_sum dd 7 +dice3d_val1 dd 4 +dice3d_val2 dd 6 +dice3d_sum dd 10 dice3d_win dd 0 -textDiceTitle db "ÊÎÑÒÈ (CRAPS) - ×¨Ò / ÍÅ×ÅÒ",0 +textDiceTitle db "=== G R A N D C R A P S ===",0 +textDiceSubtitle db "[ PASS LINE ] [ ANY CRAPS ]",0 textDiceEven db "[1] רÒ",0 textDiceOdd db "[2] ÍÅ×ÅÒ",0 -textDiceTally db "ÊÎÑÒÈ: ",0 +textDiceEvenNums db "2 4 6 8 10",0 +textDiceOddNums db "3 5 7 9 11",0 +textDiceSumHdr db "ÑÓÌÌÀ: ",0 textDicePlus db " + ",0 textDiceEqual db " = ",0 textDiceEvenResult db " (רÒ)",0 textDiceOddResult db " (ÍÅ×ÅÒ)",0 -textDiceRolling db "ÁÐÎÑÎÊ ÊÎÑÒÅÉ...",0 -textDiceWin db "ÂÛÈÃÐÛØ! +$",0 +textDiceRolling db "ÊÎÑÒÈ ËÅÒßÒ ÏÎ ÑÓÊÍÓ...",0 +textDiceWin db "ÂÛÈÃÐÛØ: +$",0 +textDiceWinMult db "! (x2)",0 textDiceLose db "ÏÐÎÈÃÐÛØ!",0 -textDiceControls1 db "[1] ×¨Ò [2] ÍÅ×ÅÒ [ÏÐÎÁÅË] ÁÐÎÑÎÊ",0 -textDiceControls2 db "[ÂÂÅÐÕ/ÂÍÈÇ] ÑÒÀÂÊÀ [ESC] ÂÛÕÎÄ",0 +textDiceKey1 db "1:רÒ",0 +textDiceKey2 db "2:ÍÅ×ÅÒ",0 +textDiceKeySpace db "ÏÐÎÁÅË:ÁÐÎÑÎÊ",0 ; --- Native 2.5D Skulls Game --- skulls3d_bet dd 50 skulls3d_state dd 0 ; 0 = Idle, 1 = Show ball, 2 = Shuffle, 3 = Pick, 4 = Reveal skulls3d_timer dd 0 -skulls3d_ballPos dd 0 ; 0, 1, 2 -skulls3d_choice dd 0 ; 0, 1, 2 +skulls3d_ballPos dd 1 ; 0, 1, 2 +skulls3d_choice dd 1 ; 0, 1, 2 skulls3d_win dd 0 skulls3d_swapPair0 dd 0 skulls3d_swapPair1 dd 1 skulls3d_swapPair2 dd 2 -textSkullsTitle db "ÍÀϨÐÑÒÊÈ (×ÅÐÅÏÀ È ÊÓÁÊÈ)",0 -textSkullsPhase0 db "ÂÛÁÅÐÈÒÅ ÑÒÀÂÊÓ È ÍÀÆÌÈÒÅ [ÏÐÎÁÅË]",0 -textSkullsPhase1 db "ÇÀÏÎÌÍÈÒÅ, ÃÄÅ ØÀÐÈÊ!",0 +textSkullsTitle db "+ + + Í À Ï ¨ Ð Ñ Ò Ê È + + +",0 +textSkullsPhase0 db "ÄÅËÀÉ ÑÒÀÂÊÓ È ÆÌÈ [ÏÐÎÁÅË]",0 +textSkullsPhase1 db "ÑÌÎÒÐÈ, ÃÄÅ ÇÎËÎÒÎÉ ØÀÐ...",0 textSkullsPhase2 db "ÊÐÓ×Ó, ÂÅÐ×Ó, ÇÀÏÓÒÀÒÜ ÕÎ×Ó...",0 -textSkullsPhase3 db "ÃÄÅ ØÀÐÈÊ? ÍÀÆÌÈÒÅ [1], [2] ÈËÈ [3]!",0 -textSkullsWin db "ÂÛ ÓÃÀÄÀËÈ! ÄÆÅÊÏÎÒ x3! +$",0 -textSkullsLose db "ÍÅ ÓÃÀÄÀËÈ! ØÀÐÈÊ ÁÛË ÍÅ ÇÄÅÑÜ!",0 +textSkullsPhase3 db "ÃÄÅ ØÀÐ? ÂÛÁÈÐÀÉ: [1], [2] ÈËÈ [3]!",0 +textSkullsWin db "ÂÅÐÍÎ! ÂÛÏËÀÒÀ x3! +$",0 +textSkullsLose db "ÌÈÌÎ! ØÀÐ ÁÛË Â ÄÐÓÃÎÌ ÌÅÑÒÅ!",0 textSkullsAgain db "[ÏÐÎÁÅË] - ÑÛÃÐÀÒÜ ÅÙ¨ ÐÀÇ",0 -textSkullsControls1 db "[1/2/3] ÂÛÁÎÐ ÊÓÁÊÀ [ÏÐÎÁÅË] ÑÒÀÐÒ",0 -textSkullsControls2 db "[ÂÂÅÐÕ/ÂÍÈÇ] ÑÒÀÂÊÀ [ESC] ÂÛÕÎÄ",0 +textSkullsKey1 db "1:ÊÓÁÎÊ",0 +textSkullsKey2 db "2:ÊÓÁÎÊ",0 +textSkullsKey3 db "3:ÊÓÁÎÊ",0 +textSkullsKeySpace db "ÏÐÎÁÅË",0 + +; --- Shared UI Strings --- +textKeyEsc db "ESC",0 +textBankPrompt db "ÁÀÍÊ: $",0 +textBetPrompt db "ÑÒÀÂÊÀ: $",0 +textBetHint db " [W/S]",0 shopMsg db 128 dup(0) atmMsg db 128 dup(0) @@ -2630,15 +2650,102 @@ RenderATMGame PROC ret RenderATMGame ENDP +; ============================================================================== +; ============================================================================== +; SHARED RICH UI PROCEDURES +; ============================================================================== +DrawKeycap3D PROC uses ebx esi edi, x:DWORD, y:DWORD, strPtr:DWORD, isActive:DWORD + LOCAL capW:DWORD + ; Compute string length + mov esi, strPtr + xor ecx, ecx +@@: + mov al, [esi+ecx] + test al, al + jz @F + inc ecx + jmp @B +@@: + shl ecx, 3 ; length * 8 + add ecx, 8 ; padding 8px + mov capW, ecx + + ; Drop shadow + mov eax, x + mov edx, y + add edx, 2 + invoke DrawBox, eax, edx, capW, 13, 0008080Ch + + ; Body & Border + mov eax, 00606870h ; default border + mov edx, 001A1C22h ; default fill + cmp isActive, 0 + je @F + mov eax, 0000FFFFh ; active gold border + mov edx, 001B4B22h ; active dark green fill +@@: + invoke DrawBoxBorder, x, y, capW, 13, edx, eax, 1 + + ; String text + mov eax, 00D0D8E0h + cmp isActive, 0 + je @F + mov eax, 0000FFFFh +@@: + mov edx, x + add edx, 4 + mov ecx, y + add ecx, 3 + invoke DrawString8x8, edx, ecx, strPtr, eax + mov eax, capW + ret +DrawKeycap3D ENDP + +DrawChipStack PROC uses ebx esi edi, x:DWORD, y:DWORD, chipCount:DWORD + xor esi, esi +chip_loop: + cmp esi, chipCount + jge chip_done + mov eax, esi + shl eax, 2 ; esi * 4 + mov edx, y + sub edx, eax ; cy = y - esi*4 + + ; Shadow + mov eax, x + add eax, 2 + mov ecx, edx + add ecx, 5 + invoke DrawBox, eax, ecx, 20, 2, 00041006h + + ; Chip body + invoke DrawBoxBorder, x, edx, 24, 6, 001515B8h, 00FFFFFFh, 1 + + ; White stripe inlays + mov eax, x + add eax, 4 + mov ecx, edx + add ecx, 1 + invoke DrawBox, eax, ecx, 3, 4, 00FFFFFFh + mov eax, x + add eax, 17 + invoke DrawBox, eax, ecx, 3, 4, 00FFFFFFh + + inc esi + jmp chip_loop +chip_done: + ret +DrawChipStack ENDP + ; ============================================================================== ; IN-ENGINE 2.5D MINI-GAME: COIN FLIP (ÌÎÍÅÒÊÀ) ; ============================================================================== -DrawCoinEllipse PROC uses ebx esi edi cxVal:DWORD, cyVal:DWORD, rxVal:DWORD, ryVal:DWORD, outerColor:DWORD, innerColor:DWORD - local curDY:DWORD, curDX:DWORD, rxSq:DWORD, rySq:DWORD, rxRySq:DWORD, threshSq:DWORD +DrawDetailedCoin PROC uses ebx esi edi, cxVal:DWORD, cyVal:DWORD, rxVal:DWORD, ryVal:DWORD, outerColor:DWORD, innerColor:DWORD, reliefType:DWORD + LOCAL curDY:DWORD, curDX:DWORD, rxSq:DWORD, rySq:DWORD, rxRySq:DWORD, threshSq:DWORD cmp rxVal, 0 - jle ellipse_done + jle coin_draw_ret cmp ryVal, 0 - jle ellipse_done + jle coin_draw_ret mov eax, rxVal imul eax, eax @@ -2661,19 +2768,19 @@ DrawCoinEllipse PROC uses ebx esi edi cxVal:DWORD, cyVal:DWORD, rxVal:DWORD, ryV neg eax mov curDY, eax -ellipse_y_loop: +coin_y_loop: mov eax, curDY cmp eax, ryVal - jg ellipse_done + jg check_coin_relief mov eax, rxVal neg eax mov curDX, eax -ellipse_x_loop: +coin_x_loop: mov eax, curDX cmp eax, rxVal - jg next_ellipse_y + jg next_coin_y mov eax, curDX imul eax, eax @@ -2683,7 +2790,7 @@ ellipse_x_loop: imul ebx, rxSq add eax, ebx cmp eax, rxRySq - jg next_ellipse_x + jg next_coin_x mov ecx, innerColor cmp eax, threshSq @@ -2696,17 +2803,163 @@ ellipse_x_loop: add edx, curDY invoke DrawPixel, eax, edx, ecx -next_ellipse_x: +next_coin_x: inc curDX - jmp ellipse_x_loop + jmp coin_x_loop -next_ellipse_y: +next_coin_y: inc curDY - jmp ellipse_y_loop + jmp coin_y_loop -ellipse_done: +check_coin_relief: + cmp rxVal, 14 + jl coin_draw_ret + cmp reliefType, 1 + je draw_eagle_relief + cmp reliefType, 2 + je draw_wreath_relief + jmp coin_draw_ret + +draw_eagle_relief: + ; Crown + mov eax, cxVal + sub eax, 3 + mov edx, cyVal + sub edx, 14 + invoke DrawBox, eax, edx, 7, 2, 00007A9Ah + mov eax, cxVal + sub eax, 4 + mov edx, cyVal + sub edx, 12 + invoke DrawBox, eax, edx, 9, 2, 00007A9Ah + + ; Body & Head + mov eax, cxVal + sub eax, 2 + mov edx, cyVal + sub edx, 9 + invoke DrawBox, eax, edx, 5, 12, 00007A9Ah + mov eax, cxVal + dec eax + mov edx, cyVal + sub edx, 8 + invoke DrawBox, eax, edx, 3, 10, 0000D7FFh + + ; Wings + mov eax, cxVal + sub eax, 10 + mov edx, cyVal + sub edx, 8 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + mov eax, cxVal + sub eax, 13 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 10, 3, 00007A9Ah + mov eax, cxVal + sub eax, 11 + mov edx, cyVal + sub edx, 2 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + + mov eax, cxVal + add eax, 4 + mov edx, cyVal + sub edx, 8 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + mov eax, cxVal + add eax, 4 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 10, 3, 00007A9Ah + mov eax, cxVal + add eax, 5 + mov edx, cyVal + sub edx, 2 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + + ; Tail + mov eax, cxVal + sub eax, 4 + mov edx, cyVal + add edx, 4 + invoke DrawBox, eax, edx, 9, 3, 00007A9Ah + mov eax, cxVal + sub eax, 3 + mov edx, cyVal + add edx, 7 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + jmp coin_draw_ret + +draw_wreath_relief: + ; Roman numeral 'I' + mov eax, cxVal + sub eax, 5 + mov edx, cyVal + sub edx, 10 + invoke DrawBox, eax, edx, 11, 3, 00007A9Ah + mov eax, cxVal + sub eax, 2 + mov edx, cyVal + sub edx, 7 + invoke DrawBox, eax, edx, 5, 14, 00007A9Ah + mov eax, cxVal + dec eax + mov edx, cyVal + sub edx, 6 + invoke DrawBox, eax, edx, 3, 12, 0000D7FFh + mov eax, cxVal + sub eax, 6 + mov edx, cyVal + add edx, 7 + invoke DrawBox, eax, edx, 13, 3, 00007A9Ah + + ; Laurel dots left & right + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + sub edx, 10 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + sub edx, 10 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + add edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + add edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + +coin_draw_ret: ret -DrawCoinEllipse ENDP +DrawDetailedCoin ENDP InitCoinGame PROC mov coin3d_state, 0 @@ -2723,7 +2976,7 @@ HandleCoinInput PROC cmp coin3d_state, 1 je coin_input_done - invoke Pressed3D, 20h ; SPACE + invoke Pressed3D, 20h ; SPACE -> Flip! test eax, eax jz check_coin_side mov eax, bankroll @@ -2734,11 +2987,13 @@ HandleCoinInput PROC mov coin3d_state, 1 mov coin3d_timer, 0 mov coin3d_win, 0 - invoke PlaySoundA, OFFSET soundClick, 0, 1 + invoke PlaySoundA, OFFSET soundCoin, 0, 1 jmp coin_input_done + coin_no_cash: invoke CopyString3D, OFFSET msgNeedMoney, OFFSET flashMessage mov flashTimer, 60 + invoke PlaySoundA, OFFSET soundClick, 0, 1 jmp coin_input_done check_coin_side: @@ -2746,11 +3001,13 @@ check_coin_side: test eax, eax jz @F mov coin3d_side, 1 + invoke PlaySoundA, OFFSET soundClick, 0, 1 @@: invoke Pressed3D, 32h ; '2' test eax, eax jz check_coin_bet_adjust mov coin3d_side, 2 + invoke PlaySoundA, OFFSET soundClick, 0, 1 check_coin_bet_adjust: invoke Pressed3D, 26h ; UP @@ -2760,6 +3017,7 @@ check_coin_bet_adjust: test eax, eax jz check_coin_bet_down coin_inc_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 cmp coin3d_bet, 500 jge coin_input_done cmp coin3d_bet, 250 @@ -2771,25 +3029,6 @@ coin_inc_bet: cmp coin3d_bet, 25 jge coin_set_50 jmp coin_set_25 - -check_coin_bet_down: - invoke Pressed3D, 28h ; DOWN - test eax, eax - jnz coin_dec_bet - invoke Pressed3D, 53h ; S - test eax, eax - jz coin_input_done -coin_dec_bet: - cmp coin3d_bet, 25 - jle coin_set_10 - cmp coin3d_bet, 50 - jle coin_set_25 - cmp coin3d_bet, 100 - jle coin_set_50 - cmp coin3d_bet, 250 - jle coin_set_100 - jmp coin_set_250 - coin_set_500: mov coin3d_bet, 500 jmp coin_input_done @@ -2805,62 +3044,113 @@ coin_set_50: coin_set_25: mov coin3d_bet, 25 jmp coin_input_done + +check_coin_bet_down: + invoke Pressed3D, 28h ; DOWN + test eax, eax + jnz coin_dec_bet + invoke Pressed3D, 53h ; S + test eax, eax + jz coin_input_done +coin_dec_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp coin3d_bet, 25 + jle coin_set_10 + cmp coin3d_bet, 50 + jle coin_set_25 + cmp coin3d_bet, 100 + jle coin_set_50 + cmp coin3d_bet, 250 + jle coin_set_100 + mov coin3d_bet, 250 + jmp coin_input_done coin_set_10: mov coin3d_bet, 10 - jmp coin_input_done coin_input_done: ret HandleCoinInput ENDP RenderCoinGame PROC uses ebx esi edi - local cyVal:DWORD, rxVal:DWORD + LOCAL cyVal:DWORD, rxVal:DWORD, curX:DWORD - ; Background & Bezel - invoke DrawBox, 0, 0, SCREEN_W, SCREEN_H, 00140D18h - invoke DrawBoxBorder, 10, 10, 300, 220, 0022142Ah, 0000D7FFh, 3 + ; Cabinet Bezel (Mahogany wood & leather) + invoke DrawBox, 0, 0, 320, 240, 00060914h + invoke DrawBoxBorder, 6, 6, 308, 228, 000F1522h, 001B2E48h, 3 + + ; Corner Brass Rivets + invoke DrawBox, 12, 12, 3, 3, 0000D7FFh + invoke DrawPixel, 13, 13, 00FFFFFFh + invoke DrawBox, 306, 12, 3, 3, 0000D7FFh + invoke DrawPixel, 307, 13, 00FFFFFFh + invoke DrawBox, 12, 226, 3, 3, 0000D7FFh + invoke DrawPixel, 13, 227, 00FFFFFFh + invoke DrawBox, 306, 226, 3, 3, 0000D7FFh + invoke DrawPixel, 307, 227, 00FFFFFFh ; Illuminated Marquee Header - invoke DrawBoxBorder, 25, 18, 270, 26, 000D0812h, 0000FFFFh, 2 - invoke DrawString8x8, 72, 27, OFFSET textCoinTitle, 0000D7FFh + invoke DrawBoxBorder, 24, 12, 272, 26, 000A101Ch, 0000D7FFh, 2 + invoke DrawString8x8, 48, 21, OFFSET textCoinTitle, 0000FFFFh - ; Streak Display - invoke DrawString8x8, 28, 50, OFFSET textCoinStreak, 0000D7FFh - invoke FormatNumber, coin3d_streak, OFFSET hudNumBuffer - invoke DrawString8x8, 84, 50, OFFSET hudNumBuffer, 0050FF50h + ; Bulb lights on marquee border + mov curX, 28 +coin_bulbs: + cmp curX, 292 + jge coin_meters + invoke DrawPixel, curX, 13, 0000FFFFh + invoke DrawPixel, curX, 36, 0000D7FFh + add curX, 12 + jmp coin_bulbs - ; Side Panel: [1] ÎÐ¨Ë - mov eax, 00120D16h - mov edx, 00505050h - mov ecx, 00808080h +coin_meters: + ; Status Meters: Bank & Bet + invoke DrawBoxBorder, 20, 44, 114, 18, 000A1A0Eh, 001A4B22h, 1 + invoke DrawString8x8, 24, 49, OFFSET textBankPrompt, 0050FF50h + invoke FormatNumber, bankroll, OFFSET hudNumBuffer + invoke DrawString8x8, 72, 49, OFFSET hudNumBuffer, 0050FF50h + + invoke DrawBoxBorder, 186, 44, 114, 18, 001A100Ah, 003A2814h, 1 + invoke DrawString8x8, 190, 49, OFFSET textBetPrompt, 0000D7FFh + invoke FormatNumber, coin3d_bet, OFFSET hudNumBuffer + invoke DrawString8x8, 254, 49, OFFSET hudNumBuffer, 0000D7FFh + invoke DrawString8x8, 278, 49, OFFSET textBetHint, 00808080h + + ; Center Velvet Platter + invoke DrawCircle, 160, 110, 46, 000090C0h + invoke DrawCircle, 160, 110, 44, 0000D7FFh + invoke DrawCircle, 160, 110, 41, 00180824h + + ; Selection Podiums: [1] ÎÐ¨Ë + mov eax, 00121620h + mov edx, 00404855h + mov ecx, 0090A0B0h cmp coin3d_side, 1 jne @F - mov eax, 001E3520h + mov eax, 001C3822h mov edx, 0000FFFFh mov ecx, 0000FFFFh @@: push ecx - invoke DrawBoxBorder, 24, 70, 68, 38, eax, edx, 2 + invoke DrawBoxBorder, 20, 72, 76, 52, eax, edx, 2 pop ecx - invoke DrawString8x8, 28, 85, OFFSET textCoinSide1, ecx + invoke DrawString8x8, 28, 78, OFFSET textCoinSide1, ecx + invoke DrawDetailedCoin, 58, 106, 12, 12, 0000A0D0h, 0000D7FFh, 1 - ; Side Panel: [2] ÐÅØÊÀ - mov eax, 00120D16h - mov edx, 00505050h - mov ecx, 00808080h + ; Selection Podiums: [2] ÐÅØÊÀ + mov eax, 00121620h + mov edx, 00404855h + mov ecx, 0090A0B0h cmp coin3d_side, 2 jne @F - mov eax, 001E3520h + mov eax, 001C3822h mov edx, 0000FFFFh mov ecx, 0000FFFFh @@: push ecx - invoke DrawBoxBorder, 228, 70, 68, 38, eax, edx, 2 + invoke DrawBoxBorder, 224, 72, 76, 52, eax, edx, 2 pop ecx - invoke DrawString8x8, 232, 85, OFFSET textCoinSide2, ecx - - ; Center coin pedestal arena - invoke DrawBoxBorder, 102, 54, 116, 84, 000F0A12h, 00504030h, 1 + invoke DrawString8x8, 232, 78, OFFSET textCoinSide2, ecx + invoke DrawDetailedCoin, 262, 106, 12, 12, 000080A0h, 0000B0D0h, 2 ; Flipping animation logic cmp coin3d_state, 1 @@ -2868,13 +3158,15 @@ RenderCoinGame PROC uses ebx esi edi inc coin3d_timer - ; Parabolic toss: cy = 96 - (t * (40 - t)) / 16 + ; Parabolic toss: cy = 110 - (t * (40 - t)) / 14 mov eax, coin3d_timer mov ecx, 40 sub ecx, eax imul eax, ecx - shr eax, 4 - mov edx, 96 + xor edx, edx + mov ecx, 14 + div ecx + mov edx, 110 sub edx, eax mov cyVal, edx @@ -2886,8 +3178,8 @@ RenderCoinGame PROC uses ebx esi edi movzx eax, BYTE PTR [coinRxAnimTable + edx] mov rxVal, eax - invoke DrawCoinEllipse, 160, cyVal, rxVal, 22, 0000D7FFh, 0000B0E0h - invoke DrawString8x8, 84, 142, OFFSET textCoinFlipping, 0000FFFFh + invoke DrawDetailedCoin, 160, cyVal, rxVal, 26, 0000A0D0h, 0000D7FFh, 0 + invoke DrawString8x8, 88, 160, OFFSET textCoinFlipping, 0000FFFFh cmp coin3d_timer, 40 jl coin_draw_controls @@ -2910,147 +3202,177 @@ RenderCoinGame PROC uses ebx esi edi inc coin3d_streak cmp coin3d_streak, 3 jl @F - invoke PlaySoundA, OFFSET soundJackpot, 0, 1 + invoke PlaySoundA, OFFSET soundVictory, 0, 1 jmp coin_draw_controls @@: - invoke PlaySoundA, OFFSET soundCoin, 0, 1 + invoke PlaySoundA, OFFSET soundCoinDing, 0, 1 jmp coin_draw_controls coin_flip_lost: mov coin3d_streak, 0 mov coin3d_win, 0 - invoke PlaySoundA, OFFSET soundClick, 0, 1 + invoke PlaySoundA, OFFSET soundLose, 0, 1 jmp coin_draw_controls coin_at_rest: - ; Draw coin resting at (160, 96) - invoke DrawCoinEllipse, 160, 96, 24, 24, 0000D7FFh, 0000B0E0h - invoke DrawCoinEllipse, 160, 96, 20, 20, 0000FFFFh, 000090D0h - - ; Draw side text inside coin + ; Draw coin resting in velvet dish + mov edx, coin3d_side cmp coin3d_state, 2 - jne coin_show_picked_side - cmp coin3d_result, 1 jne @F - invoke DrawString8x8, 144, 92, OFFSET textCoinHeads, 00000030h - jmp coin_show_splash + mov edx, coin3d_result @@: - invoke DrawString8x8, 140, 92, OFFSET textCoinTails, 00000030h - jmp coin_show_splash + invoke DrawDetailedCoin, 160, 110, 26, 26, 0000A0D0h, 0000D7FFh, edx -coin_show_picked_side: - cmp coin3d_side, 1 - jne @F - invoke DrawString8x8, 144, 92, OFFSET textCoinHeads, 00000030h - jmp coin_draw_controls -@@: - invoke DrawString8x8, 140, 92, OFFSET textCoinTails, 00000030h - jmp coin_draw_controls + ; Outcome / Streak Display + cmp coin3d_state, 2 + jne coin_show_streak -coin_show_splash: cmp coin3d_win, 0 - je coin_show_loss - invoke DrawString8x8, 90, 142, OFFSET textCoinWin, 0000FFFFh + je coin_show_lose + invoke DrawBoxBorder, 44, 154, 232, 20, 000E141Eh, 0050FF50h, 1 + invoke DrawString8x8, 64, 160, OFFSET textCoinWin, 0050FF50h invoke FormatNumber, coin3d_win, OFFSET hudNumBuffer - invoke DrawString8x8, 178, 142, OFFSET hudNumBuffer, 0000FFFFh + invoke DrawString8x8, 152, 160, OFFSET hudNumBuffer, 0050FF50h jmp coin_draw_controls -coin_show_loss: - invoke DrawString8x8, 126, 142, OFFSET textCoinLose, 000000FFh +coin_show_lose: + invoke DrawBoxBorder, 44, 154, 232, 20, 000E141Eh, 000000FFh, 1 + invoke DrawString8x8, 114, 160, OFFSET textCoinLose, 008080FFh + jmp coin_draw_controls + +coin_show_streak: + invoke DrawBoxBorder, 44, 154, 232, 20, 000E141Eh, 0000D7FFh, 1 + invoke DrawString8x8, 52, 160, OFFSET textCoinStreakFmt, 0000FFFFh coin_draw_controls: - ; Bottom Control Panel - invoke DrawBoxBorder, 20, 165, 280, 58, 00151515h, 0000D7FFh, 1 - invoke DrawString8x8, 28, 170, OFFSET textBank3D, 0000D7FFh - invoke FormatNumber, bankroll, OFFSET hudNumBuffer - invoke DrawString8x8, 88, 170, OFFSET hudNumBuffer, 0000FFFFh + ; Keycaps Control Bar + invoke DrawBox, 16, 194, 288, 1, 001E2E40h - invoke DrawString8x8, 180, 170, OFFSET textBet3D, 0000D7FFh - invoke FormatNumber, coin3d_bet, OFFSET hudNumBuffer - invoke DrawString8x8, 236, 170, OFFSET hudNumBuffer, 0050FF50h + mov eax, 0 + cmp coin3d_side, 1 + sete al + invoke DrawKeycap3D, 22, 202, OFFSET textCoinKey1, eax - invoke DrawString8x8, 28, 186, OFFSET textCoinControls1, 00FFFFFFh - invoke DrawString8x8, 28, 202, OFFSET textCoinControls2, 00A0A0A0h + mov eax, 0 + cmp coin3d_side, 2 + sete al + invoke DrawKeycap3D, 88, 202, OFFSET textCoinKey2, eax + + invoke DrawKeycap3D, 160, 202, OFFSET textCoinKeySpace, 1 + invoke DrawKeycap3D, 274, 202, OFFSET textKeyEsc, 0 ret RenderCoinGame ENDP ; ============================================================================== ; IN-ENGINE 2.5D MINI-GAME: DICE / CRAPS (ÊÎÑÒÈ) ; ============================================================================== -DrawDieCube PROC uses ebx esi edi x:DWORD, y:DWORD, cubeSize:DWORD, valNum:DWORD - local pipColor:DWORD - mov pipColor, 001515C0h - - invoke DrawBoxBorder, x, y, cubeSize, cubeSize, 00F4F6F8h, 00303038h, 2 - +DrawCrapsDie PROC uses ebx esi edi, x:DWORD, y:DWORD, sizeVal:DWORD, valNum:DWORD + ; Drop shadow on green felt mov eax, x - add eax, cubeSize + add eax, 3 mov edx, y - add edx, 2 - invoke DrawBox, eax, edx, 2, cubeSize, 0008140Ah + add edx, sizeVal + invoke DrawBox, eax, edx, sizeVal, 3, 00031408h mov eax, x - add eax, 2 + add eax, sizeVal mov edx, y - add edx, cubeSize - invoke DrawBox, eax, edx, cubeSize, 2, 0008140Ah + add edx, 3 + invoke DrawBox, eax, edx, 3, sizeVal, 00031408h + ; Ivory body & dark border + invoke DrawBoxBorder, x, y, sizeVal, sizeVal, 00F0F4FAh, 00353540h, 1 + + ; Bevel highlight top & left + mov eax, sizeVal + sub eax, 2 + mov edx, x + inc edx + mov ecx, y + inc ecx + invoke DrawBox, edx, ecx, eax, 1, 00FFFFFFh + invoke DrawBox, edx, ecx, 1, eax, 00FFFFFFh + + ; Bevel shadow bottom & right + mov edx, x + inc edx + mov ecx, y + add ecx, sizeVal + sub ecx, 2 + invoke DrawBox, edx, ecx, eax, 1, 00C0C6D0h + mov edx, x + add edx, sizeVal + sub edx, 2 + mov ecx, y + inc ecx + invoke DrawBox, edx, ecx, 1, eax, 00C0C6D0h + + ; Casino Ruby Red circular pips (radius 3) cmp valNum, 1 - je draw_die_center + je draw_die_c cmp valNum, 3 - je draw_die_center + je draw_die_c cmp valNum, 5 - je draw_die_center - jmp check_die_corners -draw_die_center: + je draw_die_c + jmp check_die_pips_corners + +draw_die_c: mov eax, x add eax, 18 mov edx, y add edx, 18 - invoke DrawCircle, eax, edx, 2, pipColor + invoke DrawCircle, eax, edx, 3, 001515C8h + dec eax + dec edx + invoke DrawPixel, eax, edx, 00FFFFFFh -check_die_corners: +check_die_pips_corners: cmp valNum, 2 - jl die_render_done + jl die_pips_done mov eax, x add eax, 9 mov edx, y add edx, 9 - invoke DrawCircle, eax, edx, 2, pipColor + invoke DrawCircle, eax, edx, 3, 001515C8h mov eax, x - add eax, 27 + add eax, sizeVal + sub eax, 10 mov edx, y - add edx, 27 - invoke DrawCircle, eax, edx, 2, pipColor + add edx, sizeVal + sub edx, 10 + invoke DrawCircle, eax, edx, 3, 001515C8h cmp valNum, 4 - jl die_render_done + jl die_pips_done mov eax, x - add eax, 27 + add eax, sizeVal + sub eax, 10 mov edx, y add edx, 9 - invoke DrawCircle, eax, edx, 2, pipColor + invoke DrawCircle, eax, edx, 3, 001515C8h mov eax, x add eax, 9 mov edx, y - add edx, 27 - invoke DrawCircle, eax, edx, 2, pipColor + add edx, sizeVal + sub edx, 10 + invoke DrawCircle, eax, edx, 3, 001515C8h cmp valNum, 6 - jl die_render_done + jl die_pips_done mov eax, x add eax, 9 mov edx, y add edx, 18 - invoke DrawCircle, eax, edx, 2, pipColor + invoke DrawCircle, eax, edx, 3, 001515C8h mov eax, x - add eax, 27 + add eax, sizeVal + sub eax, 10 mov edx, y add edx, 18 - invoke DrawCircle, eax, edx, 2, pipColor + invoke DrawCircle, eax, edx, 3, 001515C8h -die_render_done: +die_pips_done: ret -DrawDieCube ENDP +DrawCrapsDie ENDP InitDiceGame PROC mov dice3d_state, 0 @@ -3067,7 +3389,7 @@ HandleDiceInput PROC cmp dice3d_state, 1 je dice_input_done - invoke Pressed3D, 20h ; SPACE + invoke Pressed3D, 20h ; SPACE -> Roll! test eax, eax jz check_dice_choice mov eax, bankroll @@ -3078,11 +3400,13 @@ HandleDiceInput PROC mov dice3d_state, 1 mov dice3d_timer, 0 mov dice3d_win, 0 - invoke PlaySoundA, OFFSET soundClick, 0, 1 + invoke PlaySoundA, OFFSET soundCards, 0, 1 jmp dice_input_done + dice_no_cash: invoke CopyString3D, OFFSET msgNeedMoney, OFFSET flashMessage mov flashTimer, 60 + invoke PlaySoundA, OFFSET soundClick, 0, 1 jmp dice_input_done check_dice_choice: @@ -3090,11 +3414,13 @@ check_dice_choice: test eax, eax jz @F mov dice3d_choice, 1 + invoke PlaySoundA, OFFSET soundClick, 0, 1 @@: invoke Pressed3D, 32h ; '2' test eax, eax jz check_dice_bet_adjust mov dice3d_choice, 2 + invoke PlaySoundA, OFFSET soundClick, 0, 1 check_dice_bet_adjust: invoke Pressed3D, 26h ; UP @@ -3104,6 +3430,7 @@ check_dice_bet_adjust: test eax, eax jz check_dice_bet_down dice_inc_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 cmp dice3d_bet, 500 jge dice_input_done cmp dice3d_bet, 250 @@ -3115,25 +3442,6 @@ dice_inc_bet: cmp dice3d_bet, 25 jge dice_set_50 jmp dice_set_25 - -check_dice_bet_down: - invoke Pressed3D, 28h ; DOWN - test eax, eax - jnz dice_dec_bet - invoke Pressed3D, 53h ; S - test eax, eax - jz dice_input_done -dice_dec_bet: - cmp dice3d_bet, 25 - jle dice_set_10 - cmp dice3d_bet, 50 - jle dice_set_25 - cmp dice3d_bet, 100 - jle dice_set_50 - cmp dice3d_bet, 250 - jle dice_set_100 - jmp dice_set_250 - dice_set_500: mov dice3d_bet, 500 jmp dice_input_done @@ -3149,58 +3457,115 @@ dice_set_50: dice_set_25: mov dice3d_bet, 25 jmp dice_input_done + +check_dice_bet_down: + invoke Pressed3D, 28h ; DOWN + test eax, eax + jnz dice_dec_bet + invoke Pressed3D, 53h ; S + test eax, eax + jz dice_input_done +dice_dec_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp dice3d_bet, 25 + jle dice_set_10 + cmp dice3d_bet, 50 + jle dice_set_25 + cmp dice3d_bet, 100 + jle dice_set_50 + cmp dice3d_bet, 250 + jle dice_set_100 + mov dice3d_bet, 250 + jmp dice_input_done dice_set_10: mov dice3d_bet, 10 - jmp dice_input_done dice_input_done: ret HandleDiceInput ENDP RenderDiceGame PROC uses ebx esi edi - local d1X:DWORD, d1Y:DWORD, d2X:DWORD, d2Y:DWORD + LOCAL d1X:DWORD, d1Y:DWORD, d2X:DWORD, d2Y:DWORD, bxX:DWORD - ; Background & Bezel - invoke DrawBox, 0, 0, SCREEN_W, SCREEN_H, 000C2612h - invoke DrawBoxBorder, 10, 10, 300, 220, 0014381Ah, 0000D7FFh, 3 + ; Craps Emerald Felt & Oak Armrest + invoke DrawBox, 0, 0, 320, 240, 00082E12h + invoke DrawBoxBorder, 4, 4, 312, 232, 00093816h, 00122244h, 3 + invoke DrawBoxBorder, 8, 8, 304, 224, 000A3F19h, 0005140Ah, 2 - ; Illuminated Marquee Header - invoke DrawBoxBorder, 25, 18, 270, 26, 000A1F0Eh, 0000FFFFh, 2 - invoke DrawString8x8, 52, 27, OFFSET textDiceTitle, 0000D7FFh + ; Pyramidal diamond craps backboard texture + mov bxX, 12 +craps_bumper: + cmp bxX, 308 + jge craps_felt_markings + mov eax, bxX + add eax, 4 + invoke DrawBox, eax, 11, 4, 4, 00041006h + mov eax, bxX + add eax, 5 + invoke DrawPixel, eax, 12, 000F4820h + add bxX, 12 + jmp craps_bumper - ; Choice Panels - mov eax, 000E2614h - mov edx, 00505050h - mov ecx, 00808080h +craps_felt_markings: + ; Table Markings printed in gold directly on felt + invoke DrawString8x8, 72, 20, OFFSET textDiceTitle, 0000D7FFh + invoke DrawString8x8, 68, 32, OFFSET textDiceSubtitle, 0000B0A0h + + ; Meters: Bank & Bet + invoke DrawBoxBorder, 16, 46, 108, 16, 0006240Eh, 000E5022h, 1 + invoke DrawString8x8, 20, 50, OFFSET textBankPrompt, 0050FF50h + invoke FormatNumber, bankroll, OFFSET hudNumBuffer + invoke DrawString8x8, 68, 50, OFFSET hudNumBuffer, 0050FF50h + + invoke DrawBoxBorder, 196, 46, 108, 16, 0006240Eh, 000E5022h, 1 + invoke DrawString8x8, 200, 50, OFFSET textBetPrompt, 0000D7FFh + invoke FormatNumber, dice3d_bet, OFFSET hudNumBuffer + invoke DrawString8x8, 264, 50, OFFSET hudNumBuffer, 0000D7FFh + + ; Betting Zone Left: [1] ×¨Ò + mov eax, 00073014h + mov edx, 001A602Ah + mov ecx, 0080B090h cmp dice3d_choice, 1 jne @F - mov eax, 001C4824h + mov eax, 000B441Dh mov edx, 0000FFFFh mov ecx, 0000FFFFh @@: push ecx - invoke DrawBoxBorder, 24, 55, 76, 36, eax, edx, 2 + invoke DrawBoxBorder, 16, 68, 88, 54, eax, edx, 2 pop ecx - invoke DrawString8x8, 34, 68, OFFSET textDiceEven, ecx + invoke DrawString8x8, 24, 74, OFFSET textDiceEven, ecx + invoke DrawString8x8, 20, 86, OFFSET textDiceEvenNums, 0000D7FFh + cmp dice3d_choice, 1 + jne @F + invoke DrawChipStack, 48, 110, 4 +@@: - mov eax, 000E2614h - mov edx, 00505050h - mov ecx, 00808080h + ; Betting Zone Right: [2] ÍÅ×ÅÒ + mov eax, 00073014h + mov edx, 001A602Ah + mov ecx, 0080B090h cmp dice3d_choice, 2 jne @F - mov eax, 001C4824h + mov eax, 000B441Dh mov edx, 0000FFFFh mov ecx, 0000FFFFh @@: push ecx - invoke DrawBoxBorder, 220, 55, 76, 36, eax, edx, 2 + invoke DrawBoxBorder, 216, 68, 88, 54, eax, edx, 2 pop ecx - invoke DrawString8x8, 226, 68, OFFSET textDiceOdd, ecx + invoke DrawString8x8, 224, 74, OFFSET textDiceOdd, ecx + invoke DrawString8x8, 224, 86, OFFSET textDiceOddNums, 00508060h + cmp dice3d_choice, 2 + jne @F + invoke DrawChipStack, 248, 110, 4 +@@: - ; Dice Table Box - invoke DrawBoxBorder, 104, 52, 112, 54, 0009200Dh, 00305030h, 1 + ; Center Dice Field + invoke DrawBoxBorder, 112, 68, 96, 54, 00072E13h, 00145826h, 1 - ; Rolling animation logic + ; Rolling Animation Logic cmp dice3d_state, 1 jne dice_at_rest @@ -3211,19 +3576,17 @@ RenderDiceGame PROC uses ebx esi edi and eax, 3 sub eax, 1 - mov edx, 112 + mov edx, 120 add edx, eax mov d1X, edx - - mov edx, 60 + mov edx, 76 sub edx, eax mov d1Y, edx - mov edx, 172 + mov edx, 164 sub edx, eax mov d2X, edx - - mov edx, 62 + mov edx, 78 add edx, eax mov d2Y, edx @@ -3236,15 +3599,14 @@ RenderDiceGame PROC uses ebx esi edi inc eax mov dice3d_val2, eax @@: - invoke DrawDieCube, d1X, d1Y, 36, dice3d_val1 - invoke DrawDieCube, d2X, d2Y, 36, dice3d_val2 - - invoke DrawString8x8, 96, 132, OFFSET textDiceRolling, 0000FFFFh + invoke DrawCrapsDie, d1X, d1Y, 36, dice3d_val1 + invoke DrawCrapsDie, d2X, d2Y, 36, dice3d_val2 + invoke DrawString8x8, 76, 138, OFFSET textDiceRolling, 0000FFFFh cmp dice3d_timer, 36 jl dice_draw_controls - ; Roll completed! + ; Settle dice! mov dice3d_state, 2 invoke RandomRange3D, 6 inc eax @@ -3257,108 +3619,228 @@ RenderDiceGame PROC uses ebx esi edi add eax, dice3d_val2 mov dice3d_sum, eax + ; Check choice vs sum parity + mov eax, dice3d_sum test eax, 1 jz dice_sum_is_even - mov ecx, 2 ; Odd - jmp check_dice_win + + ; Odd + cmp dice3d_choice, 2 + je dice_roll_won + jmp dice_roll_lost + dice_sum_is_even: - mov ecx, 1 ; Even + cmp dice3d_choice, 1 + je dice_roll_won -check_dice_win: - cmp ecx, dice3d_choice - jne dice_roll_lost +dice_roll_lost: + mov dice3d_win, 0 + invoke PlaySoundA, OFFSET soundLose, 0, 1 + jmp dice_draw_controls +dice_roll_won: mov eax, dice3d_bet shl eax, 1 mov dice3d_win, eax add bankroll, eax - invoke PlaySoundA, OFFSET soundCoin, 0, 1 - jmp dice_draw_controls - -dice_roll_lost: - mov dice3d_win, 0 - invoke PlaySoundA, OFFSET soundClick, 0, 1 + invoke PlaySoundA, OFFSET soundVictory, 0, 1 jmp dice_draw_controls dice_at_rest: - invoke DrawDieCube, 112, 60, 36, dice3d_val1 - invoke DrawDieCube, 172, 60, 36, dice3d_val2 + invoke DrawCrapsDie, 120, 76, 36, dice3d_val1 + invoke DrawCrapsDie, 164, 76, 36, dice3d_val2 cmp dice3d_state, 2 jne dice_draw_controls - ; Score Tally - invoke DrawString8x8, 80, 112, OFFSET textDiceTally, 0000D7FFh + ; Scoreboard Plaque + invoke DrawBoxBorder, 36, 130, 248, 30, 00051D0Ch, 0000D7FFh, 1 + invoke DrawString8x8, 44, 135, OFFSET textDiceSumHdr, 0000D7FFh invoke FormatNumber, dice3d_val1, OFFSET hudNumBuffer - invoke DrawString8x8, 136, 112, OFFSET hudNumBuffer, 00FFFFFFh - invoke DrawString8x8, 146, 112, OFFSET textDicePlus, 0000D7FFh + invoke DrawString8x8, 100, 135, OFFSET hudNumBuffer, 00FFFFFFh + invoke DrawString8x8, 110, 135, OFFSET textDicePlus, 0000D7FFh invoke FormatNumber, dice3d_val2, OFFSET hudNumBuffer - invoke DrawString8x8, 170, 112, OFFSET hudNumBuffer, 00FFFFFFh - invoke DrawString8x8, 180, 112, OFFSET textDiceEqual, 0000D7FFh + invoke DrawString8x8, 134, 135, OFFSET hudNumBuffer, 00FFFFFFh + invoke DrawString8x8, 144, 135, OFFSET textDiceEqual, 0000D7FFh invoke FormatNumber, dice3d_sum, OFFSET hudNumBuffer - invoke DrawString8x8, 204, 112, OFFSET hudNumBuffer, 0000FFFFh + invoke DrawString8x8, 168, 135, OFFSET hudNumBuffer, 0000FFFFh mov eax, dice3d_sum test eax, 1 - jz @F - invoke DrawString8x8, 222, 112, OFFSET textDiceOddResult, 00FF8080h - jmp dice_show_outcome + jnz @F + invoke DrawString8x8, 186, 135, OFFSET textDiceEvenResult, 0080FF80h + jmp dice_show_win_msg @@: - invoke DrawString8x8, 222, 112, OFFSET textDiceEvenResult, 0080FF80h + invoke DrawString8x8, 186, 135, OFFSET textDiceOddResult, 00FF8080h -dice_show_outcome: +dice_show_win_msg: cmp dice3d_win, 0 je dice_show_loss - invoke DrawString8x8, 90, 132, OFFSET textDiceWin, 0000FFFFh + invoke DrawString8x8, 68, 147, OFFSET textDiceWin, 0050FF50h invoke FormatNumber, dice3d_win, OFFSET hudNumBuffer - invoke DrawString8x8, 178, 132, OFFSET hudNumBuffer, 0000FFFFh + invoke DrawString8x8, 156, 147, OFFSET hudNumBuffer, 0050FF50h + invoke DrawString8x8, 188, 147, OFFSET textDiceWinMult, 0050FF50h jmp dice_draw_controls + dice_show_loss: - invoke DrawString8x8, 126, 132, OFFSET textDiceLose, 000000FFh + invoke DrawString8x8, 116, 147, OFFSET textDiceLose, 008080FFh dice_draw_controls: - invoke DrawBoxBorder, 20, 165, 280, 58, 00151515h, 0000D7FFh, 1 - invoke DrawString8x8, 28, 170, OFFSET textBank3D, 0000D7FFh - invoke FormatNumber, bankroll, OFFSET hudNumBuffer - invoke DrawString8x8, 88, 170, OFFSET hudNumBuffer, 0000FFFFh + invoke DrawBox, 16, 188, 288, 1, 00104820h - invoke DrawString8x8, 180, 170, OFFSET textBet3D, 0000D7FFh - invoke FormatNumber, dice3d_bet, OFFSET hudNumBuffer - invoke DrawString8x8, 236, 170, OFFSET hudNumBuffer, 0050FF50h + mov eax, 0 + cmp dice3d_choice, 1 + sete al + invoke DrawKeycap3D, 20, 198, OFFSET textDiceKey1, eax - invoke DrawString8x8, 28, 186, OFFSET textDiceControls1, 00FFFFFFh - invoke DrawString8x8, 28, 202, OFFSET textDiceControls2, 00A0A0A0h + mov eax, 0 + cmp dice3d_choice, 2 + sete al + invoke DrawKeycap3D, 82, 198, OFFSET textDiceKey2, eax + + invoke DrawKeycap3D, 160, 198, OFFSET textDiceKeySpace, 1 + invoke DrawKeycap3D, 274, 198, OFFSET textKeyEsc, 0 ret RenderDiceGame ENDP ; ============================================================================== ; IN-ENGINE 2.5D MINI-GAME: SKULLS & CUPS (ÍÀϨÐÑÒÊÈ) ; ============================================================================== -DrawCup PROC uses ebx esi edi x:DWORD, y:DWORD, cupNumChar:DWORD - invoke DrawBox, x + 14, y, 16, 4, 0000C8E8h - invoke DrawBoxBorder, x + 8, y + 4, 28, 12, 00221A30h, 0000D7FFh, 2 - invoke DrawBoxBorder, x + 4, y + 16, 36, 16, 00221A30h, 0000D7FFh, 2 - invoke DrawBoxBorder, x + 2, y + 32, 40, 8, 002A2038h, 0000FFFFh, 2 +DrawGlowingOrb PROC uses ebx esi edi, cxVal:DWORD, cyVal:DWORD + invoke DrawCircle, cxVal, cyVal, 14, 00081E2Eh + invoke DrawCircle, cxVal, cyVal, 11, 00103A58h + invoke DrawCircle, cxVal, cyVal, 8, 001A588Ah + invoke DrawCircle, cxVal, cyVal, 6, 0000B0E0h + invoke DrawCircle, cxVal, cyVal, 4, 0000E0FFh + mov eax, cxVal + dec eax + mov edx, cyVal + dec edx + invoke DrawCircle, eax, edx, 2, 00A0FFFFh + mov eax, cxVal + sub eax, 2 + mov edx, cyVal + sub edx, 2 + invoke DrawPixel, eax, edx, 00FFFFFFh + ret +DrawGlowingOrb ENDP +DrawGothicCup PROC uses ebx esi edi, x:DWORD, y:DWORD, cupId:DWORD, isLifted:DWORD, hasBall:DWORD + LOCAL liftY:DWORD + + mov eax, y + cmp isLifted, 0 + je @F + sub eax, 28 +@@: + mov liftY, eax + + ; Drop shadow on purple velvet + cmp isLifted, 0 + jne @F + mov eax, x + add eax, 6 + mov edx, y + add edx, 42 + invoke DrawBox, eax, edx, 36, 4, 000A0410h + jmp draw_cup_body +@@: + mov eax, x + add eax, 10 + mov edx, y + add edx, 42 + invoke DrawBox, eax, edx, 28, 2, 0012061Ah + + ; Draw orb if lifted and has ball + cmp hasBall, 0 + je draw_cup_body + mov eax, x + add eax, 24 + mov edx, y + add edx, 26 + invoke DrawGlowingOrb, eax, edx + +draw_cup_body: + ; Base + mov eax, x + add eax, 6 + mov edx, liftY + add edx, 36 + invoke DrawBoxBorder, eax, edx, 36, 6, 00221A30h, 0000B0D0h, 1 + + ; Stem mov eax, x add eax, 18 - mov edx, y - add edx, 18 - invoke DrawChar8x8, eax, edx, cupNumChar, 0000FFFFh - ret -DrawCup ENDP - -DrawGoldenBall PROC uses ebx esi edi x:DWORD, y:DWORD - invoke DrawCircle, x, y, 7, 0000A5D8h - invoke DrawCircle, x, y, 6, 0000D7FFh - invoke DrawCircle, x, y, 4, 0000FFFFh + mov edx, liftY + add edx, 28 + invoke DrawBox, eax, edx, 12, 8, 00332545h mov eax, x - sub eax, 2 - mov edx, y - sub edx, 2 - invoke DrawCircle, eax, edx, 1, 00FFFFFFh + add eax, 22 + mov edx, liftY + add edx, 28 + invoke DrawBox, eax, edx, 4, 8, 0055406Eh + + ; Bowl + mov eax, x + add eax, 8 + mov edx, liftY + add edx, 8 + invoke DrawBoxBorder, eax, edx, 32, 20, 002D1E40h, 0000D7FFh, 1 + mov eax, x + add eax, 14 + mov edx, liftY + add edx, 10 + invoke DrawBox, eax, edx, 6, 16, 00503570h + mov eax, x + add eax, 17 + mov edx, liftY + add edx, 10 + invoke DrawBox, eax, edx, 3, 16, 007850A0h + + ; Flared Rim + mov eax, x + add eax, 4 + mov edx, liftY + add edx, 2 + invoke DrawBoxBorder, eax, edx, 40, 7, 00150A20h, 0000FFFFh, 1 + mov eax, x + add eax, 12 + mov edx, liftY + add edx, 4 + invoke DrawBox, eax, edx, 16, 2, 0000FFFFh + + ; Skull Insignia on Bowl + mov eax, x + add eax, 21 + mov edx, liftY + add edx, 12 + invoke DrawBox, eax, edx, 6, 5, 0000D7FFh + mov eax, x + add eax, 22 + mov edx, liftY + add edx, 17 + invoke DrawBox, eax, edx, 4, 3, 0000D7FFh + mov eax, x + add eax, 22 + mov edx, liftY + add edx, 14 + invoke DrawPixel, eax, edx, 00150A20h + mov eax, x + add eax, 24 + mov edx, liftY + add edx, 14 + invoke DrawPixel, eax, edx, 00150A20h + + ; Cup ID Badge + mov eax, x + add eax, 21 + mov edx, liftY + add edx, 24 + mov ecx, cupId + add ecx, '0' + invoke DrawChar8x8, eax, edx, ecx, 0000FFFFh ret -DrawGoldenBall ENDP +DrawGothicCup ENDP InitSkullsGame PROC mov skulls3d_state, 0 @@ -3368,25 +3850,28 @@ InitSkullsGame PROC jge @F mov skulls3d_bet, 50 @@: + invoke RandomRange3D, 3 + mov skulls3d_ballPos, eax ret InitSkullsGame ENDP -FinalizeSkullsGuess PROC +FinalizeSkullsGuess PROC uses ebx mov skulls3d_state, 4 mov eax, skulls3d_choice cmp eax, skulls3d_ballPos jne skulls_guess_lost + ; Won 3x! mov eax, skulls3d_bet imul eax, 3 mov skulls3d_win, eax add bankroll, eax - invoke PlaySoundA, OFFSET soundJackpot, 0, 1 + invoke PlaySoundA, OFFSET soundVictory, 0, 1 ret skulls_guess_lost: mov skulls3d_win, 0 - invoke PlaySoundA, OFFSET soundClick, 0, 1 + invoke PlaySoundA, OFFSET soundLose, 0, 1 ret FinalizeSkullsGuess ENDP @@ -3396,10 +3881,10 @@ HandleSkullsInput PROC cmp skulls3d_state, 2 je skulls_input_done - cmp skulls3d_state, 0 - jne check_skulls_in_phase3 + cmp skulls3d_state, 3 + je check_skulls_pick - ; Phase 0: Idle / betting + ; State 0 or 4: Can start new round or adjust bet invoke Pressed3D, 20h ; SPACE test eax, eax jz check_skulls_bet_adjust @@ -3412,22 +3897,27 @@ HandleSkullsInput PROC invoke RandomRange3D, 3 mov skulls3d_ballPos, eax + + invoke RandomRange3D, 3 + mov skulls3d_swapPair0, eax + invoke RandomRange3D, 3 + mov skulls3d_swapPair1, eax + invoke RandomRange3D, 3 + mov skulls3d_swapPair2, eax + mov skulls3d_state, 1 mov skulls3d_timer, 0 mov skulls3d_win, 0 - invoke PlaySoundA, OFFSET soundCoin, 0, 1 + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 jmp skulls_input_done skulls_no_cash: invoke CopyString3D, OFFSET msgNeedMoney, OFFSET flashMessage mov flashTimer, 60 + invoke PlaySoundA, OFFSET soundClick, 0, 1 jmp skulls_input_done -check_skulls_in_phase3: - cmp skulls3d_state, 3 - jne check_skulls_in_phase4 - - ; Phase 3: Pick cup +check_skulls_pick: invoke Pressed3D, 31h ; '1' test eax, eax jz @F @@ -3449,14 +3939,6 @@ check_skulls_in_phase3: call FinalizeSkullsGuess jmp skulls_input_done -check_skulls_in_phase4: - ; Phase 4: Play again or adjust bet - invoke Pressed3D, 20h ; SPACE - test eax, eax - jz check_skulls_bet_adjust - mov skulls3d_state, 0 - jmp skulls_input_done - check_skulls_bet_adjust: invoke Pressed3D, 26h ; UP test eax, eax @@ -3465,6 +3947,7 @@ check_skulls_bet_adjust: test eax, eax jz check_skulls_bet_down skulls_inc_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 cmp skulls3d_bet, 500 jge skulls_input_done cmp skulls3d_bet, 250 @@ -3476,25 +3959,6 @@ skulls_inc_bet: cmp skulls3d_bet, 25 jge skulls_set_50 jmp skulls_set_25 - -check_skulls_bet_down: - invoke Pressed3D, 28h ; DOWN - test eax, eax - jnz skulls_dec_bet - invoke Pressed3D, 53h ; S - test eax, eax - jz skulls_input_done -skulls_dec_bet: - cmp skulls3d_bet, 25 - jle skulls_set_10 - cmp skulls3d_bet, 50 - jle skulls_set_25 - cmp skulls3d_bet, 100 - jle skulls_set_50 - cmp skulls3d_bet, 250 - jle skulls_set_100 - jmp skulls_set_250 - skulls_set_500: mov skulls3d_bet, 500 jmp skulls_input_done @@ -3510,26 +3974,60 @@ skulls_set_50: skulls_set_25: mov skulls3d_bet, 25 jmp skulls_input_done + +check_skulls_bet_down: + invoke Pressed3D, 28h ; DOWN + test eax, eax + jnz skulls_dec_bet + invoke Pressed3D, 53h ; S + test eax, eax + jz skulls_input_done +skulls_dec_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp skulls3d_bet, 25 + jle skulls_set_10 + cmp skulls3d_bet, 50 + jle skulls_set_25 + cmp skulls3d_bet, 100 + jle skulls_set_50 + cmp skulls3d_bet, 250 + jle skulls_set_100 + mov skulls3d_bet, 250 + jmp skulls_input_done skulls_set_10: mov skulls3d_bet, 10 - jmp skulls_input_done skulls_input_done: ret HandleSkullsInput ENDP RenderSkullsGame PROC uses ebx esi edi - local passTick:DWORD, curPair:DWORD, swapSlotA:DWORD, swapSlotB:DWORD - local xA:DWORD, xB:DWORD, curXA:DWORD, curXB:DWORD, curYA:DWORD, curYB:DWORD - local distX:DWORD, dxVal:DWORD, archY:DWORD + LOCAL passTick:DWORD, curPair:DWORD, swapSlotA:DWORD, swapSlotB:DWORD + LOCAL xA:DWORD, xB:DWORD, curXA:DWORD, curXB:DWORD, curYA:DWORD, curYB:DWORD + LOCAL distX:DWORD, dxVal:DWORD, archY:DWORD - ; Background & Bezel - invoke DrawBox, 0, 0, SCREEN_W, SCREEN_H, 00100E18h - invoke DrawBoxBorder, 10, 10, 300, 220, 001A1828h, 0000D7FFh, 3 + ; Dark Gothic Tavern Table with Royal Velvet + invoke DrawBox, 0, 0, 320, 240, 000D0612h + invoke DrawBoxBorder, 4, 4, 312, 232, 00160B20h, 00321845h, 3 + invoke DrawBoxBorder, 8, 8, 304, 224, 00220F30h, 0000D7FFh, 1 - ; Illuminated Marquee Header - invoke DrawBoxBorder, 25, 18, 270, 26, 000C0A14h, 0000FFFFh, 2 - invoke DrawString8x8, 56, 27, OFFSET textSkullsTitle, 0000D7FFh + ; Header + invoke DrawBoxBorder, 24, 12, 272, 24, 0012081Ah, 0000D7FFh, 2 + invoke DrawString8x8, 64, 20, OFFSET textSkullsTitle, 0000FFFFh + + ; Meters: Bank & Bet + invoke DrawBoxBorder, 20, 42, 114, 16, 000D0616h, 00401858h, 1 + invoke DrawString8x8, 24, 46, OFFSET textBankPrompt, 0050FF50h + invoke FormatNumber, bankroll, OFFSET hudNumBuffer + invoke DrawString8x8, 72, 46, OFFSET hudNumBuffer, 0050FF50h + + invoke DrawBoxBorder, 186, 42, 114, 16, 000D0616h, 00401858h, 1 + invoke DrawString8x8, 190, 46, OFFSET textBetPrompt, 0000D7FFh + invoke FormatNumber, skulls3d_bet, OFFSET hudNumBuffer + invoke DrawString8x8, 254, 46, OFFSET hudNumBuffer, 0000D7FFh + + ; Velvet Table Runner + invoke DrawBoxBorder, 16, 64, 288, 120, 001A0B25h, 00452060h, 1 ; Dispatch based on phase cmp skulls3d_state, 1 @@ -3542,52 +4040,31 @@ RenderSkullsGame PROC uses ebx esi edi je skulls_render_phase4 ; Phase 0: Idle / Betting - invoke DrawCup, 50, 105, '1' - invoke DrawCup, 138, 105, '2' - invoke DrawCup, 226, 105, '3' - invoke DrawString8x8, 24, 48, OFFSET textSkullsPhase0, 0000D7FFh + invoke DrawString8x8, 56, 70, OFFSET textSkullsPhase0, 0000D7FFh + invoke DrawGothicCup, 48, 108, 1, 0, 0 + invoke DrawGothicCup, 136, 108, 2, 0, 0 + invoke DrawGothicCup, 224, 108, 3, 0, 0 jmp skulls_draw_controls skulls_render_phase1: inc skulls3d_timer + invoke DrawString8x8, 56, 70, OFFSET textSkullsPhase1, 0000FFFFh - ; Draw golden ball under starting cup - mov eax, skulls3d_ballPos - cmp eax, 0 - je p1_ball_0 - cmp eax, 1 - je p1_ball_1 - invoke DrawGoldenBall, 248, 120 - jmp p1_draw_cups -p1_ball_0: - invoke DrawGoldenBall, 72, 120 - jmp p1_draw_cups -p1_ball_1: - invoke DrawGoldenBall, 160, 120 - -p1_draw_cups: - mov edx, 105 + ; Draw cups with ballPos cup lifted + mov eax, 0 cmp skulls3d_ballPos, 0 - jne @F - mov edx, 70 -@@: - invoke DrawCup, 50, edx, '1' + sete al + invoke DrawGothicCup, 48, 108, 1, eax, eax - mov edx, 105 + mov eax, 0 cmp skulls3d_ballPos, 1 - jne @F - mov edx, 70 -@@: - invoke DrawCup, 138, edx, '2' + sete al + invoke DrawGothicCup, 136, 108, 2, eax, eax - mov edx, 105 + mov eax, 0 cmp skulls3d_ballPos, 2 - jne @F - mov edx, 70 -@@: - invoke DrawCup, 226, edx, '3' - - invoke DrawString8x8, 76, 48, OFFSET textSkullsPhase1, 0000FFFFh + sete al + invoke DrawGothicCup, 224, 108, 3, eax, eax cmp skulls3d_timer, 40 jl skulls_draw_controls @@ -3595,12 +4072,7 @@ p1_draw_cups: ; Start Phase 2 (Shuffle) mov skulls3d_state, 2 mov skulls3d_timer, 0 - invoke RandomRange3D, 3 - mov skulls3d_swapPair0, eax - invoke RandomRange3D, 3 - mov skulls3d_swapPair1, eax - invoke RandomRange3D, 3 - mov skulls3d_swapPair2, eax + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 jmp skulls_draw_controls skulls_render_phase2: @@ -3637,20 +4109,20 @@ setup_pass_pair: je pair_2 mov swapSlotA, 0 mov swapSlotB, 1 - mov xA, 50 - mov xB, 138 + mov xA, 48 + mov xB, 136 jmp calc_swap_coords pair_1: mov swapSlotA, 1 mov swapSlotB, 2 - mov xA, 138 - mov xB, 226 + mov xA, 136 + mov xB, 224 jmp calc_swap_coords pair_2: mov swapSlotA, 0 mov swapSlotB, 2 - mov xA, 50 - mov xB, 226 + mov xA, 48 + mov xB, 224 calc_swap_coords: mov eax, xB @@ -3672,6 +4144,7 @@ calc_swap_coords: sub eax, dxVal mov curXB, eax + ; Arch height: archY = (t * (20 - t)) / 25 mov eax, passTick mov ecx, 20 sub ecx, eax @@ -3681,138 +4154,137 @@ calc_swap_coords: div ecx mov archY, eax - mov eax, 105 - sub eax, archY - mov curYA, eax + mov edx, 108 + sub edx, archY + mov curYA, edx ; upper arc - mov eax, 105 - add eax, archY - mov curYB, eax + mov edx, 108 + add edx, archY + mov curYB, edx ; lower arc - cmp passTick, 19 - jne @F - mov eax, skulls3d_ballPos - cmp eax, swapSlotA - jne check_ball_b - mov eax, swapSlotB - mov skulls3d_ballPos, eax - jmp ball_swap_done -check_ball_b: - cmp eax, swapSlotB - jne ball_swap_done - mov eax, swapSlotA - mov skulls3d_ballPos, eax -ball_swap_done: - invoke PlaySoundA, OFFSET soundClick, 0, 1 -@@: + invoke DrawString8x8, 56, 70, OFFSET textSkullsPhase2, 0000FFFFh + + ; Depth sorted rendering: Background cup (upper arc) draws FIRST, Foreground cup (lower arc) draws OVER it! + invoke DrawGothicCup, curXA, curYA, 1, 0, 0 + invoke DrawGothicCup, curXB, curYB, 2, 0, 0 + + ; Draw remaining static cup cmp curPair, 1 je draw_stat_0 cmp curPair, 2 je draw_stat_1 - invoke DrawCup, 226, 105, '3' - jmp draw_moving_cups + invoke DrawGothicCup, 224, 108, 3, 0, 0 + jmp check_pass_finish draw_stat_0: - invoke DrawCup, 50, 105, '1' - jmp draw_moving_cups + invoke DrawGothicCup, 48, 108, 1, 0, 0 + jmp check_pass_finish draw_stat_1: - invoke DrawCup, 138, 105, '2' + invoke DrawGothicCup, 136, 108, 2, 0, 0 -draw_moving_cups: - mov edx, '1' - add edx, swapSlotA - invoke DrawCup, curXA, curYA, edx +check_pass_finish: + cmp skulls3d_timer, 20 + je do_pass_swap + cmp skulls3d_timer, 40 + je do_pass_swap + cmp skulls3d_timer, 60 + je do_pass_swap + jmp skulls_draw_controls - mov edx, '1' - add edx, swapSlotB - invoke DrawCup, curXB, curYB, edx +do_pass_swap: + ; Update ballPos if involved in swap + mov eax, skulls3d_ballPos + cmp eax, swapSlotA + jne check_swap_b + mov eax, swapSlotB + mov skulls3d_ballPos, eax + jmp ball_swap_done +check_swap_b: + cmp eax, swapSlotB + jne ball_swap_done + mov eax, swapSlotA + mov skulls3d_ballPos, eax - invoke DrawString8x8, 40, 48, OFFSET textSkullsPhase2, 0000FFFFh +ball_swap_done: + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 cmp skulls3d_timer, 60 jl skulls_draw_controls + + ; Shuffle complete -> Move to Pick phase mov skulls3d_state, 3 mov skulls3d_timer, 0 jmp skulls_draw_controls skulls_render_phase3: - invoke DrawCup, 50, 105, '1' - invoke DrawCup, 138, 105, '2' - invoke DrawCup, 226, 105, '3' - invoke DrawString8x8, 16, 48, OFFSET textSkullsPhase3, 0000FFFFh + invoke DrawString8x8, 40, 70, OFFSET textSkullsPhase3, 0000FFFFh + invoke DrawGothicCup, 48, 108, 1, 0, 0 + invoke DrawGothicCup, 136, 108, 2, 0, 0 + invoke DrawGothicCup, 224, 108, 3, 0, 0 jmp skulls_draw_controls skulls_render_phase4: - mov eax, skulls3d_ballPos - cmp eax, 0 - je p4_ball_0 - cmp eax, 1 - je p4_ball_1 - invoke DrawGoldenBall, 248, 120 - jmp p4_draw_cups -p4_ball_0: - invoke DrawGoldenBall, 72, 120 - jmp p4_draw_cups -p4_ball_1: - invoke DrawGoldenBall, 160, 120 - -p4_draw_cups: - mov edx, 105 - cmp skulls3d_choice, 0 - je @F - cmp skulls3d_ballPos, 0 - jne draw_cup0 -@@: - mov edx, 70 -draw_cup0: - invoke DrawCup, 50, edx, '1' - - mov edx, 105 - cmp skulls3d_choice, 1 - je @F - cmp skulls3d_ballPos, 1 - jne draw_cup1 -@@: - mov edx, 70 -draw_cup1: - invoke DrawCup, 138, edx, '2' - - mov edx, 105 - cmp skulls3d_choice, 2 - je @F - cmp skulls3d_ballPos, 2 - jne draw_cup2 -@@: - mov edx, 70 -draw_cup2: - invoke DrawCup, 226, edx, '3' - + ; Reveal phase cmp skulls3d_win, 0 - je p4_splash_lose - invoke DrawString8x8, 24, 48, OFFSET textSkullsWin, 0000FFFFh + je skulls_show_loss + invoke DrawString8x8, 48, 70, OFFSET textSkullsWin, 0050FF50h invoke FormatNumber, skulls3d_win, OFFSET hudNumBuffer - invoke DrawString8x8, 236, 48, OFFSET hudNumBuffer, 0000FFFFh - jmp p4_again_hint -p4_splash_lose: - invoke DrawString8x8, 36, 48, OFFSET textSkullsLose, 000000FFh -p4_again_hint: - invoke DrawString8x8, 56, 138, OFFSET textSkullsAgain, 0000D7FFh + invoke DrawString8x8, 216, 70, OFFSET hudNumBuffer, 0050FF50h + jmp skulls_p4_cups + +skulls_show_loss: + invoke DrawString8x8, 40, 70, OFFSET textSkullsLose, 008080FFh + +skulls_p4_cups: + ; Draw chosen cup lifted + mov eax, 0 + cmp skulls3d_choice, 0 + sete al + mov edx, 0 + cmp skulls3d_ballPos, 0 + sete dl + invoke DrawGothicCup, 48, 108, 1, eax, edx + + mov eax, 0 + cmp skulls3d_choice, 1 + sete al + mov edx, 0 + cmp skulls3d_ballPos, 1 + sete dl + invoke DrawGothicCup, 136, 108, 2, eax, edx + + mov eax, 0 + cmp skulls3d_choice, 2 + sete al + mov edx, 0 + cmp skulls3d_ballPos, 2 + sete dl + invoke DrawGothicCup, 224, 108, 3, eax, edx + + invoke DrawString8x8, 64, 168, OFFSET textSkullsAgain, 0000D7FFh skulls_draw_controls: - invoke DrawBoxBorder, 20, 165, 280, 58, 00151515h, 0000D7FFh, 1 - invoke DrawString8x8, 28, 170, OFFSET textBank3D, 0000D7FFh - invoke FormatNumber, bankroll, OFFSET hudNumBuffer - invoke DrawString8x8, 88, 170, OFFSET hudNumBuffer, 0000FFFFh + invoke DrawBox, 16, 190, 288, 1, 003A1850h - invoke DrawString8x8, 180, 170, OFFSET textBet3D, 0000D7FFh - invoke FormatNumber, skulls3d_bet, OFFSET hudNumBuffer - invoke DrawString8x8, 236, 170, OFFSET hudNumBuffer, 0050FF50h + mov eax, 0 + cmp skulls3d_choice, 0 + sete al + invoke DrawKeycap3D, 18, 200, OFFSET textSkullsKey1, eax - invoke DrawString8x8, 24, 186, OFFSET textSkullsControls1, 00FFFFFFh - invoke DrawString8x8, 24, 202, OFFSET textSkullsControls2, 00A0A0A0h + mov eax, 0 + cmp skulls3d_choice, 1 + sete al + invoke DrawKeycap3D, 82, 200, OFFSET textSkullsKey2, eax + + mov eax, 0 + cmp skulls3d_choice, 2 + sete al + invoke DrawKeycap3D, 146, 200, OFFSET textSkullsKey3, eax + + invoke DrawKeycap3D, 212, 200, OFFSET textSkullsKeySpace, 1 + invoke DrawKeycap3D, 274, 200, OFFSET textKeyEsc, 0 ret RenderSkullsGame ENDP -; --- Player Input & Movement Physics --- HandlePlayerInput PROC uses ebx esi edi local speed:DWORD local nextX:DWORD diff --git a/docs/previews_minigames/coin_preview.png b/docs/previews_minigames/coin_preview.png new file mode 100644 index 0000000..64d7668 Binary files /dev/null and b/docs/previews_minigames/coin_preview.png differ diff --git a/docs/previews_minigames/dice_preview.png b/docs/previews_minigames/dice_preview.png new file mode 100644 index 0000000..a9758b6 Binary files /dev/null and b/docs/previews_minigames/dice_preview.png differ diff --git a/docs/previews_minigames/skulls_preview.png b/docs/previews_minigames/skulls_preview.png new file mode 100644 index 0000000..400d5bd Binary files /dev/null and b/docs/previews_minigames/skulls_preview.png differ diff --git a/docs/previews_minigames/test_basic.png b/docs/previews_minigames/test_basic.png new file mode 100644 index 0000000..214322c Binary files /dev/null and b/docs/previews_minigames/test_basic.png differ diff --git a/poker.exe b/poker.exe index fd5115f..dfd7820 100644 Binary files a/poker.exe and b/poker.exe differ diff --git a/tools/README.md b/tools/README.md index 482ffda..9ffc82e 100644 --- a/tools/README.md +++ b/tools/README.md @@ -43,6 +43,7 @@ Offline build tools that process raw images, generate lookup tables, and compose | `build_all_3d.py` | Python | Offline assembly file generator that synthesizes the complete `casino3d.inc` raycaster engine in CP1251. | | `generate_unified_casino3d.py` | Python | Alternative unified generator for `casino3d.inc` with full sprite definitions and game interaction hooks. | | `generate_casino3d.py`
`build_engine.py`
`make_casino3d.py` | Python | Lightweight orchestration scripts for triggering `casino3d.inc` regeneration in CP1251 encoding. | +| `generate_rich_minigames.py` | Python | Patches `casino3d.inc` with rich, custom 2.5D graphics and fixed audio for Coin, Craps, and Skulls. | | `patch_poker_asm.py` | Python | Helper utility to patch `poker.asm` with 3D engine hooks, player save anchors, and mutex management. | --- @@ -53,7 +54,9 @@ Verification scripts, simulation harnesses, and runtime helpers: | Tool | Language | Purpose & Usage | |------|----------|-----------------| +| `test_minigames_render.py` | Python (Pillow) | Visual simulation test suite that rasterizes 2.5D minigame UI frames and saves PNG previews to `docs/previews_minigames/`. Usage: `python tools/tests/test_minigames_render.py`. | | `simulate_render.py` | Python (Pillow) | Software CPU simulation of the 3D raycaster renderer. Reads `casino3d_data.inc` and texture binary, performs DDA raycasting, depth-sorting of 28 casino sprites, and saves the rendered view to `docs/render_view.png`. Usage: `python tools/tests/simulate_render.py`. | | `release_smoke_check.ps1` | PowerShell | Automated smoke test for release readiness: verifies file presence, launcher SHA-256 hashes, CP1251 source markers, and absence of deprecated assets. Usage: `powershell -File tools/tests/release_smoke_check.ps1`. | | `build_graphics.ps1` | PowerShell | Builds the Windows subsystem executable `build/graphics/poker.exe` using MASM32/MSVC toolchain. Usage: `powershell -File tools/tests/build_graphics.ps1`. | | `achievement_popup.bat` | Batch (CP1251) | Standalone console achievement notification popup called by `poker.exe`. Reads achievement text from `runtime/achievement.txt` and renders a decorative banner with rarity colors (COMMON, RARE, EPIC, LEGENDARY). | + diff --git a/tools/generators/generate_rich_minigames.py b/tools/generators/generate_rich_minigames.py new file mode 100644 index 0000000..739d449 --- /dev/null +++ b/tools/generators/generate_rich_minigames.py @@ -0,0 +1,1790 @@ +# -*- coding: utf-8 -*- +""" +Generator script to patch casino3d.inc with rich, custom 2.5D graphics +and fixed audio for Coin Flip, Craps/Dice, and Skulls & Cups. +""" +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +CASINO_INC = REPO_ROOT / 'casino3d.inc' + +# Read original casino3d.inc +with open(CASINO_INC, 'rb') as f: + raw_content = f.read() + +# 1. Fix Audio Declarations (around line 150) +old_audio_block = ( + b'soundCoin db "assets\\audio\\\xc7\xe2\xee\xed \xef\xe0\xe4\xe0\xfe\xf9\xe5\xe9 \xec\xee\xed\xe5\xf2\xfb.wav",0\r\n' + b'soundCards db "assets\\audio\\playing_cards_deck_flick_through_001_20485.wav",0\r\n' + b'soundRoulette db "assets\\audio\\roulette-wheel-throw-2.wav",0\r\n' + b'soundJackpot db "assets\\audio\\\xc4\xe6\xe5\xea\xef\xee\xf2.wav",0\r\n' + b'soundClick db "assets\\audio\\b05e1ac8be34248.wav",0' +) + +new_audio_block = ( + b'soundCoin db "assets\\audio\\\xc7\xe2\xf3\xea \xec\xee\xed\xe5\xf2\xfb \xe1\xf0\xee\xf8\xe5\xed\xed\xee\xe9 \xe2 \xe2\xee\xe7\xe4\xf3\xf5 \xe8 \xf3\xef\xe0\xe2\xf8\xe5\xe9 \xed\xe0 \xef\xee\xe2\xe5\xf0\xf5\xed\xee\xf1\xf2\xfc.wav",0\r\n' + b'soundCoinDing db "assets\\audio\\\xc7\xe2\xf3\xea \xec\xee\xed\xe5\xf2\xfb \xe2 \xe8\xe3\xf0\xe5 Ultrakill.wav",0\r\n' + b'soundCards db "assets\\audio\\playing_cards_deck_flick_through_001_20485.wav",0\r\n' + b'soundRoulette db "assets\\audio\\roulette-wheel-throw-2.wav",0\r\n' + b'soundJackpot db "assets\\audio\\\xc4\xe6\xfd\xea \xef\xee\xf2.wav",0\r\n' + b'soundVictory db "assets\\audio\\victory-tf2.wav",0\r\n' + b'soundClick db "assets\\audio\\revolverdryfirec.wav",0\r\n' + b'soundShuffle db "assets\\audio\\moving-stone.wav",0\r\n' + b'soundLose db "assets\\audio\\\xcf\xf0\xee\xe8\xe3\xf0\xfb\xf8.wav",0' +) + +if old_audio_block in raw_content: + raw_content = raw_content.replace(old_audio_block, new_audio_block, 1) + print("[OK] Replaced audio block (removed gunshots, fixed real sound paths)") +else: + print("[WARN] Audio block pattern not found verbatim, checking sub-patterns...") + if b'soundClick db "assets\\audio\\b05e1ac8be34248.wav",0' in raw_content: + raw_content = raw_content.replace( + b'soundClick db "assets\\audio\\b05e1ac8be34248.wav",0', + b'soundClick db "assets\\audio\\revolverdryfirec.wav",0\r\n' + b'soundShuffle db "assets\\audio\\moving-stone.wav",0\r\n' + b'soundLose db "assets\\audio\\\xcf\xf0\xee\xe8\xe3\xf0\xfb\xf8.wav",0\r\n' + b'soundVictory db "assets\\audio\\victory-tf2.wav",0\r\n' + b'soundCoinDing db "assets\\audio\\\xc7\xe2\xf3\xea \xec\xee\xed\xe5\xf2\xfb \xe2 \xe8\xe3\xf0\xe5 Ultrakill.wav",0' + ) + print("[OK] Replaced soundClick and added soundShuffle / soundLose") + +# 2. Update string constants (around line 290) +# Find start of coinRxAnimTable and end before shopMsg +pos_coin_table = raw_content.find(b'coinRxAnimTable db') +pos_shop_msg = raw_content.find(b'shopMsg db 128 dup(0)') + +assert pos_coin_table != -1 and pos_shop_msg != -1, "Could not locate text strings section" + +new_strings_text = """coinRxAnimTable db 26, 22, 16, 6, 16, 22, 26, 22, 16, 6 + +; --- Native 2.5D Coin Flip --- +textCoinTitle db "* * * \xc7 \xce \xcb \xce \xd2 \xc0 \xdf \xcc \xce \xcd \xc5 \xd2 \xc0 * * *",0 +textCoinHeads db "\xce\xd0\xa8\xcb",0 +textCoinTails db "\xd0\xc5\xd8\xca\xc0",0 +textCoinStreak db "\xd1\xc5\xd0\xc8\xdf: ",0 +textCoinSide1 db "[1] \xce\xd0\xa8\xcb",0 +textCoinSide2 db "[2] \xd0\xc5\xd8\xca\xc0",0 +textCoinFlipping db "\xcc\xce\xcd\xc5\xd2\xc0 \xc2 \xcf\xce\xcb\xa8\xd2\xc5...",0 +textCoinWin db "\xc2\xdb\xc8\xc3\xd0\xdb\xd8: +$",0 +textCoinLose db "\xcd\xc5 \xd3\xc3\xc0\xc4\xc0\xcb!",0 +textCoinKey1 db "1:\xce\xd0\xa8\xcb",0 +textCoinKey2 db "2:\xd0\xc5\xd8\xca\xc0",0 +textCoinKeySpace db "\xcf\xd0\xce\xc1\xc5\xcb:\xc1\xd0\xce\xd1\xce\xca",0 +textCoinStreakFmt db "\xd1\xc5\xd0\xc8\xdf: [X] [X] [X] [ ] [ ] x4!",0 + +; --- Native 2.5D Dice Game --- +dice3d_bet dd 50 +dice3d_choice dd 1 ; 1 = \xd7\xb8\xf2, 2 = \xcd\xe5\xf7\xe5\xf2 +dice3d_state dd 0 ; 0 = Idle, 1 = Rolling, 2 = Result +dice3d_timer dd 0 +dice3d_val1 dd 4 +dice3d_val2 dd 6 +dice3d_sum dd 10 +dice3d_win dd 0 + +textDiceTitle db "=== G R A N D C R A P S ===",0 +textDiceSubtitle db "[ PASS LINE ] [ ANY CRAPS ]",0 +textDiceEven db "[1] \xd7\xa8\xd2",0 +textDiceOdd db "[2] \xcd\xc5\xd7\xc5\xd2",0 +textDiceEvenNums db "2 4 6 8 10",0 +textDiceOddNums db "3 5 7 9 11",0 +textDiceSumHdr db "\xd1\xd3\xcc\xcc\xc0: ",0 +textDicePlus db " + ",0 +textDiceEqual db " = ",0 +textDiceEvenResult db " (\xd7\xa8\xd2)",0 +textDiceOddResult db " (\xcd\xc5\xd7\xc5\xd2)",0 +textDiceRolling db "\xca\xce\xd1\xd2\xc8 \xcb\xc5\xd2\xdf\xd2 \xcf\xce \xd1\xd3\xca\xcd\xd3...",0 +textDiceWin db "\xc2\xdb\xc8\xc3\xd0\xdb\xd8: +$",0 +textDiceWinMult db "! (x2)",0 +textDiceLose db "\xcf\xd0\xce\xc8\xc3\xd0\xdb\xd8!",0 +textDiceKey1 db "1:\xd7\xa8\xd2",0 +textDiceKey2 db "2:\xcd\xc5\xd7\xc5\xd2",0 +textDiceKeySpace db "\xcf\xd0\xce\xc1\xc5\xcb:\xc1\xd0\xce\xd1\xce\xca",0 + +; --- Native 2.5D Skulls Game --- +skulls3d_bet dd 50 +skulls3d_state dd 0 ; 0 = Idle, 1 = Show ball, 2 = Shuffle, 3 = Pick, 4 = Reveal +skulls3d_timer dd 0 +skulls3d_ballPos dd 1 ; 0, 1, 2 +skulls3d_choice dd 1 ; 0, 1, 2 +skulls3d_win dd 0 +skulls3d_swapPair0 dd 0 +skulls3d_swapPair1 dd 1 +skulls3d_swapPair2 dd 2 + +textSkullsTitle db "+ + + \xcd \xc0 \xcf \xa8 \xd0 \xd1 \xd2 \xca \xc8 + + +",0 +textSkullsPhase0 db "\xc4\xc5\xcb\xc0\xc9 \xd1\xd2\xc0\xc2\xca\xd3 \xc8 \xc6\xcc\xc8 [\xcf\xd0\xce\xc1\xc5\xcb]",0 +textSkullsPhase1 db "\xd1\xcc\xce\xd2\xd0\xc8, \xc3\xc4\xc5 \xc7\xce\xcb\xce\xd2\xce\xc9 \xd8\xc0\xd0...",0 +textSkullsPhase2 db "\xca\xd0\xd3\xd7\xd3, \xc2\xc5\xd0\xd7\xd3, \xc7\xc0\xcf\xd3\xd2\xc0\xd2\xdc \xd5\xce\xd7\xd3...",0 +textSkullsPhase3 db "\xc3\xc4\xc5 \xd8\xc0\xd0? \xc2\xdb\xc1\xc8\xd0\xc0\xc9: [1], [2] \xc8\xcb\xc8 [3]!",0 +textSkullsWin db "\xc2\xc5\xd0\xcd\xce! \xc2\xdb\xcf\xcb\xc0\xd2\xc0 x3! +$",0 +textSkullsLose db "\xcc\xc8\xcc\xce! \xd8\xc0\xd0 \xc1\xdb\xcb \xc2 \xc4\xd0\xd3\xc3\xce\xcc \xcc\xc5\xd1\xd2\xc5!",0 +textSkullsAgain db "[\xcf\xd0\xce\xc1\xc5\xcb] - \xd1\xdb\xc3\xd0\xc0\xd2\xdc \xc5\xd9\xa8 \xd0\xc0\xc7",0 +textSkullsKey1 db "1:\xca\xd3\xc1\xce\xca",0 +textSkullsKey2 db "2:\xca\xd3\xc1\xce\xca",0 +textSkullsKey3 db "3:\xca\xd3\xc1\xce\xca",0 +textSkullsKeySpace db "\xcf\xd0\xce\xc1\xc5\xcb",0 + +; --- Shared UI Strings --- +textKeyEsc db "ESC",0 +textBankPrompt db "\xc1\xc0\xcd\xca: $",0 +textBetPrompt db "\xd1\xd2\xc0\xc2\xca\xc0: $",0 +textBetHint db " [W/S]",0 + +""" + +raw_content = raw_content[:pos_coin_table] + new_strings_text.encode('latin1') + raw_content[pos_shop_msg:] +print("[OK] Replaced string constants section with rich game strings") + +# 3. Replace procedural drawing and rendering block +# Find start of '; IN-ENGINE 2.5D MINI-GAME: COIN FLIP' and end before 'HandlePlayerInput PROC' +pos_games_start = raw_content.find(b'; IN-ENGINE 2.5D MINI-GAME: COIN FLIP') +pos_player_input = raw_content.find(b'HandlePlayerInput PROC') + +assert pos_games_start != -1 and pos_player_input != -1, "Could not locate games procedures section" + +new_games_code = r"""; ============================================================================== +; SHARED RICH UI PROCEDURES +; ============================================================================== +DrawKeycap3D PROC uses ebx esi edi, x:DWORD, y:DWORD, strPtr:DWORD, isActive:DWORD + LOCAL capW:DWORD + ; Compute string length + mov esi, strPtr + xor ecx, ecx +@@: + mov al, [esi+ecx] + test al, al + jz @F + inc ecx + jmp @B +@@: + shl ecx, 3 ; length * 8 + add ecx, 8 ; padding 8px + mov capW, ecx + + ; Drop shadow + mov eax, x + mov edx, y + add edx, 2 + invoke DrawBox, eax, edx, capW, 13, 0008080Ch + + ; Body & Border + mov eax, 00606870h ; default border + mov edx, 001A1C22h ; default fill + cmp isActive, 0 + je @F + mov eax, 0000FFFFh ; active gold border + mov edx, 001B4B22h ; active dark green fill +@@: + invoke DrawBoxBorder, x, y, capW, 13, edx, eax, 1 + + ; String text + mov eax, 00D0D8E0h + cmp isActive, 0 + je @F + mov eax, 0000FFFFh +@@: + mov edx, x + add edx, 4 + mov ecx, y + add ecx, 3 + invoke DrawString8x8, edx, ecx, strPtr, eax + mov eax, capW + ret +DrawKeycap3D ENDP + +DrawChipStack PROC uses ebx esi edi, x:DWORD, y:DWORD, chipCount:DWORD + xor esi, esi +chip_loop: + cmp esi, chipCount + jge chip_done + mov eax, esi + shl eax, 2 ; esi * 4 + mov edx, y + sub edx, eax ; cy = y - esi*4 + + ; Shadow + mov eax, x + add eax, 2 + mov ecx, edx + add ecx, 5 + invoke DrawBox, eax, ecx, 20, 2, 00041006h + + ; Chip body + invoke DrawBoxBorder, x, edx, 24, 6, 001515B8h, 00FFFFFFh, 1 + + ; White stripe inlays + mov eax, x + add eax, 4 + mov ecx, edx + add ecx, 1 + invoke DrawBox, eax, ecx, 3, 4, 00FFFFFFh + mov eax, x + add eax, 17 + invoke DrawBox, eax, ecx, 3, 4, 00FFFFFFh + + inc esi + jmp chip_loop +chip_done: + ret +DrawChipStack ENDP + +; ============================================================================== +; IN-ENGINE 2.5D MINI-GAME: COIN FLIP (МО�ЕТК�) +; ============================================================================== +DrawDetailedCoin PROC uses ebx esi edi, cxVal:DWORD, cyVal:DWORD, rxVal:DWORD, ryVal:DWORD, outerColor:DWORD, innerColor:DWORD, reliefType:DWORD + LOCAL curDY:DWORD, curDX:DWORD, rxSq:DWORD, rySq:DWORD, rxRySq:DWORD, threshSq:DWORD + cmp rxVal, 0 + jle coin_draw_ret + cmp ryVal, 0 + jle coin_draw_ret + + mov eax, rxVal + imul eax, eax + mov rxSq, eax + + mov eax, ryVal + imul eax, eax + mov rySq, eax + + mov eax, rxSq + imul eax, rySq + mov rxRySq, eax + + mov eax, rxRySq + shr eax, 2 + imul eax, 3 + mov threshSq, eax + + mov eax, ryVal + neg eax + mov curDY, eax + +coin_y_loop: + mov eax, curDY + cmp eax, ryVal + jg check_coin_relief + + mov eax, rxVal + neg eax + mov curDX, eax + +coin_x_loop: + mov eax, curDX + cmp eax, rxVal + jg next_coin_y + + mov eax, curDX + imul eax, eax + imul eax, rySq + mov ebx, curDY + imul ebx, ebx + imul ebx, rxSq + add eax, ebx + cmp eax, rxRySq + jg next_coin_x + + mov ecx, innerColor + cmp eax, threshSq + jb @F + mov ecx, outerColor +@@: + mov eax, cxVal + add eax, curDX + mov edx, cyVal + add edx, curDY + invoke DrawPixel, eax, edx, ecx + +next_coin_x: + inc curDX + jmp coin_x_loop + +next_coin_y: + inc curDY + jmp coin_y_loop + +check_coin_relief: + cmp rxVal, 14 + jl coin_draw_ret + cmp reliefType, 1 + je draw_eagle_relief + cmp reliefType, 2 + je draw_wreath_relief + jmp coin_draw_ret + +draw_eagle_relief: + ; Crown + mov eax, cxVal + sub eax, 3 + mov edx, cyVal + sub edx, 14 + invoke DrawBox, eax, edx, 7, 2, 00007A9Ah + mov eax, cxVal + sub eax, 4 + mov edx, cyVal + sub edx, 12 + invoke DrawBox, eax, edx, 9, 2, 00007A9Ah + + ; Body & Head + mov eax, cxVal + sub eax, 2 + mov edx, cyVal + sub edx, 9 + invoke DrawBox, eax, edx, 5, 12, 00007A9Ah + mov eax, cxVal + dec eax + mov edx, cyVal + sub edx, 8 + invoke DrawBox, eax, edx, 3, 10, 0000D7FFh + + ; Wings + mov eax, cxVal + sub eax, 10 + mov edx, cyVal + sub edx, 8 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + mov eax, cxVal + sub eax, 13 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 10, 3, 00007A9Ah + mov eax, cxVal + sub eax, 11 + mov edx, cyVal + sub edx, 2 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + + mov eax, cxVal + add eax, 4 + mov edx, cyVal + sub edx, 8 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + mov eax, cxVal + add eax, 4 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 10, 3, 00007A9Ah + mov eax, cxVal + add eax, 5 + mov edx, cyVal + sub edx, 2 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + + ; Tail + mov eax, cxVal + sub eax, 4 + mov edx, cyVal + add edx, 4 + invoke DrawBox, eax, edx, 9, 3, 00007A9Ah + mov eax, cxVal + sub eax, 3 + mov edx, cyVal + add edx, 7 + invoke DrawBox, eax, edx, 7, 3, 00007A9Ah + jmp coin_draw_ret + +draw_wreath_relief: + ; Roman numeral 'I' + mov eax, cxVal + sub eax, 5 + mov edx, cyVal + sub edx, 10 + invoke DrawBox, eax, edx, 11, 3, 00007A9Ah + mov eax, cxVal + sub eax, 2 + mov edx, cyVal + sub edx, 7 + invoke DrawBox, eax, edx, 5, 14, 00007A9Ah + mov eax, cxVal + dec eax + mov edx, cyVal + sub edx, 6 + invoke DrawBox, eax, edx, 3, 12, 0000D7FFh + mov eax, cxVal + sub eax, 6 + mov edx, cyVal + add edx, 7 + invoke DrawBox, eax, edx, 13, 3, 00007A9Ah + + ; Laurel dots left & right + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + sub edx, 10 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + sub edx, 10 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + sub edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + + mov eax, cxVal + sub eax, 12 + mov edx, cyVal + add edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + mov eax, cxVal + add eax, 11 + mov edx, cyVal + add edx, 5 + invoke DrawBox, eax, edx, 2, 2, 00007A9Ah + +coin_draw_ret: + ret +DrawDetailedCoin ENDP + +InitCoinGame PROC + mov coin3d_state, 0 + mov coin3d_timer, 0 + mov coin3d_win, 0 + cmp coin3d_bet, 10 + jge @F + mov coin3d_bet, 50 +@@: + ret +InitCoinGame ENDP + +HandleCoinInput PROC + cmp coin3d_state, 1 + je coin_input_done + + invoke Pressed3D, 20h ; SPACE -> Flip! + test eax, eax + jz check_coin_side + mov eax, bankroll + cmp eax, coin3d_bet + jl coin_no_cash + sub eax, coin3d_bet + mov bankroll, eax + mov coin3d_state, 1 + mov coin3d_timer, 0 + mov coin3d_win, 0 + invoke PlaySoundA, OFFSET soundCoin, 0, 1 + jmp coin_input_done + +coin_no_cash: + invoke CopyString3D, OFFSET msgNeedMoney, OFFSET flashMessage + mov flashTimer, 60 + invoke PlaySoundA, OFFSET soundClick, 0, 1 + jmp coin_input_done + +check_coin_side: + invoke Pressed3D, 31h ; '1' + test eax, eax + jz @F + mov coin3d_side, 1 + invoke PlaySoundA, OFFSET soundClick, 0, 1 +@@: + invoke Pressed3D, 32h ; '2' + test eax, eax + jz check_coin_bet_adjust + mov coin3d_side, 2 + invoke PlaySoundA, OFFSET soundClick, 0, 1 + +check_coin_bet_adjust: + invoke Pressed3D, 26h ; UP + test eax, eax + jnz coin_inc_bet + invoke Pressed3D, 57h ; W + test eax, eax + jz check_coin_bet_down +coin_inc_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp coin3d_bet, 500 + jge coin_input_done + cmp coin3d_bet, 250 + jge coin_set_500 + cmp coin3d_bet, 100 + jge coin_set_250 + cmp coin3d_bet, 50 + jge coin_set_100 + cmp coin3d_bet, 25 + jge coin_set_50 + jmp coin_set_25 +coin_set_500: + mov coin3d_bet, 500 + jmp coin_input_done +coin_set_250: + mov coin3d_bet, 250 + jmp coin_input_done +coin_set_100: + mov coin3d_bet, 100 + jmp coin_input_done +coin_set_50: + mov coin3d_bet, 50 + jmp coin_input_done +coin_set_25: + mov coin3d_bet, 25 + jmp coin_input_done + +check_coin_bet_down: + invoke Pressed3D, 28h ; DOWN + test eax, eax + jnz coin_dec_bet + invoke Pressed3D, 53h ; S + test eax, eax + jz coin_input_done +coin_dec_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp coin3d_bet, 25 + jle coin_set_10 + cmp coin3d_bet, 50 + jle coin_set_25 + cmp coin3d_bet, 100 + jle coin_set_50 + cmp coin3d_bet, 250 + jle coin_set_100 + mov coin3d_bet, 250 + jmp coin_input_done +coin_set_10: + mov coin3d_bet, 10 + +coin_input_done: + ret +HandleCoinInput ENDP + +RenderCoinGame PROC uses ebx esi edi + LOCAL cyVal:DWORD, rxVal:DWORD, curX:DWORD + + ; Cabinet Bezel (Mahogany wood & leather) + invoke DrawBox, 0, 0, 320, 240, 00060914h + invoke DrawBoxBorder, 6, 6, 308, 228, 000F1522h, 001B2E48h, 3 + + ; Corner Brass Rivets + invoke DrawBox, 12, 12, 3, 3, 0000D7FFh + invoke DrawPixel, 13, 13, 00FFFFFFh + invoke DrawBox, 306, 12, 3, 3, 0000D7FFh + invoke DrawPixel, 307, 13, 00FFFFFFh + invoke DrawBox, 12, 226, 3, 3, 0000D7FFh + invoke DrawPixel, 13, 227, 00FFFFFFh + invoke DrawBox, 306, 226, 3, 3, 0000D7FFh + invoke DrawPixel, 307, 227, 00FFFFFFh + + ; Illuminated Marquee Header + invoke DrawBoxBorder, 24, 12, 272, 26, 000A101Ch, 0000D7FFh, 2 + invoke DrawString8x8, 48, 21, OFFSET textCoinTitle, 0000FFFFh + + ; Bulb lights on marquee border + mov curX, 28 +coin_bulbs: + cmp curX, 292 + jge coin_meters + invoke DrawPixel, curX, 13, 0000FFFFh + invoke DrawPixel, curX, 36, 0000D7FFh + add curX, 12 + jmp coin_bulbs + +coin_meters: + ; Status Meters: Bank & Bet + invoke DrawBoxBorder, 20, 44, 114, 18, 000A1A0Eh, 001A4B22h, 1 + invoke DrawString8x8, 24, 49, OFFSET textBankPrompt, 0050FF50h + invoke FormatNumber, bankroll, OFFSET hudNumBuffer + invoke DrawString8x8, 72, 49, OFFSET hudNumBuffer, 0050FF50h + + invoke DrawBoxBorder, 186, 44, 114, 18, 001A100Ah, 003A2814h, 1 + invoke DrawString8x8, 190, 49, OFFSET textBetPrompt, 0000D7FFh + invoke FormatNumber, coin3d_bet, OFFSET hudNumBuffer + invoke DrawString8x8, 254, 49, OFFSET hudNumBuffer, 0000D7FFh + invoke DrawString8x8, 278, 49, OFFSET textBetHint, 00808080h + + ; Center Velvet Platter + invoke DrawCircle, 160, 110, 46, 000090C0h + invoke DrawCircle, 160, 110, 44, 0000D7FFh + invoke DrawCircle, 160, 110, 41, 00180824h + + ; Selection Podiums: [1] ОР�Л + mov eax, 00121620h + mov edx, 00404855h + mov ecx, 0090A0B0h + cmp coin3d_side, 1 + jne @F + mov eax, 001C3822h + mov edx, 0000FFFFh + mov ecx, 0000FFFFh +@@: + push ecx + invoke DrawBoxBorder, 20, 72, 76, 52, eax, edx, 2 + pop ecx + invoke DrawString8x8, 28, 78, OFFSET textCoinSide1, ecx + invoke DrawDetailedCoin, 58, 106, 12, 12, 0000A0D0h, 0000D7FFh, 1 + + ; Selection Podiums: [2] РЕШК� + mov eax, 00121620h + mov edx, 00404855h + mov ecx, 0090A0B0h + cmp coin3d_side, 2 + jne @F + mov eax, 001C3822h + mov edx, 0000FFFFh + mov ecx, 0000FFFFh +@@: + push ecx + invoke DrawBoxBorder, 224, 72, 76, 52, eax, edx, 2 + pop ecx + invoke DrawString8x8, 232, 78, OFFSET textCoinSide2, ecx + invoke DrawDetailedCoin, 262, 106, 12, 12, 000080A0h, 0000B0D0h, 2 + + ; Flipping animation logic + cmp coin3d_state, 1 + jne coin_at_rest + + inc coin3d_timer + + ; Parabolic toss: cy = 110 - (t * (40 - t)) / 14 + mov eax, coin3d_timer + mov ecx, 40 + sub ecx, eax + imul eax, ecx + xor edx, edx + mov ecx, 14 + div ecx + mov edx, 110 + sub edx, eax + mov cyVal, edx + + ; Width rx from lookup table: (t % 10) + mov eax, coin3d_timer + xor edx, edx + mov ecx, 10 + div ecx + movzx eax, BYTE PTR [coinRxAnimTable + edx] + mov rxVal, eax + + invoke DrawDetailedCoin, 160, cyVal, rxVal, 26, 0000A0D0h, 0000D7FFh, 0 + invoke DrawString8x8, 88, 160, OFFSET textCoinFlipping, 0000FFFFh + + cmp coin3d_timer, 40 + jl coin_draw_controls + + ; Flip complete! + mov coin3d_state, 2 + invoke RandomRange3D, 2 + inc eax + mov coin3d_result, eax + + mov eax, coin3d_result + cmp eax, coin3d_side + jne coin_flip_lost + + ; Won! + mov eax, coin3d_bet + shl eax, 1 + mov coin3d_win, eax + add bankroll, eax + inc coin3d_streak + cmp coin3d_streak, 3 + jl @F + invoke PlaySoundA, OFFSET soundVictory, 0, 1 + jmp coin_draw_controls +@@: + invoke PlaySoundA, OFFSET soundCoinDing, 0, 1 + jmp coin_draw_controls + +coin_flip_lost: + mov coin3d_streak, 0 + mov coin3d_win, 0 + invoke PlaySoundA, OFFSET soundLose, 0, 1 + jmp coin_draw_controls + +coin_at_rest: + ; Draw coin resting in velvet dish + mov edx, coin3d_side + cmp coin3d_state, 2 + jne @F + mov edx, coin3d_result +@@: + invoke DrawDetailedCoin, 160, 110, 26, 26, 0000A0D0h, 0000D7FFh, edx + + ; Outcome / Streak Display + cmp coin3d_state, 2 + jne coin_show_streak + + cmp coin3d_win, 0 + je coin_show_lose + invoke DrawBoxBorder, 44, 154, 232, 20, 000E141Eh, 0050FF50h, 1 + invoke DrawString8x8, 64, 160, OFFSET textCoinWin, 0050FF50h + invoke FormatNumber, coin3d_win, OFFSET hudNumBuffer + invoke DrawString8x8, 152, 160, OFFSET hudNumBuffer, 0050FF50h + jmp coin_draw_controls + +coin_show_lose: + invoke DrawBoxBorder, 44, 154, 232, 20, 000E141Eh, 000000FFh, 1 + invoke DrawString8x8, 114, 160, OFFSET textCoinLose, 008080FFh + jmp coin_draw_controls + +coin_show_streak: + invoke DrawBoxBorder, 44, 154, 232, 20, 000E141Eh, 0000D7FFh, 1 + invoke DrawString8x8, 52, 160, OFFSET textCoinStreakFmt, 0000FFFFh + +coin_draw_controls: + ; Keycaps Control Bar + invoke DrawBox, 16, 194, 288, 1, 001E2E40h + + mov eax, 0 + cmp coin3d_side, 1 + sete al + invoke DrawKeycap3D, 22, 202, OFFSET textCoinKey1, eax + + mov eax, 0 + cmp coin3d_side, 2 + sete al + invoke DrawKeycap3D, 88, 202, OFFSET textCoinKey2, eax + + invoke DrawKeycap3D, 160, 202, OFFSET textCoinKeySpace, 1 + invoke DrawKeycap3D, 274, 202, OFFSET textKeyEsc, 0 + ret +RenderCoinGame ENDP + +; ============================================================================== +; IN-ENGINE 2.5D MINI-GAME: DICE / CRAPS (КОСТИ) +; ============================================================================== +DrawCrapsDie PROC uses ebx esi edi, x:DWORD, y:DWORD, sizeVal:DWORD, valNum:DWORD + ; Drop shadow on green felt + mov eax, x + add eax, 3 + mov edx, y + add edx, sizeVal + invoke DrawBox, eax, edx, sizeVal, 3, 00031408h + mov eax, x + add eax, sizeVal + mov edx, y + add edx, 3 + invoke DrawBox, eax, edx, 3, sizeVal, 00031408h + + ; Ivory body & dark border + invoke DrawBoxBorder, x, y, sizeVal, sizeVal, 00F0F4FAh, 00353540h, 1 + + ; Bevel highlight top & left + mov eax, sizeVal + sub eax, 2 + mov edx, x + inc edx + mov ecx, y + inc ecx + invoke DrawBox, edx, ecx, eax, 1, 00FFFFFFh + invoke DrawBox, edx, ecx, 1, eax, 00FFFFFFh + + ; Bevel shadow bottom & right + mov edx, x + inc edx + mov ecx, y + add ecx, sizeVal + sub ecx, 2 + invoke DrawBox, edx, ecx, eax, 1, 00C0C6D0h + mov edx, x + add edx, sizeVal + sub edx, 2 + mov ecx, y + inc ecx + invoke DrawBox, edx, ecx, 1, eax, 00C0C6D0h + + ; Casino Ruby Red circular pips (radius 3) + cmp valNum, 1 + je draw_die_c + cmp valNum, 3 + je draw_die_c + cmp valNum, 5 + je draw_die_c + jmp check_die_pips_corners + +draw_die_c: + mov eax, x + add eax, 18 + mov edx, y + add edx, 18 + invoke DrawCircle, eax, edx, 3, 001515C8h + dec eax + dec edx + invoke DrawPixel, eax, edx, 00FFFFFFh + +check_die_pips_corners: + cmp valNum, 2 + jl die_pips_done + mov eax, x + add eax, 9 + mov edx, y + add edx, 9 + invoke DrawCircle, eax, edx, 3, 001515C8h + mov eax, x + add eax, sizeVal + sub eax, 10 + mov edx, y + add edx, sizeVal + sub edx, 10 + invoke DrawCircle, eax, edx, 3, 001515C8h + + cmp valNum, 4 + jl die_pips_done + mov eax, x + add eax, sizeVal + sub eax, 10 + mov edx, y + add edx, 9 + invoke DrawCircle, eax, edx, 3, 001515C8h + mov eax, x + add eax, 9 + mov edx, y + add edx, sizeVal + sub edx, 10 + invoke DrawCircle, eax, edx, 3, 001515C8h + + cmp valNum, 6 + jl die_pips_done + mov eax, x + add eax, 9 + mov edx, y + add edx, 18 + invoke DrawCircle, eax, edx, 3, 001515C8h + mov eax, x + add eax, sizeVal + sub eax, 10 + mov edx, y + add edx, 18 + invoke DrawCircle, eax, edx, 3, 001515C8h + +die_pips_done: + ret +DrawCrapsDie ENDP + +InitDiceGame PROC + mov dice3d_state, 0 + mov dice3d_timer, 0 + mov dice3d_win, 0 + cmp dice3d_bet, 10 + jge @F + mov dice3d_bet, 50 +@@: + ret +InitDiceGame ENDP + +HandleDiceInput PROC + cmp dice3d_state, 1 + je dice_input_done + + invoke Pressed3D, 20h ; SPACE -> Roll! + test eax, eax + jz check_dice_choice + mov eax, bankroll + cmp eax, dice3d_bet + jl dice_no_cash + sub eax, dice3d_bet + mov bankroll, eax + mov dice3d_state, 1 + mov dice3d_timer, 0 + mov dice3d_win, 0 + invoke PlaySoundA, OFFSET soundCards, 0, 1 + jmp dice_input_done + +dice_no_cash: + invoke CopyString3D, OFFSET msgNeedMoney, OFFSET flashMessage + mov flashTimer, 60 + invoke PlaySoundA, OFFSET soundClick, 0, 1 + jmp dice_input_done + +check_dice_choice: + invoke Pressed3D, 31h ; '1' + test eax, eax + jz @F + mov dice3d_choice, 1 + invoke PlaySoundA, OFFSET soundClick, 0, 1 +@@: + invoke Pressed3D, 32h ; '2' + test eax, eax + jz check_dice_bet_adjust + mov dice3d_choice, 2 + invoke PlaySoundA, OFFSET soundClick, 0, 1 + +check_dice_bet_adjust: + invoke Pressed3D, 26h ; UP + test eax, eax + jnz dice_inc_bet + invoke Pressed3D, 57h ; W + test eax, eax + jz check_dice_bet_down +dice_inc_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp dice3d_bet, 500 + jge dice_input_done + cmp dice3d_bet, 250 + jge dice_set_500 + cmp dice3d_bet, 100 + jge dice_set_250 + cmp dice3d_bet, 50 + jge dice_set_100 + cmp dice3d_bet, 25 + jge dice_set_50 + jmp dice_set_25 +dice_set_500: + mov dice3d_bet, 500 + jmp dice_input_done +dice_set_250: + mov dice3d_bet, 250 + jmp dice_input_done +dice_set_100: + mov dice3d_bet, 100 + jmp dice_input_done +dice_set_50: + mov dice3d_bet, 50 + jmp dice_input_done +dice_set_25: + mov dice3d_bet, 25 + jmp dice_input_done + +check_dice_bet_down: + invoke Pressed3D, 28h ; DOWN + test eax, eax + jnz dice_dec_bet + invoke Pressed3D, 53h ; S + test eax, eax + jz dice_input_done +dice_dec_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp dice3d_bet, 25 + jle dice_set_10 + cmp dice3d_bet, 50 + jle dice_set_25 + cmp dice3d_bet, 100 + jle dice_set_50 + cmp dice3d_bet, 250 + jle dice_set_100 + mov dice3d_bet, 250 + jmp dice_input_done +dice_set_10: + mov dice3d_bet, 10 + +dice_input_done: + ret +HandleDiceInput ENDP + +RenderDiceGame PROC uses ebx esi edi + LOCAL d1X:DWORD, d1Y:DWORD, d2X:DWORD, d2Y:DWORD, bxX:DWORD + + ; Craps Emerald Felt & Oak Armrest + invoke DrawBox, 0, 0, 320, 240, 00082E12h + invoke DrawBoxBorder, 4, 4, 312, 232, 00093816h, 00122244h, 3 + invoke DrawBoxBorder, 8, 8, 304, 224, 000A3F19h, 0005140Ah, 2 + + ; Pyramidal diamond craps backboard texture + mov bxX, 12 +craps_bumper: + cmp bxX, 308 + jge craps_felt_markings + mov eax, bxX + add eax, 4 + invoke DrawBox, eax, 11, 4, 4, 00041006h + mov eax, bxX + add eax, 5 + invoke DrawPixel, eax, 12, 000F4820h + add bxX, 12 + jmp craps_bumper + +craps_felt_markings: + ; Table Markings printed in gold directly on felt + invoke DrawString8x8, 72, 20, OFFSET textDiceTitle, 0000D7FFh + invoke DrawString8x8, 68, 32, OFFSET textDiceSubtitle, 0000B0A0h + + ; Meters: Bank & Bet + invoke DrawBoxBorder, 16, 46, 108, 16, 0006240Eh, 000E5022h, 1 + invoke DrawString8x8, 20, 50, OFFSET textBankPrompt, 0050FF50h + invoke FormatNumber, bankroll, OFFSET hudNumBuffer + invoke DrawString8x8, 68, 50, OFFSET hudNumBuffer, 0050FF50h + + invoke DrawBoxBorder, 196, 46, 108, 16, 0006240Eh, 000E5022h, 1 + invoke DrawString8x8, 200, 50, OFFSET textBetPrompt, 0000D7FFh + invoke FormatNumber, dice3d_bet, OFFSET hudNumBuffer + invoke DrawString8x8, 264, 50, OFFSET hudNumBuffer, 0000D7FFh + + ; Betting Zone Left: [1] Ч�Т + mov eax, 00073014h + mov edx, 001A602Ah + mov ecx, 0080B090h + cmp dice3d_choice, 1 + jne @F + mov eax, 000B441Dh + mov edx, 0000FFFFh + mov ecx, 0000FFFFh +@@: + push ecx + invoke DrawBoxBorder, 16, 68, 88, 54, eax, edx, 2 + pop ecx + invoke DrawString8x8, 24, 74, OFFSET textDiceEven, ecx + invoke DrawString8x8, 20, 86, OFFSET textDiceEvenNums, 0000D7FFh + cmp dice3d_choice, 1 + jne @F + invoke DrawChipStack, 48, 110, 4 +@@: + + ; Betting Zone Right: [2] �ЕЧЕТ + mov eax, 00073014h + mov edx, 001A602Ah + mov ecx, 0080B090h + cmp dice3d_choice, 2 + jne @F + mov eax, 000B441Dh + mov edx, 0000FFFFh + mov ecx, 0000FFFFh +@@: + push ecx + invoke DrawBoxBorder, 216, 68, 88, 54, eax, edx, 2 + pop ecx + invoke DrawString8x8, 224, 74, OFFSET textDiceOdd, ecx + invoke DrawString8x8, 224, 86, OFFSET textDiceOddNums, 00508060h + cmp dice3d_choice, 2 + jne @F + invoke DrawChipStack, 248, 110, 4 +@@: + + ; Center Dice Field + invoke DrawBoxBorder, 112, 68, 96, 54, 00072E13h, 00145826h, 1 + + ; Rolling Animation Logic + cmp dice3d_state, 1 + jne dice_at_rest + + inc dice3d_timer + + ; Tumbling jitter offsets + mov eax, dice3d_timer + and eax, 3 + sub eax, 1 + + mov edx, 120 + add edx, eax + mov d1X, edx + mov edx, 76 + sub edx, eax + mov d1Y, edx + + mov edx, 164 + sub edx, eax + mov d2X, edx + mov edx, 78 + add edx, eax + mov d2Y, edx + + test dice3d_timer, 1 + jz @F + invoke RandomRange3D, 6 + inc eax + mov dice3d_val1, eax + invoke RandomRange3D, 6 + inc eax + mov dice3d_val2, eax +@@: + invoke DrawCrapsDie, d1X, d1Y, 36, dice3d_val1 + invoke DrawCrapsDie, d2X, d2Y, 36, dice3d_val2 + invoke DrawString8x8, 76, 138, OFFSET textDiceRolling, 0000FFFFh + + cmp dice3d_timer, 36 + jl dice_draw_controls + + ; Settle dice! + mov dice3d_state, 2 + invoke RandomRange3D, 6 + inc eax + mov dice3d_val1, eax + invoke RandomRange3D, 6 + inc eax + mov dice3d_val2, eax + + mov eax, dice3d_val1 + add eax, dice3d_val2 + mov dice3d_sum, eax + + ; Check choice vs sum parity + mov eax, dice3d_sum + test eax, 1 + jz dice_sum_is_even + + ; Odd + cmp dice3d_choice, 2 + je dice_roll_won + jmp dice_roll_lost + +dice_sum_is_even: + cmp dice3d_choice, 1 + je dice_roll_won + +dice_roll_lost: + mov dice3d_win, 0 + invoke PlaySoundA, OFFSET soundLose, 0, 1 + jmp dice_draw_controls + +dice_roll_won: + mov eax, dice3d_bet + shl eax, 1 + mov dice3d_win, eax + add bankroll, eax + invoke PlaySoundA, OFFSET soundVictory, 0, 1 + jmp dice_draw_controls + +dice_at_rest: + invoke DrawCrapsDie, 120, 76, 36, dice3d_val1 + invoke DrawCrapsDie, 164, 76, 36, dice3d_val2 + + cmp dice3d_state, 2 + jne dice_draw_controls + + ; Scoreboard Plaque + invoke DrawBoxBorder, 36, 130, 248, 30, 00051D0Ch, 0000D7FFh, 1 + invoke DrawString8x8, 44, 135, OFFSET textDiceSumHdr, 0000D7FFh + invoke FormatNumber, dice3d_val1, OFFSET hudNumBuffer + invoke DrawString8x8, 100, 135, OFFSET hudNumBuffer, 00FFFFFFh + invoke DrawString8x8, 110, 135, OFFSET textDicePlus, 0000D7FFh + invoke FormatNumber, dice3d_val2, OFFSET hudNumBuffer + invoke DrawString8x8, 134, 135, OFFSET hudNumBuffer, 00FFFFFFh + invoke DrawString8x8, 144, 135, OFFSET textDiceEqual, 0000D7FFh + invoke FormatNumber, dice3d_sum, OFFSET hudNumBuffer + invoke DrawString8x8, 168, 135, OFFSET hudNumBuffer, 0000FFFFh + + mov eax, dice3d_sum + test eax, 1 + jnz @F + invoke DrawString8x8, 186, 135, OFFSET textDiceEvenResult, 0080FF80h + jmp dice_show_win_msg +@@: + invoke DrawString8x8, 186, 135, OFFSET textDiceOddResult, 00FF8080h + +dice_show_win_msg: + cmp dice3d_win, 0 + je dice_show_loss + invoke DrawString8x8, 68, 147, OFFSET textDiceWin, 0050FF50h + invoke FormatNumber, dice3d_win, OFFSET hudNumBuffer + invoke DrawString8x8, 156, 147, OFFSET hudNumBuffer, 0050FF50h + invoke DrawString8x8, 188, 147, OFFSET textDiceWinMult, 0050FF50h + jmp dice_draw_controls + +dice_show_loss: + invoke DrawString8x8, 116, 147, OFFSET textDiceLose, 008080FFh + +dice_draw_controls: + invoke DrawBox, 16, 188, 288, 1, 00104820h + + mov eax, 0 + cmp dice3d_choice, 1 + sete al + invoke DrawKeycap3D, 20, 198, OFFSET textDiceKey1, eax + + mov eax, 0 + cmp dice3d_choice, 2 + sete al + invoke DrawKeycap3D, 82, 198, OFFSET textDiceKey2, eax + + invoke DrawKeycap3D, 160, 198, OFFSET textDiceKeySpace, 1 + invoke DrawKeycap3D, 274, 198, OFFSET textKeyEsc, 0 + ret +RenderDiceGame ENDP + +; ============================================================================== +; IN-ENGINE 2.5D MINI-GAME: SKULLS & CUPS (��П�РСТКИ) +; ============================================================================== +DrawGlowingOrb PROC uses ebx esi edi, cxVal:DWORD, cyVal:DWORD + invoke DrawCircle, cxVal, cyVal, 14, 00081E2Eh + invoke DrawCircle, cxVal, cyVal, 11, 00103A58h + invoke DrawCircle, cxVal, cyVal, 8, 001A588Ah + invoke DrawCircle, cxVal, cyVal, 6, 0000B0E0h + invoke DrawCircle, cxVal, cyVal, 4, 0000E0FFh + mov eax, cxVal + dec eax + mov edx, cyVal + dec edx + invoke DrawCircle, eax, edx, 2, 00A0FFFFh + mov eax, cxVal + sub eax, 2 + mov edx, cyVal + sub edx, 2 + invoke DrawPixel, eax, edx, 00FFFFFFh + ret +DrawGlowingOrb ENDP + +DrawGothicCup PROC uses ebx esi edi, x:DWORD, y:DWORD, cupId:DWORD, isLifted:DWORD, hasBall:DWORD + LOCAL liftY:DWORD + + mov eax, y + cmp isLifted, 0 + je @F + sub eax, 28 +@@: + mov liftY, eax + + ; Drop shadow on purple velvet + cmp isLifted, 0 + jne @F + mov eax, x + add eax, 6 + mov edx, y + add edx, 42 + invoke DrawBox, eax, edx, 36, 4, 000A0410h + jmp draw_cup_body +@@: + mov eax, x + add eax, 10 + mov edx, y + add edx, 42 + invoke DrawBox, eax, edx, 28, 2, 0012061Ah + + ; Draw orb if lifted and has ball + cmp hasBall, 0 + je draw_cup_body + mov eax, x + add eax, 24 + mov edx, y + add edx, 26 + invoke DrawGlowingOrb, eax, edx + +draw_cup_body: + ; Base + mov eax, x + add eax, 6 + mov edx, liftY + add edx, 36 + invoke DrawBoxBorder, eax, edx, 36, 6, 00221A30h, 0000B0D0h, 1 + + ; Stem + mov eax, x + add eax, 18 + mov edx, liftY + add edx, 28 + invoke DrawBox, eax, edx, 12, 8, 00332545h + mov eax, x + add eax, 22 + mov edx, liftY + add edx, 28 + invoke DrawBox, eax, edx, 4, 8, 0055406Eh + + ; Bowl + mov eax, x + add eax, 8 + mov edx, liftY + add edx, 8 + invoke DrawBoxBorder, eax, edx, 32, 20, 002D1E40h, 0000D7FFh, 1 + mov eax, x + add eax, 14 + mov edx, liftY + add edx, 10 + invoke DrawBox, eax, edx, 6, 16, 00503570h + mov eax, x + add eax, 17 + mov edx, liftY + add edx, 10 + invoke DrawBox, eax, edx, 3, 16, 007850A0h + + ; Flared Rim + mov eax, x + add eax, 4 + mov edx, liftY + add edx, 2 + invoke DrawBoxBorder, eax, edx, 40, 7, 00150A20h, 0000FFFFh, 1 + mov eax, x + add eax, 12 + mov edx, liftY + add edx, 4 + invoke DrawBox, eax, edx, 16, 2, 0000FFFFh + + ; Skull Insignia on Bowl + mov eax, x + add eax, 21 + mov edx, liftY + add edx, 12 + invoke DrawBox, eax, edx, 6, 5, 0000D7FFh + mov eax, x + add eax, 22 + mov edx, liftY + add edx, 17 + invoke DrawBox, eax, edx, 4, 3, 0000D7FFh + mov eax, x + add eax, 22 + mov edx, liftY + add edx, 14 + invoke DrawPixel, eax, edx, 00150A20h + mov eax, x + add eax, 24 + mov edx, liftY + add edx, 14 + invoke DrawPixel, eax, edx, 00150A20h + + ; Cup ID Badge + mov eax, x + add eax, 21 + mov edx, liftY + add edx, 24 + mov ecx, cupId + add ecx, '0' + invoke DrawChar8x8, eax, edx, ecx, 0000FFFFh + ret +DrawGothicCup ENDP + +InitSkullsGame PROC + mov skulls3d_state, 0 + mov skulls3d_timer, 0 + mov skulls3d_win, 0 + cmp skulls3d_bet, 10 + jge @F + mov skulls3d_bet, 50 +@@: + invoke RandomRange3D, 3 + mov skulls3d_ballPos, eax + ret +InitSkullsGame ENDP + +FinalizeSkullsGuess PROC uses ebx + mov skulls3d_state, 4 + mov eax, skulls3d_choice + cmp eax, skulls3d_ballPos + jne skulls_guess_lost + + ; Won 3x! + mov eax, skulls3d_bet + imul eax, 3 + mov skulls3d_win, eax + add bankroll, eax + invoke PlaySoundA, OFFSET soundVictory, 0, 1 + ret + +skulls_guess_lost: + mov skulls3d_win, 0 + invoke PlaySoundA, OFFSET soundLose, 0, 1 + ret +FinalizeSkullsGuess ENDP + +HandleSkullsInput PROC + cmp skulls3d_state, 1 + je skulls_input_done + cmp skulls3d_state, 2 + je skulls_input_done + + cmp skulls3d_state, 3 + je check_skulls_pick + + ; State 0 or 4: Can start new round or adjust bet + invoke Pressed3D, 20h ; SPACE + test eax, eax + jz check_skulls_bet_adjust + + mov eax, bankroll + cmp eax, skulls3d_bet + jl skulls_no_cash + sub eax, skulls3d_bet + mov bankroll, eax + + invoke RandomRange3D, 3 + mov skulls3d_ballPos, eax + + invoke RandomRange3D, 3 + mov skulls3d_swapPair0, eax + invoke RandomRange3D, 3 + mov skulls3d_swapPair1, eax + invoke RandomRange3D, 3 + mov skulls3d_swapPair2, eax + + mov skulls3d_state, 1 + mov skulls3d_timer, 0 + mov skulls3d_win, 0 + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + jmp skulls_input_done + +skulls_no_cash: + invoke CopyString3D, OFFSET msgNeedMoney, OFFSET flashMessage + mov flashTimer, 60 + invoke PlaySoundA, OFFSET soundClick, 0, 1 + jmp skulls_input_done + +check_skulls_pick: + invoke Pressed3D, 31h ; '1' + test eax, eax + jz @F + mov skulls3d_choice, 0 + call FinalizeSkullsGuess + jmp skulls_input_done +@@: + invoke Pressed3D, 32h ; '2' + test eax, eax + jz @F + mov skulls3d_choice, 1 + call FinalizeSkullsGuess + jmp skulls_input_done +@@: + invoke Pressed3D, 33h ; '3' + test eax, eax + jz skulls_input_done + mov skulls3d_choice, 2 + call FinalizeSkullsGuess + jmp skulls_input_done + +check_skulls_bet_adjust: + invoke Pressed3D, 26h ; UP + test eax, eax + jnz skulls_inc_bet + invoke Pressed3D, 57h ; W + test eax, eax + jz check_skulls_bet_down +skulls_inc_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp skulls3d_bet, 500 + jge skulls_input_done + cmp skulls3d_bet, 250 + jge skulls_set_500 + cmp skulls3d_bet, 100 + jge skulls_set_250 + cmp skulls3d_bet, 50 + jge skulls_set_100 + cmp skulls3d_bet, 25 + jge skulls_set_50 + jmp skulls_set_25 +skulls_set_500: + mov skulls3d_bet, 500 + jmp skulls_input_done +skulls_set_250: + mov skulls3d_bet, 250 + jmp skulls_input_done +skulls_set_100: + mov skulls3d_bet, 100 + jmp skulls_input_done +skulls_set_50: + mov skulls3d_bet, 50 + jmp skulls_input_done +skulls_set_25: + mov skulls3d_bet, 25 + jmp skulls_input_done + +check_skulls_bet_down: + invoke Pressed3D, 28h ; DOWN + test eax, eax + jnz skulls_dec_bet + invoke Pressed3D, 53h ; S + test eax, eax + jz skulls_input_done +skulls_dec_bet: + invoke PlaySoundA, OFFSET soundClick, 0, 1 + cmp skulls3d_bet, 25 + jle skulls_set_10 + cmp skulls3d_bet, 50 + jle skulls_set_25 + cmp skulls3d_bet, 100 + jle skulls_set_50 + cmp skulls3d_bet, 250 + jle skulls_set_100 + mov skulls3d_bet, 250 + jmp skulls_input_done +skulls_set_10: + mov skulls3d_bet, 10 + +skulls_input_done: + ret +HandleSkullsInput ENDP + +RenderSkullsGame PROC uses ebx esi edi + LOCAL passTick:DWORD, curPair:DWORD, swapSlotA:DWORD, swapSlotB:DWORD + LOCAL xA:DWORD, xB:DWORD, curXA:DWORD, curXB:DWORD, curYA:DWORD, curYB:DWORD + LOCAL distX:DWORD, dxVal:DWORD, archY:DWORD + + ; Dark Gothic Tavern Table with Royal Velvet + invoke DrawBox, 0, 0, 320, 240, 000D0612h + invoke DrawBoxBorder, 4, 4, 312, 232, 00160B20h, 00321845h, 3 + invoke DrawBoxBorder, 8, 8, 304, 224, 00220F30h, 0000D7FFh, 1 + + ; Header + invoke DrawBoxBorder, 24, 12, 272, 24, 0012081Ah, 0000D7FFh, 2 + invoke DrawString8x8, 64, 20, OFFSET textSkullsTitle, 0000FFFFh + + ; Meters: Bank & Bet + invoke DrawBoxBorder, 20, 42, 114, 16, 000D0616h, 00401858h, 1 + invoke DrawString8x8, 24, 46, OFFSET textBankPrompt, 0050FF50h + invoke FormatNumber, bankroll, OFFSET hudNumBuffer + invoke DrawString8x8, 72, 46, OFFSET hudNumBuffer, 0050FF50h + + invoke DrawBoxBorder, 186, 42, 114, 16, 000D0616h, 00401858h, 1 + invoke DrawString8x8, 190, 46, OFFSET textBetPrompt, 0000D7FFh + invoke FormatNumber, skulls3d_bet, OFFSET hudNumBuffer + invoke DrawString8x8, 254, 46, OFFSET hudNumBuffer, 0000D7FFh + + ; Velvet Table Runner + invoke DrawBoxBorder, 16, 64, 288, 120, 001A0B25h, 00452060h, 1 + + ; Dispatch based on phase + cmp skulls3d_state, 1 + je skulls_render_phase1 + cmp skulls3d_state, 2 + je skulls_render_phase2 + cmp skulls3d_state, 3 + je skulls_render_phase3 + cmp skulls3d_state, 4 + je skulls_render_phase4 + + ; Phase 0: Idle / Betting + invoke DrawString8x8, 56, 70, OFFSET textSkullsPhase0, 0000D7FFh + invoke DrawGothicCup, 48, 108, 1, 0, 0 + invoke DrawGothicCup, 136, 108, 2, 0, 0 + invoke DrawGothicCup, 224, 108, 3, 0, 0 + jmp skulls_draw_controls + +skulls_render_phase1: + inc skulls3d_timer + invoke DrawString8x8, 56, 70, OFFSET textSkullsPhase1, 0000FFFFh + + ; Draw cups with ballPos cup lifted + mov eax, 0 + cmp skulls3d_ballPos, 0 + sete al + invoke DrawGothicCup, 48, 108, 1, eax, eax + + mov eax, 0 + cmp skulls3d_ballPos, 1 + sete al + invoke DrawGothicCup, 136, 108, 2, eax, eax + + mov eax, 0 + cmp skulls3d_ballPos, 2 + sete al + invoke DrawGothicCup, 224, 108, 3, eax, eax + + cmp skulls3d_timer, 40 + jl skulls_draw_controls + + ; Start Phase 2 (Shuffle) + mov skulls3d_state, 2 + mov skulls3d_timer, 0 + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + jmp skulls_draw_controls + +skulls_render_phase2: + inc skulls3d_timer + + mov eax, skulls3d_timer + cmp eax, 20 + jbe pass_0 + cmp eax, 40 + jbe pass_1 + sub eax, 41 + mov passTick, eax + mov eax, skulls3d_swapPair2 + mov curPair, eax + jmp setup_pass_pair + +pass_0: + dec eax + mov passTick, eax + mov eax, skulls3d_swapPair0 + mov curPair, eax + jmp setup_pass_pair + +pass_1: + sub eax, 21 + mov passTick, eax + mov eax, skulls3d_swapPair1 + mov curPair, eax + +setup_pass_pair: + cmp curPair, 1 + je pair_1 + cmp curPair, 2 + je pair_2 + mov swapSlotA, 0 + mov swapSlotB, 1 + mov xA, 48 + mov xB, 136 + jmp calc_swap_coords +pair_1: + mov swapSlotA, 1 + mov swapSlotB, 2 + mov xA, 136 + mov xB, 224 + jmp calc_swap_coords +pair_2: + mov swapSlotA, 0 + mov swapSlotB, 2 + mov xA, 48 + mov xB, 224 + +calc_swap_coords: + mov eax, xB + sub eax, xA + mov distX, eax + + mov eax, distX + imul eax, passTick + xor edx, edx + mov ecx, 20 + div ecx + mov dxVal, eax + + mov eax, xA + add eax, dxVal + mov curXA, eax + + mov eax, xB + sub eax, dxVal + mov curXB, eax + + ; Arch height: archY = (t * (20 - t)) / 25 + mov eax, passTick + mov ecx, 20 + sub ecx, eax + imul eax, ecx + xor edx, edx + mov ecx, 25 + div ecx + mov archY, eax + + mov edx, 108 + sub edx, archY + mov curYA, edx ; upper arc + + mov edx, 108 + add edx, archY + mov curYB, edx ; lower arc + + invoke DrawString8x8, 56, 70, OFFSET textSkullsPhase2, 0000FFFFh + + ; Depth sorted rendering: Background cup (upper arc) draws FIRST, Foreground cup (lower arc) draws OVER it! + invoke DrawGothicCup, curXA, curYA, 1, 0, 0 + invoke DrawGothicCup, curXB, curYB, 2, 0, 0 + + ; Draw remaining static cup + cmp curPair, 1 + je draw_stat_0 + cmp curPair, 2 + je draw_stat_1 + invoke DrawGothicCup, 224, 108, 3, 0, 0 + jmp check_pass_finish +draw_stat_0: + invoke DrawGothicCup, 48, 108, 1, 0, 0 + jmp check_pass_finish +draw_stat_1: + invoke DrawGothicCup, 136, 108, 2, 0, 0 + +check_pass_finish: + cmp skulls3d_timer, 20 + je do_pass_swap + cmp skulls3d_timer, 40 + je do_pass_swap + cmp skulls3d_timer, 60 + je do_pass_swap + jmp skulls_draw_controls + +do_pass_swap: + ; Update ballPos if involved in swap + mov eax, skulls3d_ballPos + cmp eax, swapSlotA + jne check_swap_b + mov eax, swapSlotB + mov skulls3d_ballPos, eax + jmp ball_swap_done +check_swap_b: + cmp eax, swapSlotB + jne ball_swap_done + mov eax, swapSlotA + mov skulls3d_ballPos, eax + +ball_swap_done: + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + + cmp skulls3d_timer, 60 + jl skulls_draw_controls + + ; Shuffle complete -> Move to Pick phase + mov skulls3d_state, 3 + mov skulls3d_timer, 0 + jmp skulls_draw_controls + +skulls_render_phase3: + invoke DrawString8x8, 40, 70, OFFSET textSkullsPhase3, 0000FFFFh + invoke DrawGothicCup, 48, 108, 1, 0, 0 + invoke DrawGothicCup, 136, 108, 2, 0, 0 + invoke DrawGothicCup, 224, 108, 3, 0, 0 + jmp skulls_draw_controls + +skulls_render_phase4: + ; Reveal phase + cmp skulls3d_win, 0 + je skulls_show_loss + invoke DrawString8x8, 48, 70, OFFSET textSkullsWin, 0050FF50h + invoke FormatNumber, skulls3d_win, OFFSET hudNumBuffer + invoke DrawString8x8, 216, 70, OFFSET hudNumBuffer, 0050FF50h + jmp skulls_p4_cups + +skulls_show_loss: + invoke DrawString8x8, 40, 70, OFFSET textSkullsLose, 008080FFh + +skulls_p4_cups: + ; Draw chosen cup lifted + mov eax, 0 + cmp skulls3d_choice, 0 + sete al + mov edx, 0 + cmp skulls3d_ballPos, 0 + sete dl + invoke DrawGothicCup, 48, 108, 1, eax, edx + + mov eax, 0 + cmp skulls3d_choice, 1 + sete al + mov edx, 0 + cmp skulls3d_ballPos, 1 + sete dl + invoke DrawGothicCup, 136, 108, 2, eax, edx + + mov eax, 0 + cmp skulls3d_choice, 2 + sete al + mov edx, 0 + cmp skulls3d_ballPos, 2 + sete dl + invoke DrawGothicCup, 224, 108, 3, eax, edx + + invoke DrawString8x8, 64, 168, OFFSET textSkullsAgain, 0000D7FFh + +skulls_draw_controls: + invoke DrawBox, 16, 190, 288, 1, 003A1850h + + mov eax, 0 + cmp skulls3d_choice, 0 + sete al + invoke DrawKeycap3D, 18, 200, OFFSET textSkullsKey1, eax + + mov eax, 0 + cmp skulls3d_choice, 1 + sete al + invoke DrawKeycap3D, 82, 200, OFFSET textSkullsKey2, eax + + mov eax, 0 + cmp skulls3d_choice, 2 + sete al + invoke DrawKeycap3D, 146, 200, OFFSET textSkullsKey3, eax + + invoke DrawKeycap3D, 212, 200, OFFSET textSkullsKeySpace, 1 + invoke DrawKeycap3D, 274, 200, OFFSET textKeyEsc, 0 + ret +RenderSkullsGame ENDP + +""" + +raw_content = raw_content[:pos_games_start] + new_games_code.encode('cp1251') + raw_content[pos_player_input:] +print("[OK] Replaced games implementation block with rich 2.5D procedures") + +# Write out patched casino3d.inc +with open(CASINO_INC, 'wb') as f: + f.write(raw_content) + +print(f"[SUCCESS] casino3d.inc patched successfully! New byte length: {len(raw_content)}") diff --git a/tools/tests/test_minigames_render.py b/tools/tests/test_minigames_render.py new file mode 100644 index 0000000..e4fbd5e --- /dev/null +++ b/tools/tests/test_minigames_render.py @@ -0,0 +1,379 @@ +# -*- coding: utf-8 -*- +import math +from pathlib import Path +from PIL import Image + +REPO_ROOT = Path(__file__).resolve().parents[2] +OUTPUT_DIR = REPO_ROOT / 'docs' / 'previews_minigames' +OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + +# Parse font8x8.inc +font_path = REPO_ROOT / 'font8x8.inc' +font_bytes = bytearray(256 * 8) +with open(font_path, 'r', encoding='latin1') as f: + text = f.read() + +import re +matches = re.findall(r'0([0-9A-Fa-f]{2})h', text) +for i, m in enumerate(matches[:256*8]): + font_bytes[i] = int(m, 16) + +SCREEN_W = 640 +SCREEN_H = 480 + +class RasterBuffer: + def __init__(self): + self.pixels = [(0, 0, 0)] * (SCREEN_W * SCREEN_H) + + def to_rgb(self, c): + # 0x00BBGGRR format + r = c & 0xFF + g = (c >> 8) & 0xFF + b = (c >> 16) & 0xFF + return (r, g, b) + + def draw_box(self, x, y, w, h, color): + px = x * 2 + py = y * 2 + pw = w * 2 + ph = h * 2 + rgb = self.to_rgb(color) + for row in range(py, min(py + ph, SCREEN_H)): + if row < 0: + continue + base = row * SCREEN_W + for col in range(px, min(px + pw, SCREEN_W)): + if col < 0: + continue + self.pixels[base + col] = rgb + + def draw_box_border(self, x, y, w, h, fill_color, border_color, b_width): + self.draw_box(x, y, w, h, border_color) + self.draw_box(x + b_width, y + b_width, w - b_width * 2, h - b_width * 2, fill_color) + + def draw_pixel(self, x, y, color): + self.draw_box(x, y, 1, 1, color) + + def draw_circle(self, cx, cy, radius, color): + r_sq = radius * radius + for dy in range(-radius, radius + 1): + for dx in range(-radius, radius + 1): + if dx * dx + dy * dy <= r_sq: + self.draw_pixel(cx + dx, cy + dy, color) + + def draw_char(self, x, y, ch, color): + if isinstance(ch, str): + try: + code = ch.encode('cp1251')[0] + except: + code = ord(ch) & 0xFF + else: + code = ch & 0xFF + + glyph_offset = code * 8 + for row in range(8): + row_bits = font_bytes[glyph_offset + row] + for col in range(8): + if row_bits & (0x80 >> col): + self.draw_pixel(x + col, y + row, color) + + def draw_string(self, x, y, s, color): + cur_x = x + for ch in s: + self.draw_char(cur_x, y, ch, color) + cur_x += 8 + + def draw_keycap(self, x, y, text, is_active=False): + w = len(text) * 8 + 8 + h = 13 + if is_active: + border_c = 0x0000FFFF + fill_c = 0x001B4B22 + txt_c = 0x0000FFFF + else: + border_c = 0x00606870 + fill_c = 0x001A1C22 + txt_c = 0x00D0D8E0 + self.draw_box(x, y + 2, w, h, 0x0008080C) # shadow + self.draw_box_border(x, y, w, h, fill_c, border_c, 1) + self.draw_string(x + 4, y + 3, text, txt_c) + return w + 4 + + def draw_chip_stack(self, x, y, count=3): + # Draw stack of 3D casino clay chips + for i in range(count): + cy = y - i * 4 + self.draw_box(x + 2, cy + 5, 20, 2, 0x00041006) # shadow + self.draw_box_border(x, cy, 24, 6, 0x001515B8, 0x00FFFFFF, 1) + # white stripe inlays + self.draw_box(x + 4, cy + 1, 3, 4, 0x00FFFFFF) + self.draw_box(x + 17, cy + 1, 3, 4, 0x00FFFFFF) + + def draw_coin_ellipse(self, cx, cy, rx, ry, outer_color, inner_color, relief=0): + if rx <= 0 or ry <= 0: + return + rx_sq = rx * rx + ry_sq = ry * ry + rx_ry_sq = rx_sq * ry_sq + thresh_sq = (rx_ry_sq * 3) // 4 + for dy in range(-ry, ry + 1): + cur_y = cy + dy + for dx in range(-rx, rx + 1): + cur_x = cx + dx + val = dx * dx * ry_sq + dy * dy * rx_sq + if val <= rx_ry_sq: + if val >= thresh_sq: + self.draw_pixel(cur_x, cur_y, outer_color) + else: + self.draw_pixel(cur_x, cur_y, inner_color) + + if rx >= 14 and relief == 1: + gold_dark = 0x00007A9A + # Imperial Eagle + self.draw_box(cx - 3, cy - 14, 7, 2, gold_dark) + self.draw_box(cx - 4, cy - 12, 9, 2, gold_dark) + self.draw_box(cx - 2, cy - 9, 5, 12, gold_dark) + self.draw_box(cx - 1, cy - 8, 3, 10, 0x0000D7FF) + self.draw_box(cx - 10, cy - 8, 7, 3, gold_dark) + self.draw_box(cx - 13, cy - 5, 10, 3, gold_dark) + self.draw_box(cx - 11, cy - 2, 7, 3, gold_dark) + self.draw_box(cx + 4, cy - 8, 7, 3, gold_dark) + self.draw_box(cx + 4, cy - 5, 10, 3, gold_dark) + self.draw_box(cx + 5, cy - 2, 7, 3, gold_dark) + self.draw_box(cx - 4, cy + 4, 9, 3, gold_dark) + self.draw_box(cx - 3, cy + 7, 7, 3, gold_dark) + elif rx >= 14 and relief == 2: + gold_dark = 0x00007A9A + # Roman numeral 'I' + self.draw_box(cx - 5, cy - 10, 11, 3, gold_dark) + self.draw_box(cx - 2, cy - 7, 5, 14, gold_dark) + self.draw_box(cx - 1, cy - 6, 3, 12, 0x0000D7FF) + self.draw_box(cx - 6, cy + 7, 13, 3, gold_dark) + for w_dy in [-10, -5, 0, 5, 9]: + self.draw_box(cx - 12, cy + w_dy, 2, 2, gold_dark) + self.draw_box(cx + 11, cy + w_dy, 2, 2, gold_dark) + + def draw_craps_die(self, x, y, size, val): + self.draw_box(x + 3, y + size, size, 3, 0x00031408) + self.draw_box(x + size, y + 3, 3, size, 0x00031408) + body_color = 0x00F0F4FA + bevel_color = 0x00C0C6D0 + border_color = 0x00353540 + self.draw_box_border(x, y, size, size, body_color, border_color, 1) + self.draw_box(x + 1, y + 1, size - 2, 1, 0x00FFFFFF) + self.draw_box(x + 1, y + 1, 1, size - 2, 0x00FFFFFF) + self.draw_box(x + 1, y + size - 2, size - 2, 1, bevel_color) + self.draw_box(x + size - 2, y + 1, 1, size - 2, bevel_color) + + pip_c = 0x001515C8 + pip_r = 3 + if val in [1, 3, 5]: + self.draw_circle(x + size // 2, y + size // 2, pip_r, pip_c) + self.draw_pixel(x + size // 2 - 1, y + size // 2 - 1, 0x00FFFFFF) + if val >= 2: + self.draw_circle(x + 9, y + 9, pip_r, pip_c) + self.draw_circle(x + size - 10, y + size - 10, pip_r, pip_c) + if val >= 4: + self.draw_circle(x + size - 10, y + 9, pip_r, pip_c) + self.draw_circle(x + 9, y + size - 10, pip_r, pip_c) + if val == 6: + self.draw_circle(x + 9, y + size // 2, pip_r, pip_c) + self.draw_circle(x + size - 10, y + size // 2, pip_r, pip_c) + + def draw_gothic_cup(self, x, y, cup_id, is_lifted=False, has_ball=False): + lift_y = y - 36 if is_lifted else y + if not is_lifted: + self.draw_box(x + 6, y + 42, 36, 4, 0x000A0410) + else: + self.draw_box(x + 10, y + 42, 28, 2, 0x0012061A) + + if is_lifted and has_ball: + self.draw_glowing_orb(x + 24, y + 26) + + self.draw_box_border(x + 6, lift_y + 36, 36, 6, 0x00221A30, 0x0000B0D0, 1) + self.draw_box(x + 18, lift_y + 28, 12, 8, 0x00332545) + self.draw_box(x + 22, lift_y + 28, 4, 8, 0x0055406E) + self.draw_box_border(x + 8, lift_y + 8, 32, 20, 0x002D1E40, 0x0000D7FF, 1) + self.draw_box(x + 14, lift_y + 10, 6, 16, 0x00503570) + self.draw_box(x + 17, lift_y + 10, 3, 16, 0x007850A0) + self.draw_box_border(x + 4, lift_y + 2, 40, 7, 0x00150A20, 0x0000FFFF, 1) + self.draw_box(x + 12, lift_y + 4, 16, 2, 0x0000FFFF) + + self.draw_box(x + 21, lift_y + 12, 6, 5, 0x0000D7FF) + self.draw_box(x + 22, lift_y + 17, 4, 3, 0x0000D7FF) + self.draw_pixel(x + 22, lift_y + 14, 0x00150A20) + self.draw_pixel(x + 24, lift_y + 14, 0x00150A20) + self.draw_string(x + 21, lift_y + 24, str(cup_id), 0x0000FFFF) + + def draw_glowing_orb(self, cx, cy): + self.draw_circle(cx, cy, 14, 0x00081E2E) + self.draw_circle(cx, cy, 11, 0x00103A58) + self.draw_circle(cx, cy, 8, 0x001A588A) + self.draw_circle(cx, cy, 6, 0x0000B0E0) + self.draw_circle(cx, cy, 4, 0x0000E0FF) + self.draw_circle(cx - 1, cy - 1, 2, 0x00A0FFFF) + self.draw_pixel(cx - 2, cy - 2, 0x00FFFFFF) + + def save(self, filename): + img = Image.new('RGB', (SCREEN_W, SCREEN_H)) + img.putdata(self.pixels) + out_path = OUTPUT_DIR / filename + img.save(out_path) + print(f"Saved {out_path}") + +# ============================================================================== +# 1. RENDER COIN GAME +# ============================================================================== +def render_coin(filename='coin_preview.png'): + b = RasterBuffer() + b.draw_box(0, 0, 320, 240, 0x00060914) # Mahogany cabinet + b.draw_box_border(6, 6, 308, 228, 0x000F1522, 0x001B2E48, 3) + for cx in [12, 306]: + for cy in [12, 226]: + b.draw_box(cx, cy, 3, 3, 0x0000D7FF) + b.draw_pixel(cx + 1, cy + 1, 0x00FFFFFF) + + b.draw_box_border(20, 12, 280, 26, 0x000A101C, 0x0000D7FF, 2) + b.draw_string(44, 21, '*** З О Л О Т � Я М О � Е Т � ***', 0x0000FFFF) + for bx in range(24, 296, 12): + b.draw_pixel(bx, 13, 0x0000FFFF) + b.draw_pixel(bx, 36, 0x0000D7FF) + + # Status meters + b.draw_box_border(20, 44, 110, 18, 0x000A1A0E, 0x001A4B22, 1) + b.draw_string(24, 49, 'Б��К: $12500', 0x0050FF50) + b.draw_box_border(190, 44, 110, 18, 0x001A100A, 0x003A2814, 1) + b.draw_string(194, 49, 'СТ�ВК�: $50 [W/S]', 0x0000D7FF) + + # Center Dish + b.draw_circle(160, 110, 46, 0x000090C0) + b.draw_circle(160, 110, 44, 0x0000D7FF) + b.draw_circle(160, 110, 41, 0x00180824) + + # Coin at rest (Heads) + b.draw_coin_ellipse(160, 110, 26, 26, 0x0000A0D0, 0x0000D7FF, relief=1) + + # Left: [1] ОРЕЛ (Active) + b.draw_box_border(20, 72, 76, 52, 0x001C3822, 0x0000FFFF, 2) + b.draw_string(28, 78, '[1] ОР�Л', 0x0000FFFF) + b.draw_coin_ellipse(58, 106, 12, 12, 0x0000A0D0, 0x0000D7FF, relief=1) + + # Right: [2] РЕШК� + b.draw_box_border(224, 72, 76, 52, 0x00121620, 0x00404855, 1) + b.draw_string(232, 78, '[2] РЕШК�', 0x0090A0B0) + b.draw_coin_ellipse(262, 106, 12, 12, 0x000080A0, 0x0000B0D0, relief=2) + + # Streak + b.draw_box_border(44, 154, 232, 20, 0x000E141E, 0x0000D7FF, 1) + b.draw_string(52, 160, 'СЕРИЯ: [X] [X] [X] [ ] [ ] x4!', 0x0000FFFF) + + # Keycaps + b.draw_box(16, 194, 288, 1, 0x001E2E40) + b.draw_keycap(22, 202, '1:ОР�Л', is_active=True) + b.draw_keycap(88, 202, '2:РЕШК�') + b.draw_keycap(160, 202, 'ПРОБЕЛ:БРОСОК', is_active=True) + b.draw_keycap(274, 202, 'ESC') + b.save(filename) + +# ============================================================================== +# 2. RENDER DICE GAME +# ============================================================================== +def render_dice(filename='dice_preview.png'): + b = RasterBuffer() + # Emerald felt + b.draw_box(0, 0, 320, 240, 0x00082E12) + # Oak wood border & leather bumper + b.draw_box_border(4, 4, 312, 232, 0x00093816, 0x00122244, 3) + b.draw_box_border(8, 8, 304, 224, 0x000A3F19, 0x0005140A, 2) + + # Pyramidal backboard rubber diamond texture along top + for bx in range(12, 308, 12): + b.draw_box(bx + 4, 11, 4, 4, 0x00041006) + b.draw_pixel(bx + 5, 12, 0x000F4820) + + # Header + b.draw_string(48, 20, '==== G R A N D C R A P S T A B L E ====', 0x0000D7FF) + b.draw_string(72, 32, '[ PASS LINE ] [ ANY CRAPS ]', 0x0000B0A0) + + # Status + b.draw_box_border(16, 46, 100, 16, 0x0006240E, 0x000E5022, 1) + b.draw_string(20, 50, 'Б��К: $12500', 0x0050FF50) + b.draw_box_border(204, 46, 100, 16, 0x0006240E, 0x000E5022, 1) + b.draw_string(208, 50, 'СТ�ВК�: $50 [W/S]', 0x0000D7FF) + + # Betting Zones + # Left: [1] ЧЕТ (Active with chip stack!) + b.draw_box_border(16, 68, 88, 54, 0x000B441D, 0x0000FFFF, 2) + b.draw_string(24, 74, '[1] Ч�Т', 0x0000FFFF) + b.draw_string(20, 86, '2 4 6 8 10 12', 0x0000D7FF) + b.draw_chip_stack(48, 110, count=4) + + # Right: [2] �ЕЧЕТ + b.draw_box_border(216, 68, 88, 54, 0x00073014, 0x001A602A, 1) + b.draw_string(224, 74, '[2] �ЕЧЕТ', 0x0080B090) + b.draw_string(222, 86, '3 5 7 9 11', 0x00508060) + + # Center Dice Field + b.draw_box_border(112, 68, 96, 54, 0x00072E13, 0x00145826, 1) + # Draw Two Precision Dice + b.draw_craps_die(120, 76, 36, 4) + b.draw_craps_die(164, 76, 36, 6) + + # Score Plaque + b.draw_box_border(36, 130, 248, 30, 0x00051D0C, 0x0000D7FF, 1) + b.draw_string(44, 135, 'СУММ�: 4 + 6 = 10 (Ч�Т)', 0x0000FFFF) + b.draw_string(68, 147, 'ВЫИГРЫШ: +$100! (x2)', 0x0050FF50) + + # Keycaps + b.draw_box(16, 188, 288, 1, 0x00104820) + b.draw_keycap(20, 198, '1:Ч�Т', is_active=True) + b.draw_keycap(82, 198, '2:�ЕЧЕТ') + b.draw_keycap(160, 198, 'ПРОБЕЛ:БРОСОК', is_active=True) + b.draw_keycap(274, 198, 'ESC') + b.save(filename) + +# ============================================================================== +# 3. RENDER SKULLS GAME +# ============================================================================== +def render_skulls(filename='skulls_preview.png'): + b = RasterBuffer() + # Dark gothic table with purple velvet + b.draw_box(0, 0, 320, 240, 0x000D0612) + b.draw_box_border(4, 4, 312, 232, 0x00160B20, 0x00321845, 3) + b.draw_box_border(8, 8, 304, 224, 0x00220F30, 0x0000D7FF, 1) + + # Header + b.draw_box_border(24, 12, 272, 24, 0x0012081A, 0x0000D7FF, 2) + b.draw_string(48, 20, '+ + + � � П � Р С Т К И С К В Е Р � Ы + + +', 0x0000FFFF) + + # Status + b.draw_box_border(20, 42, 110, 16, 0x000D0616, 0x00401858, 1) + b.draw_string(24, 46, 'Б��К: $12500', 0x0050FF50) + b.draw_box_border(190, 42, 110, 16, 0x000D0616, 0x00401858, 1) + b.draw_string(194, 46, 'СТ�ВК�: $50 [W/S]', 0x0000D7FF) + + # Table Runner + b.draw_box_border(16, 66, 288, 114, 0x001A0B25, 0x00452060, 1) + + # Narrative banner + b.draw_string(40, 74, 'СЛЕДИ З� ЗОЛОТЫМ ОКОМ СУДЬБЫ...', 0x0000FFFF) + + # Draw 3 Gothic Cups (Cup 2 is lifted revealing glowing orb!) + b.draw_gothic_cup(48, 100, 1, is_lifted=False) + b.draw_gothic_cup(136, 100, 2, is_lifted=True, has_ball=True) + b.draw_gothic_cup(224, 100, 3, is_lifted=False) + + # Keycaps + b.draw_box(16, 190, 288, 1, 0x003A1850) + b.draw_keycap(18, 200, '1:КУБОК') + b.draw_keycap(82, 200, '2:КУБОК', is_active=True) + b.draw_keycap(146, 200, '3:КУБОК') + b.draw_keycap(212, 200, 'ПРОБЕЛ', is_active=True) + b.draw_keycap(274, 200, 'ESC') + b.save(filename) + +if __name__ == '__main__': + render_coin() + render_dice() + render_skulls() + print("All 3 minigames previews rendered successfully!")