From f25597da8035764b64f09a67564049a99241194c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=A4=D0=B5=D0=B4=D0=BE?= =?UTF-8?q?=D1=81=D0=B5=D0=B5=D0=B2?= Date: Wed, 30 Sep 2026 21:23:38 +0300 Subject: [PATCH] fix(skulls): restore 60 FPS 3D shuffle animation with physical cup swapping and ball tracking --- build/graphics/poker.exe | Bin 214016 -> 215552 bytes casino3d.inc | 372 ++++++++++++++++---- poker.exe | Bin 214016 -> 215552 bytes tools/generators/generate_rich_minigames.py | 369 +++++++++++++++---- 4 files changed, 617 insertions(+), 124 deletions(-) diff --git a/build/graphics/poker.exe b/build/graphics/poker.exe index f8df6dc3d870ef90b3a5a7113a6b9afafec83769..1c3578f8715cb3c6d4c2057d213caf7f69e7061c 100644 GIT binary patch delta 12140 zcmaia30zdw`~E!#Mnypo*#uEQKoHa!n0@Bn3nFN)7`fnXnT}(c8n~5?HfR~<(n%+^ zQZlvNYcyOF#l+H7)LbbsEiZzKJFfG4&%Luie*d38eVRG%^FHr;&Uw#!-gE99=X~rp z=X1X$r((X!*>7f?f0YbI%~10cCJiynsjeb=wAa#mGtn9{0S#%|p9eeu;52d?C84h5 zB??z3uLR;qP@IPUv*G`Ge`yR;(rL=u@4WjK!>oy?iY$gnfUg*PAN&CMgtCaS)-ieG zNn2SPe8EV@dndF#Zj``qGh=EpGM&jbjhvItI8+R@g+e14A&bBhjpU?vH=JQ4VX`>v zZ6rL{|7D=M!3Pb98J;`n*F)Y+|H2waeiNsu5BQ~^xv>+d17c~mDFFOBk$Zs9@q}6Z zDC+G{)l*(J*w4z=G{$c!$yC2Y{9P(3@*9l*P9-n=uHl0z_P?8dghD4Os0T-Wl9{3Z znVM8|VT2+DnG?_xe;_Bn2Xw>ZEf=Ua)NNLGx@Xyi~Z!} zcnd`g*BLQ6!X@||JY-O5ww3jb>%yRXUo$f8uYwN7bS7;BBmGtZ0XolXlr1dpOkNAz zfIsa-?gfs=GdqzHEtUA|PGnijXuPx|+0}A5F6&6bg5pBfbVQ6P`6hVzwASG;?SR!0 zk~_kHBgv#7e+yMR=Q95ykd}C4ze^y`@yL->p5&2NP!IDFw9j9m z&8M_k9@+#dPk4nUCi(z3&0?yhysTGvTj?5fcJoYMP`T|Zw9YirO+2(rfYvKwEqA3zs`cxW7zt*_87Q1~4W?I4whzCs&9JIQp@ za!eT%P`$z{psU!%%`;7)a&srmIg@o1dz$E;+2>TgnMgEYN!|AYT&;8xk5k1aio`gP zPFDe0L6HC_vV>W=sBNVtij-Z$jvM3@Fe;G%%L@4o?IQzAT z%`&{JEm;~p5qEEEM=_%?_Kzf^W8?7c*7m~KB!rhnkX>iTQYCB+w`a81Bh0iSmV`{aJIsD0;a!Bk2_>2iv3`{y2r6)YN}Lu-OdS^D zYa!%Ohh)4ngv2H#_&p1D>*t5qbCZH)_(gzyl6+Sq{GzG7gC+=LzKK0ecgq`Z^dg

(8E?rw7gK$!uqs!PHSC5YhP+T-DCaek=RLI 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 T{5}35U-oNfk6s8*K!g7mLd&r4 diff --git a/casino3d.inc b/casino3d.inc index 5c4ee4d..584d4e6 100644 --- a/casino3d.inc +++ b/casino3d.inc @@ -347,6 +347,9 @@ skulls3d_win dd 0 skulls3d_swapPair0 dd 0 skulls3d_swapPair1 dd 1 skulls3d_swapPair2 dd 2 +skulls3d_cup0 dd 0 +skulls3d_cup1 dd 1 +skulls3d_cup2 dd 2 textSkullsTitle db "* * * Í À Ï ¨ Ð Ñ Ò Ê È * * *",0 textSkullsPhase0 db "ÄÅËÀÉ ÑÒÀÂÊÓ È ÆÌÈ [ÏÐÎÁÅË]",0 @@ -3763,9 +3766,12 @@ InitSkullsGame PROC mov skulls3d_state, 0 mov skulls3d_timer, 0 mov skulls3d_win, 0 + mov skulls3d_choice, 1 + mov skulls3d_cup0, 0 + mov skulls3d_cup1, 1 + mov skulls3d_cup2, 2 invoke RandomRange3D, 3 mov skulls3d_ballPos, eax - mov skulls3d_choice, 1 ret InitSkullsGame ENDP @@ -3774,7 +3780,7 @@ FinalizeSkullsGuess PROC uses ebx cmp eax, skulls3d_ballPos jne skulls_guess_wrong - ; Won! 3x payout + ; Correct! 3x payout mov eax, skulls3d_bet imul eax, 3 add bankroll, eax @@ -3854,33 +3860,46 @@ skulls_start_game: mov eax, bankroll mov skulls3d_bet, eax test eax, eax - jz skulls_done + jz skulls_input_done @@: mov eax, skulls3d_bet sub bankroll, eax invoke RandomRange3D, 3 mov skulls3d_ballPos, eax + mov skulls3d_cup0, 0 + mov skulls3d_cup1, 1 + mov skulls3d_cup2, 2 + + ; Pre-generate 3 dynamic swap pairs + 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, 45 - invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + mov skulls3d_timer, 0 + invoke PlaySoundA, OFFSET soundClick, 0, 1 ret skulls_handle_show: - dec skulls3d_timer - cmp skulls3d_timer, 0 - jg skulls_done + inc skulls3d_timer + cmp skulls3d_timer, 45 + jl skulls_input_done + ; Transition to Phase 2: Active Shuffle Animation mov skulls3d_state, 2 - mov skulls3d_timer, 60 + mov skulls3d_timer, 0 invoke PlaySoundA, OFFSET soundShuffle, 0, 1 ret skulls_handle_shuffle: - dec skulls3d_timer - cmp skulls3d_timer, 0 - jg skulls_done - invoke RandomRange3D, 3 - mov skulls3d_ballPos, eax + inc skulls3d_timer + cmp skulls3d_timer, 60 + jl skulls_input_done + ; Transition to Phase 3: Pick cup mov skulls3d_state, 3 + mov skulls3d_timer, 0 ret skulls_handle_pick: @@ -3913,15 +3932,18 @@ skulls_pick_3: skulls_handle_reveal: invoke Pressed3D, 20h test eax, eax - jz skulls_done + jz skulls_input_done mov skulls3d_state, 0 invoke PlaySoundA, OFFSET soundClick, 0, 1 -skulls_done: +skulls_input_done: ret HandleSkullsInput ENDP RenderSkullsGame PROC uses ebx esi edi local kx: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 ; Mahogany & Gothic Purple table invoke DrawBox, 0, 0, 320, 240, 00140906h @@ -3929,7 +3951,7 @@ RenderSkullsGame PROC uses ebx esi edi ; Header invoke DrawBoxBorder, 20, 12, 280, 26, 001A0812h, 00FFD700h, 2 - invoke DrawString8x8, 40, 21, OFFSET textSkullsTitle, 00FFFF00h + invoke DrawString8x8, 44, 21, OFFSET textSkullsTitle, 00FFFF00h ; Status invoke DrawBoxBorder, 20, 44, 110, 18, 001A0812h, 00452033h, 1 @@ -3942,99 +3964,301 @@ RenderSkullsGame PROC uses ebx esi edi invoke FormatNumber, skulls3d_bet, OFFSET hudNumBuffer invoke DrawString8x8, 254, 49, OFFSET hudNumBuffer, 00FFD700h - ; Subtitle phase prompts + ; Phase Prompts cmp skulls3d_state, 0 jne @F invoke DrawString8x8, 56, 62, OFFSET textSkullsPhase0, 00FFD700h - jmp skulls_render_cups + jmp skulls_draw_idle_cups @@: cmp skulls3d_state, 1 jne @F invoke DrawString8x8, 56, 62, OFFSET textSkullsPhase1, 00FFFF00h - jmp skulls_render_cups + jmp skulls_draw_phase1_cups @@: cmp skulls3d_state, 2 jne @F - invoke DrawString8x8, 56, 62, OFFSET textSkullsPhase2, 00FFFF00h - jmp skulls_render_cups + invoke DrawString8x8, 44, 62, OFFSET textSkullsPhase2, 00FFFF00h + jmp skulls_draw_animated_cups @@: cmp skulls3d_state, 3 jne @F - invoke DrawString8x8, 40, 62, OFFSET textSkullsPhase3, 00FFFF00h - jmp skulls_render_cups + invoke DrawString8x8, 28, 62, OFFSET textSkullsPhase3, 00FFFF00h + jmp skulls_draw_idle_cups @@: - ; Reveal outcome + ; Phase 4: Reveal outcome mov eax, skulls3d_win test eax, eax - jz skulls_show_loss_text + jz skulls_reveal_loss_text invoke DrawString8x8, 40, 62, OFFSET textSkullsWin, 003CFF3Ch invoke FormatNumber, skulls3d_win, OFFSET hudNumBuffer invoke DrawString8x8, 136, 62, OFFSET hudNumBuffer, 003CFF3Ch invoke DrawString8x8, 168, 62, OFFSET textSkullsWinMult, 003CFF3Ch - jmp skulls_render_cups + jmp skulls_draw_reveal_cups -skulls_show_loss_text: +skulls_reveal_loss_text: invoke DrawString8x8, 40, 62, OFFSET textSkullsLose, 00FF6464h + jmp skulls_draw_reveal_cups -skulls_render_cups: - ; Draw Cup 1 (X=36, Y=114) +skulls_draw_idle_cups: + ; Draw resting cups at static slots: Slot 0 (X=36), Slot 1 (X=136), Slot 2 (X=236) + invoke DrawGothicCup, 36, 114, skulls3d_cup0, 0, 0 + invoke DrawGothicCup, 136, 114, skulls3d_cup1, 0, 0 + invoke DrawGothicCup, 236, 114, skulls3d_cup2, 0, 0 + jmp skulls_render_controls + +skulls_draw_phase1_cups: + ; Draw with golden ball showing under starting cup mov ebx, 0 mov edx, 0 - cmp skulls3d_state, 1 - jne @F cmp skulls3d_ballPos, 0 jne @F mov ebx, 1 mov edx, 1 @@: - cmp skulls3d_state, 4 + invoke DrawGothicCup, 36, 114, skulls3d_cup0, ebx, edx + + mov ebx, 0 + mov edx, 0 + cmp skulls3d_ballPos, 1 jne @F + mov ebx, 1 + mov edx, 1 +@@: + invoke DrawGothicCup, 136, 114, skulls3d_cup1, ebx, edx + + mov ebx, 0 + mov edx, 0 + cmp skulls3d_ballPos, 2 + jne @F + mov ebx, 1 + mov edx, 1 +@@: + invoke DrawGothicCup, 236, 114, skulls3d_cup2, ebx, edx + jmp skulls_render_controls + +skulls_draw_animated_cups: + ; 60 frames = 3 passes of 20 frames + 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 + +pass_0: + dec eax + mov passTick, eax + mov eax, skulls3d_swapPair0 + mov curPair, eax + jmp setup_pass + +pass_1: + sub eax, 21 + mov passTick, eax + mov eax, skulls3d_swapPair1 + mov curPair, eax + +setup_pass: + ; curPair determines swap pair: + ; 0: swap slot 0 (X=36) and slot 1 (X=136) + ; 1: swap slot 1 (X=136) and slot 2 (X=236) + ; 2: swap slot 0 (X=36) and slot 2 (X=236) + cmp curPair, 1 + je pair_1 + cmp curPair, 2 + je pair_2 + mov swapSlotA, 0 + mov swapSlotB, 1 + mov xA, 36 + mov xB, 136 + jmp calc_coords +pair_1: + mov swapSlotA, 1 + mov swapSlotB, 2 + mov xA, 136 + mov xB, 236 + jmp calc_coords +pair_2: + mov swapSlotA, 0 + mov swapSlotB, 2 + mov xA, 36 + mov xB, 236 + +calc_coords: + ; distX = xB - xA + mov eax, xB + sub eax, xA + mov distX, eax + + ; dxVal = (distX * passTick) / 20 + mov eax, distX + imul eax, passTick + xor edx, edx + mov ecx, 20 + div ecx + mov dxVal, eax + + ; curXA = xA + dxVal + mov eax, xA + add eax, dxVal + mov curXA, eax + + ; curXB = xB - dxVal + mov eax, xB + sub eax, dxVal + mov curXB, eax + + ; archY = (passTick * (20 - passTick)) / 10 + mov eax, passTick + mov ecx, 20 + sub ecx, eax + imul eax, ecx + xor edx, edx + mov ecx, 10 + div ecx + mov archY, eax + + ; Cup A hops UP: 114 - archY + mov eax, 114 + sub eax, archY + mov curYA, eax + + ; Cup B hops DOWN: 114 + archY + mov eax, 114 + add eax, archY + mov curYB, eax + + ; Check if pass ended (frame 19): swap ball position and cups + cmp passTick, 19 + jne draw_active_shuffle + + ; Swap ball position if involved + mov eax, skulls3d_ballPos + cmp eax, swapSlotA + jne check_ball_b + mov edx, swapSlotB + mov skulls3d_ballPos, edx + jmp swap_slot_identities +check_ball_b: + cmp eax, swapSlotB + jne swap_slot_identities + mov edx, swapSlotA + mov skulls3d_ballPos, edx + +swap_slot_identities: + cmp curPair, 1 + je swap_ids_1_2 + cmp curPair, 2 + je swap_ids_0_2 + ; swap 0 and 1 + mov eax, skulls3d_cup0 + xchg eax, skulls3d_cup1 + mov skulls3d_cup0, eax + jmp swap_sound_tick +swap_ids_1_2: + mov eax, skulls3d_cup1 + xchg eax, skulls3d_cup2 + mov skulls3d_cup1, eax + jmp swap_sound_tick +swap_ids_0_2: + mov eax, skulls3d_cup0 + xchg eax, skulls3d_cup2 + mov skulls3d_cup0, eax + +swap_sound_tick: + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + +draw_active_shuffle: + ; Draw stationary cup + cmp curPair, 1 + je draw_stat_cup0 + cmp curPair, 2 + je draw_stat_cup1 + ; Stationary cup is slot 2 (X=236) + invoke DrawGothicCup, 236, 114, skulls3d_cup2, 0, 0 + jmp draw_moving_cups +draw_stat_cup0: + ; Stationary cup is slot 0 (X=36) + invoke DrawGothicCup, 36, 114, skulls3d_cup0, 0, 0 + jmp draw_moving_cups +draw_stat_cup1: + ; Stationary cup is slot 1 (X=136) + invoke DrawGothicCup, 136, 114, skulls3d_cup1, 0, 0 + +draw_moving_cups: + ; Moving Cup A and Moving Cup B + cmp curPair, 1 + je ids_pair_1 + cmp curPair, 2 + je ids_pair_2 + mov eax, skulls3d_cup0 + mov ebx, skulls3d_cup1 + jmp draw_the_pair +ids_pair_1: + mov eax, skulls3d_cup1 + mov ebx, skulls3d_cup2 + jmp draw_the_pair +ids_pair_2: + mov eax, skulls3d_cup0 + mov ebx, skulls3d_cup2 + +draw_the_pair: + push ebx + invoke DrawGothicCup, curXA, curYA, eax, 0, 0 + pop ebx + invoke DrawGothicCup, curXB, curYB, ebx, 0, 0 + jmp skulls_render_controls + +skulls_draw_reveal_cups: + ; Reveal Phase: lift chosen cup and lift actual ball cup if different + ; Slot 0 + mov ebx, 0 + mov edx, 0 cmp skulls3d_choice, 0 - sete bl + je slot0_is_lifted + cmp skulls3d_ballPos, 0 + jne @F +slot0_is_lifted: + mov ebx, 1 cmp skulls3d_ballPos, 0 sete dl @@: - invoke DrawGothicCup, 36, 114, 0, ebx, edx + invoke DrawGothicCup, 36, 114, skulls3d_cup0, ebx, edx - ; Draw Cup 2 (X=136, Y=114) + ; Slot 1 mov ebx, 0 mov edx, 0 - cmp skulls3d_state, 1 - jne @F - cmp skulls3d_ballPos, 1 - jne @F - mov ebx, 1 - mov edx, 1 -@@: - cmp skulls3d_state, 4 - jne @F cmp skulls3d_choice, 1 - sete bl + je slot1_is_lifted + cmp skulls3d_ballPos, 1 + jne @F +slot1_is_lifted: + mov ebx, 1 cmp skulls3d_ballPos, 1 sete dl @@: - invoke DrawGothicCup, 136, 114, 1, ebx, edx + invoke DrawGothicCup, 136, 114, skulls3d_cup1, ebx, edx - ; Draw Cup 3 (X=236, Y=114) + ; Slot 2 mov ebx, 0 mov edx, 0 - cmp skulls3d_state, 1 - jne @F + cmp skulls3d_choice, 2 + je slot2_is_lifted cmp skulls3d_ballPos, 2 jne @F +slot2_is_lifted: mov ebx, 1 - mov edx, 1 -@@: - cmp skulls3d_state, 4 - jne @F - cmp skulls3d_choice, 2 - sete bl cmp skulls3d_ballPos, 2 sete dl @@: - invoke DrawGothicCup, 236, 114, 2, ebx, edx + invoke DrawGothicCup, 236, 114, skulls3d_cup2, ebx, edx - ; Cup selection indicators +skulls_render_controls: + ; Cup selection slot labels [1], [2], [3] invoke DrawString8x8, 54, 168, OFFSET textSkullsKey1, 00D0D8E0h invoke DrawString8x8, 150, 168, OFFSET textSkullsKey2, 00D0D8E0h invoke DrawString8x8, 250, 168, OFFSET textSkullsKey3, 00D0D8E0h @@ -4042,33 +4266,57 @@ skulls_render_cups: ; Keycaps mov kx, 16 xor eax, eax + cmp skulls3d_state, 3 + je key1_active cmp skulls3d_choice, 0 sete al + jmp draw_key1 +key1_active: + mov eax, 1 +draw_key1: invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKey1, eax add kx, eax xor eax, eax + cmp skulls3d_state, 3 + je key2_active cmp skulls3d_choice, 1 sete al + jmp draw_key2 +key2_active: + mov eax, 1 +draw_key2: invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKey2, eax add kx, eax xor eax, eax + cmp skulls3d_state, 3 + je key3_active cmp skulls3d_choice, 2 sete al + jmp draw_key3 +key3_active: + mov eax, 1 +draw_key3: invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKey3, eax add kx, eax - invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKeySpace, 1 + xor eax, eax + cmp skulls3d_state, 0 + je start_active + cmp skulls3d_state, 4 + je start_active + jmp draw_start_key +start_active: + mov eax, 1 +draw_start_key: + invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKeySpace, eax add kx, eax invoke DrawKeycap3D, kx, 192, OFFSET textKeyEsc, 0 ret RenderSkullsGame ENDP -; ============================================================================== -; IN-ENGINE 2.5D MINI-GAME: SKILL CHECK (DEAD BY DAYLIGHT SURVIVOR CHECK) -; ============================================================================== InitSkillGame PROC mov skill3d_state, 0 mov skill3d_timer, 0 diff --git a/poker.exe b/poker.exe index f8df6dc3d870ef90b3a5a7113a6b9afafec83769..1c3578f8715cb3c6d4c2057d213caf7f69e7061c 100644 GIT binary patch delta 12140 zcmaia30zdw`~E!#Mnypo*#uEQKoHa!n0@Bn3nFN)7`fnXnT}(c8n~5?HfR~<(n%+^ zQZlvNYcyOF#l+H7)LbbsEiZzKJFfG4&%Luie*d38eVRG%^FHr;&Uw#!-gE99=X~rp z=X1X$r((X!*>7f?f0YbI%~10cCJiynsjeb=wAa#mGtn9{0S#%|p9eeu;52d?C84h5 zB??z3uLR;qP@IPUv*G`Ge`yR;(rL=u@4WjK!>oy?iY$gnfUg*PAN&CMgtCaS)-ieG zNn2SPe8EV@dndF#Zj``qGh=EpGM&jbjhvItI8+R@g+e14A&bBhjpU?vH=JQ4VX`>v zZ6rL{|7D=M!3Pb98J;`n*F)Y+|H2waeiNsu5BQ~^xv>+d17c~mDFFOBk$Zs9@q}6Z zDC+G{)l*(J*w4z=G{$c!$yC2Y{9P(3@*9l*P9-n=uHl0z_P?8dghD4Os0T-Wl9{3Z znVM8|VT2+DnG?_xe;_Bn2Xw>ZEf=Ua)NNLGx@Xyi~Z!} zcnd`g*BLQ6!X@||JY-O5ww3jb>%yRXUo$f8uYwN7bS7;BBmGtZ0XolXlr1dpOkNAz zfIsa-?gfs=GdqzHEtUA|PGnijXuPx|+0}A5F6&6bg5pBfbVQ6P`6hVzwASG;?SR!0 zk~_kHBgv#7e+yMR=Q95ykd}C4ze^y`@yL->p5&2NP!IDFw9j9m z&8M_k9@+#dPk4nUCi(z3&0?yhysTGvTj?5fcJoYMP`T|Zw9YirO+2(rfYvKwEqA3zs`cxW7zt*_87Q1~4W?I4whzCs&9JIQp@ za!eT%P`$z{psU!%%`;7)a&srmIg@o1dz$E;+2>TgnMgEYN!|AYT&;8xk5k1aio`gP zPFDe0L6HC_vV>W=sBNVtij-Z$jvM3@Fe;G%%L@4o?IQzAT z%`&{JEm;~p5qEEEM=_%?_Kzf^W8?7c*7m~KB!rhnkX>iTQYCB+w`a81Bh0iSmV`{aJIsD0;a!Bk2_>2iv3`{y2r6)YN}Lu-OdS^D zYa!%Ohh)4ngv2H#_&p1D>*t5qbCZH)_(gzyl6+Sq{GzG7gC+=LzKK0ecgq`Z^dg

(8E?rw7gK$!uqs!PHSC5YhP+T-DCaek=RLI zpy~7a*8e##fah?Mk*z(*dDKYOTf(Ib0M&y7CZfg11?dmiTXwgZlXPcR$@ zn<%EFSU)#rqgYppv2M&-L!Q48-*gD5O0#pyWM7gA{ln~C`zyS;CW6cDOgH_(4VT+7 zZmg1GqTAQq*zp_0o*R$9y1C?@5JolZD;WJgI{H(W$hI+O+djPrL1B-^c41`iwKaWn5;*2Bfm7fGoIfv;Z^oA4 zsEhXTrci{RowpZ^yMb}wA7sKibMTV0_B-!jgcqJ6_a`LaH_zB3CoV$xbh&-YyTIOk z%6?>0Q-rsiB&R3G;rS=YlgT}??j*^YvLDYpVVAwP65+p&+l%tTF+TJg**{f|m;6TV zOdW|k{YLstTZF&ch~N8__)bs9@?S~c>AUdMqr_)M4vs!bhR+DaO^%XjGa_+S z8CgDKE*?`xTFp$tx-xs#%+mwm9XAi@T4v`E?Km2JaIcaH2c3Q&lVA@n*U8x<<`3d8mC1lTs?OG<6 zFwDG?d=pJp>P(0(A+;ZN2z_>dW|Leq@;HmgW(HA8j->ZKSsq^rLJYwVvqUD}Fu6g|9~I$qe}VcO6*)Uq{xF-K$i1@j83$sy4=_6AOG*E+eZqad^!#a%IyP ztX*dBw)tm-rxSbQtqTyowUm6eZ6N-9DXHF8h#M~@)3jQt`AWy zg7obI>m;mw30ZlYW^VK%G8`_-<)4zRz*zPvFunn$;N@n_{gk}4FT!^hl0`e>@NWyr z&K>RWx`pJ@j%fVhLi>vyC=g$q`B_;o)mg_Egx%4Fhq zN{#iC?U|>(za^-Y8{QgAZeWo2AFp(s}mtvy5&l&8EJDTmUpI?bum5zp0{H9dA zrUyCl104Cja9ovY-uy~G z1cZ^590;o`cTx7BYx(eb;#wwr23_wS;`?+S%vgYZF9;7HvJhg%ENa!V+ME`O*Bg3lvWiSSt~wq-Y?+YjiR!I<_!vYJ~9r|30X8dXU`1|#_0 z0U5R^jEeNWsqFO=aLwb)N?|$>ORaS%Kgh&v1ob|bVuYl?RB;{@hg^y*N#O;GQ?wEu zCBNSc>vmF7%Y`6GEu(g{zjH3TXE20P$ybF7yFoE}RE3(NqJaDpNv@92NnLav)2Ir8ZJMCLfr8bwV9F8)XH#mfy&kQM*e2*d%?}Ub1yNQXJ z_nJfiJrKl)Pouh5*JHGS;q_nRRpB3G~1kJa(sWYmK$lID{Jn*82m*8_F1nZ}BDX~dQ4 z(3^NYR7o}uuhsWf*^Bgkm>4ClZgB(6H@%?n_?u3_Bu%62%O7^f&fpxS!5MSa6TJtI zci@p6*;v;lY(OVq?+XKhu@b6q7|d}vEYtwI_mf9S*UsjGnBQnyVwwKA;bdXX`O#S{e z^*f~7yb$V%|4g%GBb19jOtWo5D5+`xH0dztpt4mW6pj^Xw&w`_S_LY28^KxuRs zL1{yc&{m`=-rfiuN8J~8az|vY)T9xaDm7_D#!F2ak&#l9Mx?*kbVnqslkIR52uZTe zhI~+LL?fLTj7sQQ%z9b}#9-Xg+R}VbC_byT<@!KSKGNFO`ydN$q_wqgilXr~jjad$ z{wmovp(zCATa9f&Q?xYJ9h564-Ln=Cq1Gon6XTlwCq$ok$Q3jSWxsnuM4F*0*Cwx_ z%A?1qQam_ppZTG9XCl>7-NJgh6MiVsse3?mz3b`xnxjchU7gtBZ=TVS=IA>JyU`!5 z$Ejtq-;$)*l*#^r7X2%e{q4s7g%+(`W-sCC5r<>#k)#aU;}%GRuXMJ_15u1+Z)aMy ztnAO!I8P-tkbl7dh_@tQ{{o9!@bWKM=@z{G3$B1bYhJ%tarryKhRBkdF|hmwso4vb zUoJJ%q29zv&7q}5dy;JBfv6+)@4UPv?0~9Hw)mE)jioO*1ziS{D5GlwV<*2zV~4r@ ze`Yz3GEajoSPi4?b5gq>ZC`?RnT$>qdVtm;Xwg5B^;t+ z#?o#NY!c3x!%blj8ndrCNO*bJmB%+>EaqTyvT&8sktsDQlQD_(K=VX%GH_2*u2>e_ z8*C?Von=MbVE0O06k2PbFm)F`v;0qgrl0A&=7`2_k$&y-LMO^h%%q z89@&$XG-UXIn~K92hOY&+JLU=4`2bo0xl3`(A}EgV!VK!tLh{M5U`7oArX>=3QBkf z;w1Iql^-m5cvB*@7WM+6I=|=ySkEiTdqaxMd`X)id_`v_7F>&pQiMx~8ia;3eM~1Y zjnc%JekNL)zN6?U+IKU^DAVZCFz9tIr>{}`K4b)ax8zXKU1FYMjujd|fh7|mS`^Gv zB;R-IMAQOIS|I?2mjiIy0k~=$IHz}FW)Jw)H zW!LX|T0H}c%Rm0FMg37_(NUd+sTI-vXsrvH8f>jgnA*lFBreFTE25ivLFR+%*4?Q@ zw?37cTZLdM)9nwkb=Cuv7Ym;_&sdgOOJ`P=Swrg@d<#&J;OjaKd$k_a5?n*~7icLA zS{kA1)*fT0)@e(%g_+lrGKB@1rwTJq`ESdtOFg|i2;j$e7i1nU%oMybk1xoq6iLvi zD9o(0i29_X{@V@$x2cnRbk`JUC1oC6ka#Vw#`IC$2au_me^P3g+uUQaG zrRwH}%$j_k!v3|AEY3AdoNH_mO@xoc17R&~R>u_4#rQ}@1H9Hk+tmS1k_d^3LyuXp zu0M;?=Aew-M;s0R)v-4PpgL7HkC4lJUl}IP?MU z72H~ESyWQ+Xc@H1U^>GXEDcXAjN+;!fKH6bLi)r_uYlDSr#l)=aL0KC2TAUfk~=y# z2XzOdqD$;@W-AXzVHVeFImF0G3^=ZwNdUJTsk>$LPPVNbJzc0L*(}%wvC#0E)de06 zB{L}#7(nTt7~}CJUPxR8Q7W%oOH)D|zxYHuyj@FiQ6+o&@Jw^h_I?Cv1=mCxbE?7&v*U}XOpSJT6h_!Us(Gx$31NZPwT*-+ z_0(ZOfekKf13)KTMC5eb!EgcB?G-OEcza{)KH9?MQM;(^z=8%f*tMN*A`zT*Db}dYz6v3m#i79!nZ4 zVIQ@W9zE$*FUy=J45bwD9yCa_l71%7@)*)Z7p&A!WQkp7ndtE^MD4N7ii9V_=DTex zB2jd30cB-s<(O$g7UgQ%VZ_a;-+`A4aTbe(S>V0X*Y-y= zicZ+J1I8K<9>bUeObK-J2EaLzO2O@aKV;x-x;Y3Qn=CtRZDLSJSW~LlxN#$6ii?4n z(Wev8_SRH zIPeGH@Dq%)gC_aD!ty@>FTY~@K!Fro-M0{0IN2CezYy4@gUkbj%j*C#@4LX5MpEcq zb06U-h4${<%a|qrWU_)kK!03UT&+Oey^(LRQjOZeZyLpKXizyGAM+2aNVlq&ut)RZ z$82V5P?>B7G+_RW@InCP{+gGLQeo>oXf}KH(qWpefFC;8BhZ>hTRl&JV##&**hD`v z9H6*D72%Y!A{ANz&=O47Y)ka0#uE7p23(v`g!@9b=MEKGl^8>6SC&^sQm zm2Qllk3!e;;NbHipqT4c&=U;a#Q`?KjWwd!`ksL0y0ONv0~w)}5x`70NRLmUNeW;J zH%4(Xt`WhoXesOm`uVeeo8|HD8%&-{rteB0{-984>3D3 zZ7b7J4DQd{eojX}`T3qgOsCPK@I-{oRZUX-MBZn;oNv`IyaYF#I54K<8E<}xEEY= zK9rB)l|0LL=lk&e`N8~HegZ#(x9|&i!ms7G@q77${4xFlf1SU{KjxVz#9W75NAl+K zaCwxxqg*2&Ezgq|$XClZ%fl31EsEC_gA`*G%M}|GTNL{gClr?zcNEVR0V$CwiWDxT zPs-2~bIPO?OUkm8?I}l7E~ng02~2IDnwHu>b#m(b)Gt$iNIj5xGWBcaX5~KR@5(dE z3(70Xf0Q?sca@Ko&y_M&luE1Wtum`7sivuBsR~q|s1B-rQ(aJ9SG`a*Qd@%5QEH<) zQ=Ow8q0Uzqs=rkqQXf-Ss2`|bsGDozHJvqSnn9XTn#r1vHET2*H9IsvYmRD8YbrGV zYHBrJ+5l~sHcI=kcB%FY?f2RrwFk82+DqC;+UHs?osTX+7p#lXCFnZow7M?3zPh2h zH+3f6ES*iaMz=$^M|aqwJE6OvyP~VoJjU!!7bsw z;p2sba@Z?5cz!h68X3CEpofORQ{{{PkEIbDHTT6Kst;6F)e6;CRVl3R4b^ScOO;F=tZuDNR;Q{n)V{nN-AF3T{A5BY5q^7+_p)qK9O{S)=CRg*8W`bs#X0E1CvrMx> zvs&}L#;)0~Dbti|E^GeL+|k_EJb?`G(}rrJwMkl)me;$b1Lv*8b<8}Eu zi*A9A=)Tfz(EXs>soSgjMR!tnQFl#uOZQlZ^v(4C`e1!qeWE^9&+5DB`{{@1-`0=Q zzpJ02x9UF?Gi8&0C*(<~{#X4O{h#_9`n!-TGK0UNl_Ap5PRy2cLr+70gJpzaoZ&sg zG{Y>zM~0<_RfctjZHC>3Qp4|ta>IGURYSGmp}}GBF}5^D8rvI_j0&T{mq&?UNQb-ykmS~^kV(kP&S%PVpXh;<=NNR9CjEx zmYvML&(32Pv&6!#VArx+*kbk|dyM^qy~0+px7hpa3$`)Wf{WndpdjhE3@Avwxq;j$ zC`tL8ggAHJHuOA|H?F|m-amSlNX`B11x3n5SU$ zwrZv7uQEY?uMa;NrnQ$7lt1U zzZfnWZW)j<*qCT!js1*o8?8p0vB)gVi^%>1;Q)H>`gyJA!?i9S=D$ zjh$s-t?U9wgk|h1wus#TnP6vkvj^B;AREfr^X#AOHOPoN>_he$i@3&6s#=N%RUDVZ zDL4(sLT#}Fl>5h0TJ;>_`54az|dVGOSrc?1p zI!ql^2*hEe1N`3u{;%_wN>Sx)Cw)0){1+6}F`g*WDJlVO5%yl_1>Ja?j;8WnQ`w_& zq^u>HsKcW@6C%H?1;Z?qxzVupREBxjtoD?HrJyau>hMrmIBKZFXFWTk>slNti$gzZ zu?hN5)e>D+mX@Z5WDY!j*puqN@UbJOQEk^^zzJw+SPLuzpd!QU2XgizXD6_Tpi?X#1(R(?b;**Hl%W;uU7W$hUfAG1BvfA12G@VC>G;B*OIC7FL6#38G z=z0fA;M(F@e%(<}8+_WYGeT`}a5EO2Ou;>ywQi2u&?3{xWr&<0EKI?Rn;Fph6nv_g zBIa^3P4yEdr_fZ^pn(+`c?R#e4iuf^ZK2Kkt6=~~GLH0b;lls{2G4GgA+$@uUHyMS z@yYm^|7aADjE4p=NRy0L21KKzB)ltN2pX1zLj&W2bxAa3PI(Ac7S}i&=2BQ4;bRGu?83B6oGm$8L16Qi9G6 zTp@%+w>;`5$j#kyE|D9%Wr4_V5?m2@OXO;|Jmwb2SKablA|H3lAyq*D>~;eQRUmIn zz|De#nlE?50$G{aM9vbfw#KI5AnORXHk-u#6OgkqA$j`rb*3hXrol~nMrdvRLYqry z!EV}EA~*O8O-%Hst({Y~nrjH}76A1}-9pyjHy6+R9g%nah1QNly2wonBJ#4o&`JqB z-A((R$QF=s-;e-nKeys0iEP?m`VSxrlH#Tri5&44+7$xJ+_XbPei{Fta~nhkxe2fo z;eO^c0-X5^FQ2SpnTuy0OXOc_X|=2KGr_Q%vx3NZ@mLd@++_g3kCKNp*bEA8;9mKw=axMrcqKT-WB%+IvRpo#9u>mLhze-t$&{o zv5@ARbg-X6sS-Me*wb2bG+G^mtqGk_-yr+3gz+@u1FaBOmd&kBE+J~F_bnH(fTt9}yAAp!mbNGHIva*;t3z)XPK1jrI<-r|Hb0X2AQ_wV>l<{$k6^lPaUy7p*k z@&ZS83$ng_PLUHPXR3KdOb76*R(ls<^CCC44*)0Ksw4 zkzfqL`nWJT!8#Jm;KCxR@%vBWyB6QFOf=iqg?)Js+cV>l;hz1E%=8B6)mnIrjT*m!Jn!;yarl{(N zaC(_*W`kFs{RrOgBi?^?8E+kVF%n;b$Qyo(?Lf&UN1BVz0>||E@P&zSDDo73GqF3ma1y6a+K&QG+GUdqX*Bh?y(l{jp+S{+{}ee&ti(^J z3`3R2aG$A5kmeY^Jv9+|AH&|c?a}e0xOeU@MvD0+SnnhJE`pdHQHiO{`$c+c!s0Y~>y z)SU7hGf7rrOxUvz*UU}|SphhC+DKYAkPIQ|Z2PbxZ)k|VR+qUh8+4Wi7TSV@fA7V= zaG>0(aRx=Z;3`mmPTG{2nNChyCFE^)%XUw~s2w z^+b_d@$DsZL$+?As7C>op~8C7@G_T>rGSg%n1y)CNwqkEopGp}IerqAWhbEpp2c zV#zH^I4^-G;8p?RVYrXnNK-j}z}zv}9&*y#6Fnb? zcRKeSLb}oT)`eCma5VP(y%lmSNJt^wdky-Fq{i*P>2i|f&qv^UHPN<(?>tXkP(1}Kq2jG4}BI+^#&lI9j+yGo8 zbVFDB<3EK87;<}cAY6}C!$Tmh5!(31`1|;6jCD>zosNKrnJtg_xtFfLwx89);^9AI$T}zS}A>5HRYv zA?dKB^8j6~C$k=f(-ruKN8zE;uoBNyXJmX9C?)ujdzfovn3^Y|BmX&!sJkfX>I`Fg zOokcs*fosEn?-oF6U0j%N26!yc>CkMXkZ^a?oW9TSsr2OBRI!tcn@nC0a8F8Z2z+d zD(-~oCyEx!Gr*Cb;N>>YpiNApf_#@O1;IQ4+UDiB_mgO(=!7Rd86Gs1Sju`>Ybh-p zq?^65@T9FYMo2x)_q}oQ)7B9d(&vwAhoe&F2?k;3Q-dlb+x_vlry*ex@P-YEncY<) zfF1~zc)_zHUh^~(-A%KXKlP$(Gu;d(gz17ir>bfu(U)+)eaOq#ahBj|64dhiRD+Lxzk+LWSkM?-&v%rpjrq)x!u}MLaUapUN5|kAV)kkFn zjS@Dc;@8hQqdDE}?VmTHrHI_VS2tE)cEiJ8bdWR!^)yYp;axA(K|@HaOiqosQWagX z$4ge~^YU(8Yty>oo-Y%l#MLdUr{TLo;qf(}gGrkE+gH8pf@*{F8wt+H+wSPS_;)FK z!{L(G9YVXc1@^vBAn2>03U7xw4uOU0X7_xPNwan8%!8Y*{!o0(o5}3Q3Hi`zmVp?d zvV*1Acf-ryj;vdudXS#KURyvaRMU5%vMzs9iHC1@C`l8fW-5Fg_)=9AxS#is9^d@1m(YUMbZN8!emo9I~d(y;P89TI-s0JXhR)-p*-IWhJ7lhm&; z*gR;uJDOs!Wzcjc3OCp`(R8xc6TNg8R48m!G#!Rc=xy(5`hR>CFeQt^5-NmVA@jD? zAR20*P3A$jZLJe^=ELBJP;r^4#c~`5g9_9oB9j6}m1TL*%baVVB+AU+L8*7>L208K z&|B!_vTY6MQ*@WOWLHGOq$Y`ouhb+F@sye*B5#sJE{Vt!vFVD)tt8u#Mi7#ttc`9= z$A+(A#b8vy&|=n;Iv@sPE^A9|OoyPctSz%K1SN{Kt#3?Q(Hh3q+KY}xGZPxGB8>!mjtF*P|ed zhY(ZgaI8C)oCXh-C*TTkvIY6m8ni0K*2|xcu}(@Mbt%tq0F+cVD>Z1}d`JeUn*>F^ z`39HZ;hUf35Kc*FqK$G>zhABDj!}PWvbxn`>3VN96 zVQ961mbg2nlHJjn7mdLAK-LzEea)E`ruGG0dkrM#|Qz!Wf41P zhf2PZzV2N=F^ME@lvNJiSdg?YV|?Y58Q#d?H4VZ6kMRwJ+&XIiMq!jC%p6K|-j+aN zFzBR&ya}#Zf?(N9$4EgotD-tlW|8i18D~iWE*VDJ->#h!09^T35Qd;7bCBvFiGk5PA52??f4V|5SL|ut0I+Zn&_@C05hK9m)lQ2g0Fp;f zY7e-V((aP*iq$?+0yFxfB^I<-4LhL$X^AxFhGiPL;jH=696ANWYBTg z`!&EPf0eQqu2x_hCl=~zy#}pD zS-gc2AO?8Ih?G|zxXv5y75xSj)!3pV=uzl#3Ms>piI2g$yS8MT)0SL!Ps}+T_wbivrQr!@ zVGx;koM|Al;oSqI?@B`=J4jE6#M3HfU$}sD`ejM z;Ta~6AV0BXWu@?udYdOB^Ffk(1dvvR{O~0@wTQeH$ib;c)>>;MnqbZ75!47;R{pS+ zk<5|FIYe{+r!_4(mF~6zYfASDI>cZ~eLP$qxK6T_q|a99lV>2V%D(o_Gmz+eeBA*a zX_KXXQg%4=NsRmGat~TOFGcBm)w(EKc}qIjOwx}$PoTxaA4u%1v4j*DE&mh{f z_JhRwVt>8F`UJ%Kc~wMwJ1^vwd#)pk242~Wk+cCV+i&|il5T;n?zjCANr#Qt4?U$K z8w(x8b7$=V@!UyelPGg&pg(1Co;%A3SFES5b0-_Pwa=yE6WaEDPKxW;>2tsq7Db0y zP2x!8k+tr*6X`ZUGF2E%3`h^Q;<+WAJ8xVgrxP#J;eq+SXihBlWU4}|`Xi@XQiTda z5es^cHfcSfoAe9aKioaZx(N^WI%%TM>E;?(4PhRz;4JUoB^5qzH}i#5HeUzaG7_FC za`vr?rlYNoNXJfEx%824rX%OIa0Q^ePOrTmorFRphAJu}h_Zw_SdyF;i-RTUDujZw zKoHMIGFd_(dw~$h?tt%o4u_cEUBr(rmIRB3HW(YlqGX}A|uK?gq~7B3S6pGp%F2(4OG%#9?)G}ucI!` zK;a(DqMAJJNgU6dPbE8-tS*vVC@0e=%jaN8`4v9w2HAD=i!+v%)g}^27M6kI(xg*` zZvcIiB8~kom)R8ItN+UG2Pm9s9m%lzhR?J*>=hx}1C zycN8e`I2vR=Y=?{)k-b!+(Fs&adh;6;9W2Yzpxm};%9~*4dDi7NMRX(+wQ!CQ>>9C z5S|9_6IO6wAZ2MPvdkBVTi(J%LM$mMq0DhHFw=h$G*H;J%Vv+Ga~oFO`REXot;^uI zu0n)P+ihnO=y>$A-S#Sh{xPM&9r8uxBviVF&<#Fi$#VPzD}3aUz(x}24d9Hcwu6cE zYjkIO+3qCTr!ie)tAt;Ltn*6XH9veaT&QhunQDj2hEiBbR!l8ontV_1vjgx+I{|Ou z!V%y*AjHDo4gxN7;U0ibfuEFwtiyo+2|nZ*h#gPB=fL}w@Cq)~WS)73Pl-YXih0LD zkq4LR{jLrT0N)9(lEU_LfdA~m8v_0UUaW=qtAHElO- zIadK6X8(R1T60M2*YlwG_XD{26H_G!hQCD=-w?`b6||;8OE`NU#(*CmcI3VOki88) zlKSS~f^QB@jPw<2{%=4y4}|K&AIWN8jjweeDgi>W#f6cRStv~etk8v#vr^b}4Gg>+ z0*Y^33Uay$l?uSdy08WW^XUy(rVDFG9?pbWCICCRKys1_<5|EIE{x=#@UA~#EnHY* zg0&^1|9u=NJY5PeqS!wMu$m9fDFH+Y^H&0Py&h&2_T&QiTRl){oDW!OeayH5u(kEE zJBtBZR39_Ffmr6&$E@!G8&MyN>JEJ~{OW^fwQWKw9f1;7*_OaPIAA%DsQR1_`m*v= z`WLjbBb`qNJO4?N4@{68SEYSusrD^0h4pz(6I(1KVKlNwoQR<26T=i`A zQuX)hBK0Qq4)sCxQT18%b#=A+x%!RTL*u6j*Tib%8cvg@>7nVP8K@bq8Lye9nX6f$ zDb#G%?A9F89MhcBT+(D}=V-BZwRVH{vR1HapJ+d519Z{4);hJWt1e47OgCQlK=)MF zi1XvZI0ZM98^g`!R&(pQ{oFC`ckT%%WiiunqxC{}D#>{gU3&M7V{9w?r`;>wgk$_S-W*;zRl z7I&6aIS*F%C*>C94&_nhDdlzLedR0V2W3-L3stO&QSqvDRd>|@RlaJ8>N{1jYO`vm zs$6wKby0OibyFp%o~hoe8ZdrL7!%91Wt5DT>A>`01~9`I3zN;vWacxAnB|ZG>zQrL z9_9#hhPlGrVeT`JnO6+Oda>3(wk4auDp@_7%BHg!Y!*9$9mnRfv)M)LDz=ba%a*XE z>;d*S_5ypIz0cOLFW9%Nr@E;+R2`#EQLELd>Tc>@>QB@|)MoW0^$hh~^|$Im_21PS z)LYcM)Q8ol)R)v%>WAtV>W^xqX{7PjL}=p0OzEiUWrai;tod9sMl(e-OH%;J@{?wR z=2y*5%|0<*u4(RRo@icckk(u4rw!IdX_K^!)~M~G?W-NE9jUcwCupZ@^R-K~-)W1r zo3%T&<=P|K6WWW~n_5BpO#5EjKe}kGx(>P?x&e@57G1V3S2t5<8mP(=W>g< zRoq&xge&C^aKCXExa(XMcb}`_-g2INQ$CcB;Zq<xyX8k1nOntt-Kz~$!L;q6mV~B%1?Qa+h30h`2 zVYp#%7y^u)jJ=GX7>63aGERab6=7-xB`L!+#x&hD&r}8_=#uGyiAtrZd*pDHd&!&2 z+sSqEKJo$bFXU$VG`Us2K#t`$x%EeRk$k;;yL_+wsQjG#s{FRxpy;INr}#|qg<`xS zN0F~6RIE{KRqTKQRjIhBxTScg2vMq(pD90w@-$01SGf!lxJK!%idGp^y;S{FlT~w7 z%OP1wVIqloO;x43uc}eKQhii4W?C`{kf;p9F&&w1OkXSW8Put%%-2jlRI1g?Pf)Aw zFfCXuRH?p@ien)a^VtRL3P?qghzB7NPeUqRhGe|WzGj=K{lzl%sd~72x;kGyRkKL5 zR#U1ut+}mvscEcjp;c(RX@_VhY3FJSwOh1@wU@LHwI8(+I=PP1_0m~K>2h@Qb#~o8 zSo@2*Yq~1keOUcxy4SjoI#0*|KQ5Sy;9?*P+HwrXajB38J-EKyKyC=+f`yyFP2pxh zKFsHqa4Wb%$cXjaX6{#R7i7f|?gV#^y9AkWhZCT1y@1@H_y)W;6tGafB_GQtwdduK zBznFB--YkRf5K<+!}yW>7=99;%g^NV`2zl1eii=%U(9ddxA3L>9=@DE#-HLZ@K^Yo T{5}35U-oNfk6s8*K!g7mLd&r4 diff --git a/tools/generators/generate_rich_minigames.py b/tools/generators/generate_rich_minigames.py index 89a8b0a..a6380e6 100644 --- a/tools/generators/generate_rich_minigames.py +++ b/tools/generators/generate_rich_minigames.py @@ -1260,9 +1260,12 @@ InitSkullsGame PROC mov skulls3d_state, 0 mov skulls3d_timer, 0 mov skulls3d_win, 0 + mov skulls3d_choice, 1 + mov skulls3d_cup0, 0 + mov skulls3d_cup1, 1 + mov skulls3d_cup2, 2 invoke RandomRange3D, 3 mov skulls3d_ballPos, eax - mov skulls3d_choice, 1 ret InitSkullsGame ENDP @@ -1271,7 +1274,7 @@ FinalizeSkullsGuess PROC uses ebx cmp eax, skulls3d_ballPos jne skulls_guess_wrong - ; Won! 3x payout + ; Correct! 3x payout mov eax, skulls3d_bet imul eax, 3 add bankroll, eax @@ -1351,33 +1354,46 @@ skulls_start_game: mov eax, bankroll mov skulls3d_bet, eax test eax, eax - jz skulls_done + jz skulls_input_done @@: mov eax, skulls3d_bet sub bankroll, eax invoke RandomRange3D, 3 mov skulls3d_ballPos, eax + mov skulls3d_cup0, 0 + mov skulls3d_cup1, 1 + mov skulls3d_cup2, 2 + + ; Pre-generate 3 dynamic swap pairs + 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, 45 - invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + mov skulls3d_timer, 0 + invoke PlaySoundA, OFFSET soundClick, 0, 1 ret skulls_handle_show: - dec skulls3d_timer - cmp skulls3d_timer, 0 - jg skulls_done + inc skulls3d_timer + cmp skulls3d_timer, 45 + jl skulls_input_done + ; Transition to Phase 2: Active Shuffle Animation mov skulls3d_state, 2 - mov skulls3d_timer, 60 + mov skulls3d_timer, 0 invoke PlaySoundA, OFFSET soundShuffle, 0, 1 ret skulls_handle_shuffle: - dec skulls3d_timer - cmp skulls3d_timer, 0 - jg skulls_done - invoke RandomRange3D, 3 - mov skulls3d_ballPos, eax + inc skulls3d_timer + cmp skulls3d_timer, 60 + jl skulls_input_done + ; Transition to Phase 3: Pick cup mov skulls3d_state, 3 + mov skulls3d_timer, 0 ret skulls_handle_pick: @@ -1410,15 +1426,18 @@ skulls_pick_3: skulls_handle_reveal: invoke Pressed3D, 20h test eax, eax - jz skulls_done + jz skulls_input_done mov skulls3d_state, 0 invoke PlaySoundA, OFFSET soundClick, 0, 1 -skulls_done: +skulls_input_done: ret HandleSkullsInput ENDP RenderSkullsGame PROC uses ebx esi edi local kx: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 ; Mahogany & Gothic Purple table invoke DrawBox, 0, 0, 320, 240, 00140906h @@ -1426,7 +1445,7 @@ RenderSkullsGame PROC uses ebx esi edi ; Header invoke DrawBoxBorder, 20, 12, 280, 26, 001A0812h, 00FFD700h, 2 - invoke DrawString8x8, 40, 21, OFFSET textSkullsTitle, 00FFFF00h + invoke DrawString8x8, 44, 21, OFFSET textSkullsTitle, 00FFFF00h ; Status invoke DrawBoxBorder, 20, 44, 110, 18, 001A0812h, 00452033h, 1 @@ -1439,99 +1458,301 @@ RenderSkullsGame PROC uses ebx esi edi invoke FormatNumber, skulls3d_bet, OFFSET hudNumBuffer invoke DrawString8x8, 254, 49, OFFSET hudNumBuffer, 00FFD700h - ; Subtitle phase prompts + ; Phase Prompts cmp skulls3d_state, 0 jne @F invoke DrawString8x8, 56, 62, OFFSET textSkullsPhase0, 00FFD700h - jmp skulls_render_cups + jmp skulls_draw_idle_cups @@: cmp skulls3d_state, 1 jne @F invoke DrawString8x8, 56, 62, OFFSET textSkullsPhase1, 00FFFF00h - jmp skulls_render_cups + jmp skulls_draw_phase1_cups @@: cmp skulls3d_state, 2 jne @F - invoke DrawString8x8, 56, 62, OFFSET textSkullsPhase2, 00FFFF00h - jmp skulls_render_cups + invoke DrawString8x8, 44, 62, OFFSET textSkullsPhase2, 00FFFF00h + jmp skulls_draw_animated_cups @@: cmp skulls3d_state, 3 jne @F - invoke DrawString8x8, 40, 62, OFFSET textSkullsPhase3, 00FFFF00h - jmp skulls_render_cups + invoke DrawString8x8, 28, 62, OFFSET textSkullsPhase3, 00FFFF00h + jmp skulls_draw_idle_cups @@: - ; Reveal outcome + ; Phase 4: Reveal outcome mov eax, skulls3d_win test eax, eax - jz skulls_show_loss_text + jz skulls_reveal_loss_text invoke DrawString8x8, 40, 62, OFFSET textSkullsWin, 003CFF3Ch invoke FormatNumber, skulls3d_win, OFFSET hudNumBuffer invoke DrawString8x8, 136, 62, OFFSET hudNumBuffer, 003CFF3Ch invoke DrawString8x8, 168, 62, OFFSET textSkullsWinMult, 003CFF3Ch - jmp skulls_render_cups + jmp skulls_draw_reveal_cups -skulls_show_loss_text: +skulls_reveal_loss_text: invoke DrawString8x8, 40, 62, OFFSET textSkullsLose, 00FF6464h + jmp skulls_draw_reveal_cups -skulls_render_cups: - ; Draw Cup 1 (X=36, Y=114) +skulls_draw_idle_cups: + ; Draw resting cups at static slots: Slot 0 (X=36), Slot 1 (X=136), Slot 2 (X=236) + invoke DrawGothicCup, 36, 114, skulls3d_cup0, 0, 0 + invoke DrawGothicCup, 136, 114, skulls3d_cup1, 0, 0 + invoke DrawGothicCup, 236, 114, skulls3d_cup2, 0, 0 + jmp skulls_render_controls + +skulls_draw_phase1_cups: + ; Draw with golden ball showing under starting cup mov ebx, 0 mov edx, 0 - cmp skulls3d_state, 1 - jne @F cmp skulls3d_ballPos, 0 jne @F mov ebx, 1 mov edx, 1 @@: - cmp skulls3d_state, 4 + invoke DrawGothicCup, 36, 114, skulls3d_cup0, ebx, edx + + mov ebx, 0 + mov edx, 0 + cmp skulls3d_ballPos, 1 jne @F + mov ebx, 1 + mov edx, 1 +@@: + invoke DrawGothicCup, 136, 114, skulls3d_cup1, ebx, edx + + mov ebx, 0 + mov edx, 0 + cmp skulls3d_ballPos, 2 + jne @F + mov ebx, 1 + mov edx, 1 +@@: + invoke DrawGothicCup, 236, 114, skulls3d_cup2, ebx, edx + jmp skulls_render_controls + +skulls_draw_animated_cups: + ; 60 frames = 3 passes of 20 frames + 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 + +pass_0: + dec eax + mov passTick, eax + mov eax, skulls3d_swapPair0 + mov curPair, eax + jmp setup_pass + +pass_1: + sub eax, 21 + mov passTick, eax + mov eax, skulls3d_swapPair1 + mov curPair, eax + +setup_pass: + ; curPair determines swap pair: + ; 0: swap slot 0 (X=36) and slot 1 (X=136) + ; 1: swap slot 1 (X=136) and slot 2 (X=236) + ; 2: swap slot 0 (X=36) and slot 2 (X=236) + cmp curPair, 1 + je pair_1 + cmp curPair, 2 + je pair_2 + mov swapSlotA, 0 + mov swapSlotB, 1 + mov xA, 36 + mov xB, 136 + jmp calc_coords +pair_1: + mov swapSlotA, 1 + mov swapSlotB, 2 + mov xA, 136 + mov xB, 236 + jmp calc_coords +pair_2: + mov swapSlotA, 0 + mov swapSlotB, 2 + mov xA, 36 + mov xB, 236 + +calc_coords: + ; distX = xB - xA + mov eax, xB + sub eax, xA + mov distX, eax + + ; dxVal = (distX * passTick) / 20 + mov eax, distX + imul eax, passTick + xor edx, edx + mov ecx, 20 + div ecx + mov dxVal, eax + + ; curXA = xA + dxVal + mov eax, xA + add eax, dxVal + mov curXA, eax + + ; curXB = xB - dxVal + mov eax, xB + sub eax, dxVal + mov curXB, eax + + ; archY = (passTick * (20 - passTick)) / 10 + mov eax, passTick + mov ecx, 20 + sub ecx, eax + imul eax, ecx + xor edx, edx + mov ecx, 10 + div ecx + mov archY, eax + + ; Cup A hops UP: 114 - archY + mov eax, 114 + sub eax, archY + mov curYA, eax + + ; Cup B hops DOWN: 114 + archY + mov eax, 114 + add eax, archY + mov curYB, eax + + ; Check if pass ended (frame 19): swap ball position and cups + cmp passTick, 19 + jne draw_active_shuffle + + ; Swap ball position if involved + mov eax, skulls3d_ballPos + cmp eax, swapSlotA + jne check_ball_b + mov edx, swapSlotB + mov skulls3d_ballPos, edx + jmp swap_slot_identities +check_ball_b: + cmp eax, swapSlotB + jne swap_slot_identities + mov edx, swapSlotA + mov skulls3d_ballPos, edx + +swap_slot_identities: + cmp curPair, 1 + je swap_ids_1_2 + cmp curPair, 2 + je swap_ids_0_2 + ; swap 0 and 1 + mov eax, skulls3d_cup0 + xchg eax, skulls3d_cup1 + mov skulls3d_cup0, eax + jmp swap_sound_tick +swap_ids_1_2: + mov eax, skulls3d_cup1 + xchg eax, skulls3d_cup2 + mov skulls3d_cup1, eax + jmp swap_sound_tick +swap_ids_0_2: + mov eax, skulls3d_cup0 + xchg eax, skulls3d_cup2 + mov skulls3d_cup0, eax + +swap_sound_tick: + invoke PlaySoundA, OFFSET soundShuffle, 0, 1 + +draw_active_shuffle: + ; Draw stationary cup + cmp curPair, 1 + je draw_stat_cup0 + cmp curPair, 2 + je draw_stat_cup1 + ; Stationary cup is slot 2 (X=236) + invoke DrawGothicCup, 236, 114, skulls3d_cup2, 0, 0 + jmp draw_moving_cups +draw_stat_cup0: + ; Stationary cup is slot 0 (X=36) + invoke DrawGothicCup, 36, 114, skulls3d_cup0, 0, 0 + jmp draw_moving_cups +draw_stat_cup1: + ; Stationary cup is slot 1 (X=136) + invoke DrawGothicCup, 136, 114, skulls3d_cup1, 0, 0 + +draw_moving_cups: + ; Moving Cup A and Moving Cup B + cmp curPair, 1 + je ids_pair_1 + cmp curPair, 2 + je ids_pair_2 + mov eax, skulls3d_cup0 + mov ebx, skulls3d_cup1 + jmp draw_the_pair +ids_pair_1: + mov eax, skulls3d_cup1 + mov ebx, skulls3d_cup2 + jmp draw_the_pair +ids_pair_2: + mov eax, skulls3d_cup0 + mov ebx, skulls3d_cup2 + +draw_the_pair: + push ebx + invoke DrawGothicCup, curXA, curYA, eax, 0, 0 + pop ebx + invoke DrawGothicCup, curXB, curYB, ebx, 0, 0 + jmp skulls_render_controls + +skulls_draw_reveal_cups: + ; Reveal Phase: lift chosen cup and lift actual ball cup if different + ; Slot 0 + mov ebx, 0 + mov edx, 0 cmp skulls3d_choice, 0 - sete bl + je slot0_is_lifted + cmp skulls3d_ballPos, 0 + jne @F +slot0_is_lifted: + mov ebx, 1 cmp skulls3d_ballPos, 0 sete dl @@: - invoke DrawGothicCup, 36, 114, 0, ebx, edx + invoke DrawGothicCup, 36, 114, skulls3d_cup0, ebx, edx - ; Draw Cup 2 (X=136, Y=114) + ; Slot 1 mov ebx, 0 mov edx, 0 - cmp skulls3d_state, 1 - jne @F - cmp skulls3d_ballPos, 1 - jne @F - mov ebx, 1 - mov edx, 1 -@@: - cmp skulls3d_state, 4 - jne @F cmp skulls3d_choice, 1 - sete bl + je slot1_is_lifted + cmp skulls3d_ballPos, 1 + jne @F +slot1_is_lifted: + mov ebx, 1 cmp skulls3d_ballPos, 1 sete dl @@: - invoke DrawGothicCup, 136, 114, 1, ebx, edx + invoke DrawGothicCup, 136, 114, skulls3d_cup1, ebx, edx - ; Draw Cup 3 (X=236, Y=114) + ; Slot 2 mov ebx, 0 mov edx, 0 - cmp skulls3d_state, 1 - jne @F + cmp skulls3d_choice, 2 + je slot2_is_lifted cmp skulls3d_ballPos, 2 jne @F +slot2_is_lifted: mov ebx, 1 - mov edx, 1 -@@: - cmp skulls3d_state, 4 - jne @F - cmp skulls3d_choice, 2 - sete bl cmp skulls3d_ballPos, 2 sete dl @@: - invoke DrawGothicCup, 236, 114, 2, ebx, edx + invoke DrawGothicCup, 236, 114, skulls3d_cup2, ebx, edx - ; Cup selection indicators +skulls_render_controls: + ; Cup selection slot labels [1], [2], [3] invoke DrawString8x8, 54, 168, OFFSET textSkullsKey1, 00D0D8E0h invoke DrawString8x8, 150, 168, OFFSET textSkullsKey2, 00D0D8E0h invoke DrawString8x8, 250, 168, OFFSET textSkullsKey3, 00D0D8E0h @@ -1539,33 +1760,57 @@ skulls_render_cups: ; Keycaps mov kx, 16 xor eax, eax + cmp skulls3d_state, 3 + je key1_active cmp skulls3d_choice, 0 sete al + jmp draw_key1 +key1_active: + mov eax, 1 +draw_key1: invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKey1, eax add kx, eax xor eax, eax + cmp skulls3d_state, 3 + je key2_active cmp skulls3d_choice, 1 sete al + jmp draw_key2 +key2_active: + mov eax, 1 +draw_key2: invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKey2, eax add kx, eax xor eax, eax + cmp skulls3d_state, 3 + je key3_active cmp skulls3d_choice, 2 sete al + jmp draw_key3 +key3_active: + mov eax, 1 +draw_key3: invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKey3, eax add kx, eax - invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKeySpace, 1 + xor eax, eax + cmp skulls3d_state, 0 + je start_active + cmp skulls3d_state, 4 + je start_active + jmp draw_start_key +start_active: + mov eax, 1 +draw_start_key: + invoke DrawKeycap3D, kx, 192, OFFSET textSkullsKeySpace, eax add kx, eax invoke DrawKeycap3D, kx, 192, OFFSET textKeyEsc, 0 ret RenderSkullsGame ENDP -; ============================================================================== -; IN-ENGINE 2.5D MINI-GAME: SKILL CHECK (DEAD BY DAYLIGHT SURVIVOR CHECK) -; ============================================================================== InitSkillGame PROC mov skill3d_state, 0 mov skill3d_timer, 0