include src_graphics\fortune_ai.inc ; Seated fortune session. All timing, input, dealing and rendering are native MASM. BeginFortuneDialog3D PROC uses ebx esi edi call StopFortuneWorker3D lea esi,screenBuffer lea edi,fortuneBackground3D mov ecx,SCREEN_SIZE rep movsd xor ebx,ebx lea esi,fortuneSessionPacks3D fortune_session_load: invoke LoadAnimPack3D,esi,fortuneSessionPaths3D[ebx*4] add esi,SIZEOF AnimPack3D inc ebx cmp ebx,FORTUNE_SESSION_PACK_COUNT jb fortune_session_load mov engineMode,MODE_FORTUNE mov paused3D,0 mov fortuneSubMode3D,0 mov fortunePhase3D,0 mov fortunePhaseTime3D,0 mov fortuneDealt3D,0 mov fortuneRevealed3D,0 mov fortuneCardsDrawn3D,0 mov fortuneQuestionLength3D,0 mov fortuneQuestion3D,0 mov fortuneWorkerState3D,0 call ResetFortuneFate3D mov eax,animationClock3D mov fortuneSessionClock3D,eax invoke SetFortuneSpeech3D,OFFSET fortuneWelcome3D call ClearKeys3D mov mouseClick3D,0 ret BeginFortuneDialog3D ENDP ; Public exit/shutdown hook releases both inference and visual resources. StopFortuneWorker3D PROC uses ebx esi call CloseFortuneInference3D lea esi,fortuneSessionPacks3D mov ebx,FORTUNE_SESSION_PACK_COUNT fortune_session_free: invoke FreeAnimPack3D,esi add esi,SIZEOF AnimPack3D dec ebx jnz fortune_session_free mov fortuneWorkerState3D,0 call ResetFortuneFate3D ret StopFortuneWorker3D ENDP SetFortuneSpeech3D PROC uses esi edi text:DWORD mov esi,text lea edi,fortuneSpeech3D xor ecx,ecx fortune_speech_copy: mov al,[esi+ecx] mov [edi+ecx],al test al,al jz fortune_speech_copied inc ecx cmp ecx,255 jb fortune_speech_copy mov BYTE PTR [edi+255],0 fortune_speech_copied: mov fortuneSpeechLength3D,ecx mov fortuneSpeechTime3D,0 mov fortuneSpeechVisible3D,0 ret SetFortuneSpeech3D ENDP FortuneCardSpeech3D PROC uses ebx esi edi cardSlot:DWORD mov eax,cardSlot mov ebx,fortuneDrawCards3D[eax*4] mov eax,ebx xor edx,edx mov ecx,22 div ecx mov esi,fortuneCardNames3D[edx*4] lea edi,fortuneSpeech3D fortune_card_name_copy: lodsb test al,al jz fortune_card_position stosb jmp fortune_card_name_copy fortune_card_position: mov al,10 stosb mov esi,OFFSET fortuneUpright3D cmp ebx,22 jb @F mov esi,OFFSET fortuneReversed3D @@: lodsb test al,al jz fortune_card_placeholder stosb jmp @B fortune_card_placeholder: mov al,10 stosb mov esi,OFFSET fortuneAiThinking3D fortune_card_placeholder_copy: lodsb stosb test al,al jnz fortune_card_placeholder_copy sub edi,OFFSET fortuneSpeech3D+1 mov fortuneSpeechLength3D,edi mov fortuneSpeechTime3D,0 mov fortuneSpeechVisible3D,0 ret FortuneCardSpeech3D ENDP FortuneChar3D PROC character:DWORD cmp engineMode,MODE_FORTUNE jne fortune_char_done cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU jne fortune_char_question cmp character,'1' je fortune_char_tarot cmp character,'2' je fortune_char_orb ret fortune_char_tarot: mov fortuneSubMode3D,FORTUNE_SUBMODE_TAROT invoke SetFortuneSpeech3D,OFFSET fortuneReady3D ret fortune_char_orb: mov fortuneSubMode3D,FORTUNE_SUBMODE_ORB call ResetFortuneFate3D mov fortunePhase3D,0 mov fortunePhaseTime3D,0 invoke SetFortuneSpeech3D,OFFSET fortuneFateReady3D ret fortune_char_question: cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne @F invoke FortuneFateChar3D,character ret @@: cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_char_done cmp fortunePhase3D,0 jne fortune_char_done mov eax,character cmp eax,8 je fortune_char_backspace cmp eax,32 jb fortune_char_done cmp eax,255 ja fortune_char_done cmp eax,127 je fortune_char_done mov ecx,fortuneQuestionLength3D cmp ecx,240 jae fortune_char_done mov fortuneQuestion3D[ecx],al inc ecx mov fortuneQuestionLength3D,ecx mov fortuneQuestion3D[ecx],0 ret fortune_char_backspace: mov ecx,fortuneQuestionLength3D test ecx,ecx jz fortune_char_done dec ecx mov fortuneQuestionLength3D,ecx mov fortuneQuestion3D[ecx],0 fortune_char_done: ret FortuneChar3D ENDP SubmitFortuneQuestion3D PROC uses ebx esi cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_submit_done cmp fortunePhase3D,0 jne fortune_submit_done cmp fortuneQuestionLength3D,0 je fortune_submit_done xor esi,esi fortune_draw_loop: invoke RandomRange3D,22 mov ebx,eax xor ecx,ecx fortune_check_unique: cmp ecx,esi jae fortune_apply_orientation mov eax,fortuneDrawCards3D[ecx*4] xor edx,edx push ecx mov ecx,22 div ecx pop ecx cmp ebx,edx je fortune_draw_loop inc ecx jmp fortune_check_unique fortune_apply_orientation: invoke RandomRange3D,2 imul eax,22 add eax,ebx mov fortuneDrawCards3D[esi*4],eax inc esi cmp esi,3 jb fortune_draw_loop mov fortunePhase3D,1 mov fortunePhaseTime3D,0 mov fortuneDealt3D,0 mov fortuneRevealed3D,0 mov fortuneCardsDrawn3D,1 mov eax,animationClock3D mov fortuneSessionClock3D,eax invoke SetFortuneSpeech3D,OFFSET fortuneShuffle3D call StartFortuneWorker3D fortune_submit_done: ret SubmitFortuneQuestion3D ENDP ; Elapsed clock, not render count: repeated renders cannot accelerate a session. UpdateFortuneSession3D PROC uses ebx call PollFortuneWorker3D mov eax,animationClock3D mov edx,eax sub eax,fortuneSessionClock3D mov fortuneSessionClock3D,edx cmp eax,100 jbe @F mov eax,100 @@: add fortuneSpeechTime3D,eax add fortunePhaseTime3D,eax cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_update_orb mov ebx,fortunePhase3D test ebx,ebx jz fortune_update_done cmp ebx,12 jae fortune_update_done ; Explanation and summary phases wait for AI, then for explicit paging. cmp ebx,6 je fortune_update_reading cmp ebx,8 je fortune_update_reading cmp ebx,10 je fortune_update_reading cmp ebx,11 jne fortune_update_timed fortune_update_reading: cmp fortuneWorkerState3D,4 je fortune_update_done cmp fortuneWorkerState3D,5 jne fortune_update_done cmp fortuneReadingShown3D,2 je fortune_update_timed cmp fortuneReadingShown3D,0 jne fortune_update_page_wait mov eax,ebx sub eax,6 shr eax,1 cmp ebx,11 jne @F mov eax,3 @@: invoke ShowFortuneReading3D,eax jmp fortune_update_done fortune_update_page_wait: cmp fortunePhaseTime3D,7FFFFFFFh jb fortune_update_done fortune_update_timed: mov eax,fortuneDurations3D[ebx*4] cmp fortunePhaseTime3D,eax jb fortune_update_done inc ebx mov fortunePhase3D,ebx mov fortunePhaseTime3D,0 cmp ebx,2 jb fortune_update_done cmp ebx,5 ja fortune_update_reveal mov eax,ebx sub eax,2 mov fortuneDealt3D,eax invoke SetFortuneSpeech3D,OFFSET fortuneDeal3D jmp fortune_update_done fortune_update_reveal: cmp ebx,6 je fortune_explain_one cmp ebx,8 je fortune_explain_two cmp ebx,10 je fortune_explain_three cmp ebx,11 jne fortune_update_done mov fortuneReadingShown3D,0 mov fortuneReadingOffset3D,0 call ShowFortuneStatus3D jmp fortune_update_done fortune_explain_one: mov fortuneRevealed3D,1 invoke FortuneCardSpeech3D,0 cmp fortuneWorkerState3D,4 jne @F call ShowFortuneStatus3D @@: mov fortuneReadingShown3D,0 mov fortuneReadingOffset3D,0 jmp fortune_update_done fortune_explain_two: mov fortuneRevealed3D,2 invoke FortuneCardSpeech3D,1 cmp fortuneWorkerState3D,4 jne @F call ShowFortuneStatus3D @@: mov fortuneReadingShown3D,0 mov fortuneReadingOffset3D,0 jmp fortune_update_done fortune_explain_three: mov fortuneRevealed3D,3 invoke FortuneCardSpeech3D,2 cmp fortuneWorkerState3D,4 jne @F call ShowFortuneStatus3D @@: mov fortuneReadingShown3D,0 mov fortuneReadingOffset3D,0 jmp fortune_update_done fortune_update_orb: cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne fortune_update_done cmp fortunePhase3D,1 jne fortune_orb_swirl cmp fortunePhaseTime3D,1400 jb fortune_update_done mov fortunePhase3D,2 mov fortunePhaseTime3D,0 invoke SetFortuneSpeech3D,OFFSET fortuneFateSwirl3D jmp fortune_update_done fortune_orb_swirl: cmp fortunePhase3D,2 jne fortune_update_done cmp fortunePhaseTime3D,5000 jb fortune_update_done mov fortunePhase3D,3 mov fortunePhaseTime3D,0 invoke SetFortuneSpeech3D,OFFSET fortuneFateReveal3D fortune_update_done: ret UpdateFortuneSession3D ENDP HandleFortuneDialog3D PROC call UpdateFortuneSession3D invoke Pressed3D,1Bh test eax,eax jnz fortune_handle_exit invoke Pressed3D,0Dh test eax,eax jnz fortune_handle_action cmp mouseClick3D,0 je fortune_handle_done mov mouseClick3D,0 cmp mouseY3D,227 jb fortune_handle_menu_click cmp mouseY3D,239 ja fortune_handle_done cmp mouseX3D,240 jae fortune_handle_exit cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU je fortune_handle_done cmp mouseX3D,172 jae fortune_handle_menu cmp mouseX3D,88 jae fortune_handle_reset jmp fortune_handle_action fortune_handle_menu_click: cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne fortune_handle_selection_click cmp fortunePhase3D,0 jne fortune_handle_done ; Form uses physical pixels; mouse input here uses half-resolution units. mov eax,mouseY3D sub eax,162 jb fortune_handle_done xor edx,edx mov ecx,21 div ecx cmp eax,2 ja fortune_handle_done cmp edx,11 ja fortune_handle_done shl eax,1 cmp mouseX3D,156 jb fortune_handle_form_left cmp mouseX3D,168 jb fortune_handle_done cmp mouseX3D,312 ja fortune_handle_done inc eax jmp fortune_handle_form_focus fortune_handle_form_left: cmp mouseX3D,8 jb fortune_handle_done fortune_handle_form_focus: mov fortuneFateField3D,eax ret fortune_handle_selection_click: cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU jne fortune_handle_done cmp mouseY3D,196 jb fortune_handle_done cmp mouseY3D,214 ja fortune_handle_done cmp mouseX3D,160 jae fortune_handle_orb fortune_handle_tarot: invoke FortuneChar3D,'1' ret fortune_handle_orb: invoke FortuneChar3D,'2' ret fortune_handle_action: cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU je fortune_handle_tarot cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne @F call SubmitFortuneFate3D ret @@: cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_handle_done cmp fortunePhase3D,0 je fortune_handle_submit ; Continue only after this speech is fully visible. Never skip dealing/flips. mov eax,fortuneSpeechLength3D imul eax,28 cmp fortuneSpeechTime3D,eax jb fortune_handle_done mov eax,fortunePhase3D cmp eax,6 je fortune_handle_advance cmp eax,8 je fortune_handle_advance cmp eax,10 je fortune_handle_advance cmp eax,11 jne fortune_handle_done fortune_handle_advance: cmp fortuneWorkerState3D,5 jne fortune_handle_done cmp fortuneReadingShown3D,1 jne fortune_handle_done call AdvanceFortuneReading3D ret fortune_handle_submit: call SubmitFortuneQuestion3D ret fortune_handle_menu: call CloseFortuneInference3D mov fortuneSubMode3D,FORTUNE_SUBMODE_MENU mov fortunePhase3D,0 mov fortuneDealt3D,0 mov fortuneRevealed3D,0 invoke SetFortuneSpeech3D,OFFSET fortuneWelcome3D ret fortune_handle_reset: cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne @F cmp fortunePhase3D,3 jne fortune_handle_done cmp fortunePhaseTime3D,1200 jb fortune_handle_done mov fortunePhase3D,0 mov fortunePhaseTime3D,0 mov fortuneFateResult3D,-1 invoke SetFortuneSpeech3D,OFFSET fortuneFateReady3D ret @@: cmp fortuneWorkerState3D,4 je fortune_handle_retry cmp fortunePhase3D,12 jne fortune_handle_done fortune_handle_retry: call CloseFortuneInference3D mov fortunePhase3D,0 mov fortuneDealt3D,0 mov fortuneRevealed3D,0 mov fortuneQuestionLength3D,0 mov fortuneQuestion3D,0 invoke SetFortuneSpeech3D,OFFSET fortuneReady3D ret fortune_handle_exit: call StopFortuneWorker3D mov engineMode,MODE_WALK call ClearKeys3D fortune_handle_done: ret HandleFortuneDialog3D ENDP FortuneWrap3D PROC uses ebx esi edi source:DWORD,top:DWORD,lines:DWORD,skip:DWORD,ink:DWORD local count:DWORD,lastSpace:DWORD,lineNumber:DWORD mov esi,source mov lineNumber,0 fortune_wrap_line: cmp BYTE PTR [esi],0 je fortune_wrap_end xor ebx,ebx mov lastSpace,0 fortune_wrap_measure: mov al,[esi+ebx] test al,al jz fortune_wrap_measured cmp al,10 je fortune_wrap_measured cmp al,32 jne @F mov lastSpace,ebx @@: inc ebx cmp ebx,72 jb fortune_wrap_measure cmp BYTE PTR [esi+ebx],0 je fortune_wrap_measured cmp lastSpace,0 je fortune_wrap_measured mov ebx,lastSpace fortune_wrap_measured: mov count,ebx mov eax,lineNumber cmp eax,skip jb fortune_wrap_advance sub eax,skip cmp eax,lines jae fortune_wrap_more imul eax,11 add eax,top push eax lea edi,fortuneLine3D mov ecx,ebx push esi rep movsb mov BYTE PTR [edi],0 pop esi pop eax mov fortuneCaretY3D,eax mov edx,count imul edx,8 add edx,24 mov fortuneCaretX3D,edx invoke DrawBuckshotText3D,24,eax,OFFSET fortuneLine3D,ink fortune_wrap_advance: add esi,count cmp BYTE PTR [esi],10 je fortune_wrap_skip_separator cmp BYTE PTR [esi],32 jne fortune_wrap_next fortune_wrap_skip_separator: inc esi fortune_wrap_next: inc lineNumber jmp fortune_wrap_line fortune_wrap_more: mov eax,1 ret fortune_wrap_end: xor eax,eax ret FortuneWrap3D ENDP ; The bubble grows with visible glyphs, rather than allocating a full-screen panel. DrawFortuneSpeech3D PROC uses ebx esi edi local column:DWORD,row:DWORD,maxColumn:DWORD,visible:DWORD,glyphX:DWORD,glyphY:DWORD mov eax,fortuneSpeechTime3D xor edx,edx mov ecx,28 div ecx cmp eax,fortuneSpeechLength3D jbe @F mov eax,fortuneSpeechLength3D @@: mov visible,eax mov fortuneSpeechVisible3D,eax test eax,eax jz fortune_bubble_done mov column,0 mov row,0 mov maxColumn,0 xor esi,esi fortune_bubble_measure: cmp esi,visible jae fortune_bubble_box cmp fortuneSpeech3D[esi],10 je fortune_bubble_newline inc column mov eax,column cmp eax,maxColumn jbe @F mov maxColumn,eax @@: cmp column,23 jb fortune_bubble_measure_next fortune_bubble_newline: mov column,0 inc row fortune_bubble_measure_next: inc esi jmp fortune_bubble_measure fortune_bubble_box: mov eax,maxColumn imul eax,8 add eax,20 mov fortuneSpeechWidth3D,eax shr eax,1 mov ebx,eax mov eax,row inc eax imul eax,12 add eax,20 mov fortuneSpeechHeight3D,eax shr eax,1 invoke DrawBoxBorder,208,30,ebx,eax,000A1829h,006E9DB8h,1 mov column,0 mov row,0 xor esi,esi fortune_bubble_glyph: cmp esi,visible jae fortune_bubble_done mov al,fortuneSpeech3D[esi] cmp al,10 je fortune_bubble_glyph_newline mov fortuneGlyph3D,al mov eax,column imul eax,8 add eax,426 mov glyphX,eax mov ebx,row imul ebx,12 add ebx,70 mov eax,esi inc eax imul eax,28 mov edx,fortuneSpeechTime3D sub edx,eax cmp edx,84 jae @F mov eax,84 sub eax,edx shr eax,5 sub ebx,eax @@: mov glyphY,ebx invoke DrawBuckshotText3D,glyphX,ebx,OFFSET fortuneGlyph3D,00D2E8F2h inc column cmp column,23 jb fortune_bubble_glyph_next fortune_bubble_glyph_newline: mov column,0 inc row fortune_bubble_glyph_next: inc esi jmp fortune_bubble_glyph fortune_bubble_done: ret DrawFortuneSpeech3D ENDP DrawFortuneSessionCard3D PROC uses ebx esi edi slot:DWORD local px:DWORD,py:DWORD,cw:DWORD,cardHeight:DWORD,face:DWORD,amount:DWORD mov esi,slot mov eax,esi imul eax,100 add eax,198 mov px,eax mov py,302 mov cw,72 mov cardHeight,96 mov face,44 cmp esi,fortuneRevealed3D jae @F mov eax,fortuneDrawCards3D[esi*4] mov face,eax @@: cmp esi,fortuneDealt3D jb fortune_card_flip_check mov eax,fortunePhase3D sub eax,2 cmp eax,esi jne fortune_card_done ; Release at 40%, then carry the card from the hand to its table slot. mov ebx,fortunePhaseTime3D sub ebx,400 js fortune_card_done cmp ebx,600 jbe @F mov ebx,600 @@: mov eax,px sub eax,220 imul eax,ebx cdq mov ecx,600 idiv ecx add eax,220 mov px,eax mov eax,ebx imul eax,40 xor edx,edx div ecx add eax,262 mov py,eax jmp fortune_card_draw fortune_card_flip_check: mov eax,fortunePhase3D sub eax,5 js fortune_card_draw test eax,1 jnz fortune_card_draw shr eax,1 cmp eax,esi jne fortune_card_draw cmp fortunePhase3D,9 ja fortune_card_draw mov ebx,fortunePhaseTime3D cmp ebx,400 jb fortune_card_flip_back mov eax,fortuneDrawCards3D[esi*4] mov face,eax sub ebx,400 jmp fortune_card_flip_width fortune_card_flip_back: mov eax,400 sub eax,ebx mov ebx,eax fortune_card_flip_width: imul ebx,72 mov eax,ebx xor edx,edx mov ecx,400 div ecx cmp eax,2 jae @F mov eax,2 @@: mov cw,eax mov ebx,72 sub ebx,eax shr ebx,1 add px,ebx fortune_card_draw: invoke DrawAnimSprite3D,OFFSET fortuneSessionCards3D,face,px,py,cw,cardHeight fortune_card_done: ret DrawFortuneSessionCard3D ENDP RenderFortuneDialog3D PROC uses ebx esi edi lea esi,fortuneBackground3D lea edi,screenBuffer mov ecx,SCREEN_SIZE rep movsd invoke DrawAnimSprite3D,OFFSET fortuneSessionRoom3D,0,0,0,640,480 cmp fortuneSessionRoom3D.pixels,0 jne @F invoke DrawBoxBorder,4,4,312,36,000A1829h,006E9DB8h,1 invoke DrawBuckshotText3D,24,24,OFFSET fortuneMissing3D,00FFFFFFh @@: mov ebx,OFFSET fortuneSessionShuffle3D xor esi,esi cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB je fortune_render_orb_hands mov eax,fortunePhase3D cmp eax,1 jne fortune_render_hand_deal mov eax,fortunePhaseTime3D xor edx,edx mov ecx,25 div ecx and eax,63 mov esi,eax jmp fortune_render_hands fortune_render_hand_deal: cmp eax,2 jb fortune_render_hands cmp eax,4 ja fortune_render_hand_gesture mov ebx,OFFSET fortuneSessionDeal3D mov eax,fortunePhaseTime3D xor edx,edx mov ecx,32 div ecx cmp eax,31 jbe @F mov eax,31 @@: mov esi,eax jmp fortune_render_hands fortune_render_hand_gesture: mov ebx,OFFSET fortuneSessionGesture3D mov eax,fortunePhaseTime3D shr eax,5 and eax,31 mov esi,eax jmp fortune_render_hands fortune_render_orb_hands: cmp fortunePhase3D,0 je fortune_render_hands mov ebx,OFFSET fortuneSessionOrbHands3D mov eax,fortunePhaseTime3D shr eax,5 and eax,31 mov esi,eax fortune_render_hands: invoke DrawAnimSprite3D,ebx,esi,134,32,280,250 cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU je fortune_render_menu cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB je fortune_render_orb cmp fortunePhase3D,0 je fortune_render_input invoke DrawAnimSprite3D,OFFSET fortuneSessionCards3D,44,142,300,30,40 xor esi,esi fortune_render_cards: invoke DrawFortuneSessionCard3D,esi cmp esi,fortuneRevealed3D jae @F mov eax,esi imul eax,100 add eax,198 invoke DrawBuckshotText3D,eax,288,fortuneRoleTexts3D[esi*4],00B9D4E7h @@: inc esi cmp esi,3 jb fortune_render_cards fortune_render_input: invoke DrawBoxBorder,8,199,304,24,00081322h,0046637Fh,1 mov fortuneCaretX3D,24 mov fortuneCaretY3D,402 invoke FortuneWrap3D,OFFSET fortuneQuestion3D,402,4,0,00E8EFF4h cmp fortunePhase3D,0 jne fortune_render_footer test animationClock3D,200h jnz fortune_render_footer invoke DrawBuckshotText3D,fortuneCaretX3D,fortuneCaretY3D,OFFSET fortuneCaret3D,00FFFFFFh jmp fortune_render_footer fortune_render_menu: invoke DrawBoxBorder,8,196,148,18,000D2136h,005D86A0h,1 invoke DrawBuckshotText3D,100,404,OFFSET fortuneTarot3D,00D2E8F2h invoke DrawBoxBorder,164,196,148,18,000D2136h,005D86A0h,1 invoke DrawBuckshotText3D,364,404,OFFSET fortuneFate3D,00D2E8F2h jmp fortune_render_footer fortune_render_orb: cmp fortunePhase3D,0 jne fortune_render_orb_active call DrawFortuneFateForm3D jmp fortune_render_footer fortune_render_orb_active: call ResolveFortuneOrb3D invoke DrawAnimSprite3D,OFFSET fortuneSessionMist3D,fortuneOrbFrame3D,fortuneOrbX3D,fortuneOrbY3D,128,128 cmp fortunePhase3D,3 jne @F call DrawFortuneFateResult3D @@: call DrawFortuneFateIdentity3D fortune_render_footer: call DrawFortuneSpeech3D cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU je fortune_render_exit_button cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_render_fate_button invoke DrawBoxBorder,8,227,76,12,000B1E30h,00445F75h,1 mov esi,OFFSET fortuneAsk3D cmp fortunePhase3D,0 je @F mov esi,OFFSET fortuneContinue3D @@: invoke DrawBuckshotText3D,24,458,esi,00CCE4F4h jmp fortune_render_again_button fortune_render_fate_button: cmp fortunePhase3D,0 jne fortune_render_again_button invoke DrawBoxBorder,8,227,76,12,000B1E30h,00445F75h,1 invoke DrawBuckshotText3D,24,458,OFFSET fortuneFateStart3D,00CCE4F4h fortune_render_again_button: invoke DrawBoxBorder,88,227,76,12,000B1E30h,00445F75h,1 mov esi,OFFSET fortuneAgain3D cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne @F mov esi,OFFSET fortuneFateEdit3D @@: invoke DrawBuckshotText3D,192,458,esi,0091A8BCh invoke DrawBoxBorder,172,227,64,12,000B1E30h,00445F75h,1 invoke DrawBuckshotText3D,368,458,OFFSET fortuneMenu3D,00CCE4F4h fortune_render_exit_button: invoke DrawBoxBorder,240,227,72,12,000B1E30h,00445F75h,1 invoke DrawBuckshotText3D,516,458,OFFSET fortuneExit3D,00CCE4F4h ret RenderFortuneDialog3D ENDP ResetFortuneFate3D PROC uses edi lea edi,fortuneFateLengths3D xor eax,eax mov ecx,6 rep stosd lea edi,fortuneFateFields3D mov ecx,96 rep stosd mov fortuneFateField3D,0 mov fortuneFateResult3D,-1 mov fortuneFateFields3D[256],'9' mov fortuneFateFields3D[320],'1' mov fortuneFateFields3D[321],'0' mov fortuneFateLengths3D[16],1 mov fortuneFateLengths3D[20],2 ret ResetFortuneFate3D ENDP FortuneFateChar3D PROC uses ebx esi edi character:DWORD cmp fortunePhase3D,0 jne fate_char_done mov ebx,fortuneFateField3D cmp ebx,6 jae fate_char_done mov eax,character cmp eax,9 je fate_char_next mov esi,ebx shl esi,6 lea esi,fortuneFateFields3D[esi] mov ecx,fortuneFateLengths3D[ebx*4] cmp eax,8 je fate_char_backspace cmp eax,32 jb fate_char_done cmp eax,255 ja fate_char_done cmp eax,127 je fate_char_done cmp ecx,fortuneFateLimits3D[ebx*4] jae fate_char_done cmp ebx,2 je fate_char_date cmp ebx,4 jae fate_char_digit cmp eax,32 jne fate_char_store test ecx,ecx jz fate_char_done cmp BYTE PTR [esi+ecx-1],32 je fate_char_done jmp fate_char_store fate_char_date: cmp eax,'/' jne @F mov eax,'.' @@: cmp eax,'.' jne fate_char_date_digit cmp ecx,2 je fate_char_store cmp ecx,5 je fate_char_store jmp fate_char_done fate_char_date_digit: cmp eax,'0' jb fate_char_done cmp eax,'9' ja fate_char_done cmp ecx,2 je fate_char_date_dot cmp ecx,5 jne fate_char_store fate_char_date_dot: mov BYTE PTR [esi+ecx],'.' inc ecx jmp fate_char_store fate_char_digit: cmp eax,'0' jb fate_char_done cmp eax,'9' ja fate_char_done fate_char_store: mov [esi+ecx],al inc ecx mov fortuneFateLengths3D[ebx*4],ecx mov BYTE PTR [esi+ecx],0 ret fate_char_backspace: test ecx,ecx jz fate_char_done dec ecx mov fortuneFateLengths3D[ebx*4],ecx mov BYTE PTR [esi+ecx],0 ret fate_char_next: inc ebx cmp ebx,6 jb @F xor ebx,ebx @@: mov fortuneFateField3D,ebx fate_char_done: ret FortuneFateChar3D ENDP ; Validation is calendar-based, including leap years, not a birth-date heuristic. ValidateFortuneBirth3D PROC uses ebx esi edi local dayValue:DWORD,monthValue:DWORD cmp fortuneFateLengths3D[8],10 jne fate_birth_bad lea esi,fortuneFateFields3D[128] cmp BYTE PTR [esi+2],'.' jne fate_birth_bad cmp BYTE PTR [esi+5],'.' jne fate_birth_bad xor ebx,ebx xor edi,edi fate_birth_parse: movzx eax,BYTE PTR [esi+edi] cmp edi,2 je fate_birth_day cmp edi,5 je fate_birth_month sub eax,'0' cmp eax,9 ja fate_birth_bad imul ebx,10 add ebx,eax jmp fate_birth_next fate_birth_day: mov dayValue,ebx xor ebx,ebx jmp fate_birth_next fate_birth_month: mov monthValue,ebx xor ebx,ebx fate_birth_next: inc edi cmp edi,10 jb fate_birth_parse cmp ebx,1900 jb fate_birth_bad cmp ebx,2099 ja fate_birth_bad mov eax,monthValue dec eax cmp eax,11 ja fate_birth_bad movzx ecx,fortuneFateMonthDays3D[eax] cmp eax,1 jne fate_birth_check_day test ebx,3 jnz fate_birth_check_day cmp ebx,1900 je fate_birth_check_day inc ecx fate_birth_check_day: cmp dayValue,1 jb fate_birth_bad cmp dayValue,ecx ja fate_birth_bad mov eax,1 ret fate_birth_bad: xor eax,eax ret ValidateFortuneBirth3D ENDP ParseFortuneFateNumber3D PROC uses ebx esi slot:DWORD mov eax,slot mov ecx,fortuneFateLengths3D[eax*4] test ecx,ecx jz fate_number_bad cmp ecx,3 ja fate_number_bad shl eax,6 lea esi,fortuneFateFields3D[eax] xor eax,eax fate_number_parse: movzx ebx,BYTE PTR [esi] sub ebx,'0' cmp ebx,9 ja fate_number_bad imul eax,10 add eax,ebx inc esi loop fate_number_parse cmp eax,100 ja fate_number_bad ret fate_number_bad: mov eax,-1 ret ParseFortuneFateNumber3D ENDP SubmitFortuneFate3D PROC uses ebx cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne fate_submit_done cmp fortunePhase3D,0 jne fate_submit_done xor ebx,ebx fate_submit_required: cmp fortuneFateLengths3D[ebx*4],0 je fate_submit_missing inc ebx cmp ebx,6 jb fate_submit_required call ValidateFortuneBirth3D test eax,eax jz fate_submit_date invoke ParseFortuneFateNumber3D,4 cmp eax,-1 je fate_submit_low mov fortuneFateLowValue3D,eax invoke ParseFortuneFateNumber3D,5 cmp eax,-1 je fate_submit_high cmp eax,fortuneFateLowValue3D jb fate_submit_high mov fortuneFateHighValue3D,eax sub eax,fortuneFateLowValue3D inc eax ; Exactly one draw, independent of identity. Renders and double clicks never reroll. invoke RandomRange3D,eax add eax,fortuneFateLowValue3D mov fortuneFateResult3D,eax mov fortunePhase3D,1 mov fortunePhaseTime3D,0 mov eax,animationClock3D mov fortuneSessionClock3D,eax invoke SetFortuneSpeech3D,OFFSET fortuneFateRising3D fate_submit_done: ret fate_submit_missing: mov fortuneFateField3D,ebx invoke SetFortuneSpeech3D,OFFSET fortuneFateRequired3D ret fate_submit_date: mov fortuneFateField3D,2 invoke SetFortuneSpeech3D,OFFSET fortuneFateDateError3D ret fate_submit_low: mov fortuneFateField3D,4 jmp fate_submit_range fate_submit_high: mov fortuneFateField3D,5 fate_submit_range: invoke SetFortuneSpeech3D,OFFSET fortuneFateRangeError3D ret SubmitFortuneFate3D ENDP DrawFortuneFateForm3D PROC uses ebx esi edi local px:DWORD,py:DWORD,edgeInk:DWORD invoke DrawBoxBorder,8,149,304,74,00081322h,0046637Fh,1 invoke DrawBuckshotText3D,24,304,OFFSET fortuneFatePlay3D,0086ABBCh xor esi,esi fate_form_field: mov eax,fortuneFateXs3D[esi*4] mov px,eax shr eax,1 mov ebx,fortuneFateYs3D[esi*4] mov py,ebx shr ebx,1 mov edgeInk,00314D65h cmp esi,fortuneFateField3D jne @F mov edgeInk,0088DDEAh @@: invoke DrawBoxBorder,eax,ebx,144,11,000B1D30h,edgeInk,1 mov eax,px add eax,8 mov ebx,py sub ebx,9 invoke DrawBuckshotText3D,eax,ebx,fortuneFateLabels3D[esi*4],00A9C8D8h mov edi,esi shl edi,6 add edi,OFFSET fortuneFateFields3D mov eax,px add eax,8 mov ebx,py add ebx,7 cmp esi,2 jne fate_form_value cmp fortuneFateLengths3D[8],0 jne fate_form_value invoke DrawBuckshotText3D,eax,ebx,OFFSET fortuneFateHint3D,005A7A95h jmp fate_form_caret fate_form_value: invoke DrawBuckshotText3D,eax,ebx,edi,00E8EFF4h fate_form_caret: cmp esi,fortuneFateField3D jne fate_form_next test animationClock3D,200h jnz fate_form_next mov eax,fortuneFateLengths3D[esi*4] shl eax,3 add eax,px add eax,8 mov ebx,py add ebx,7 invoke DrawBuckshotText3D,eax,ebx,OFFSET fortuneCaret3D,00B8F3FFh fate_form_next: inc esi cmp esi,6 jb fate_form_field ret DrawFortuneFateForm3D ENDP DrawFortuneFateIdentity3D PROC uses ebx esi edi invoke DrawBoxBorder,8,190,304,33,00081322h,0046637Fh,1 xor esi,esi fate_identity_field: mov eax,fortuneFateXs3D[esi*4] add eax,8 mov ebx,esi shr ebx,1 imul ebx,20 add ebx,388 mov edi,esi shl edi,6 add edi,OFFSET fortuneFateFields3D cmp esi,4 jb @F invoke DrawBuckshotText3D,eax,ebx,fortuneFateLabels3D[esi*4],0086ABBCh mov eax,fortuneFateXs3D[esi*4] add eax,40 @@: invoke DrawBuckshotText3D,eax,ebx,edi,00E8EFF4h inc esi cmp esi,6 jb fate_identity_field ret DrawFortuneFateIdentity3D ENDP ; One resolver supplies the frame, sprite placement and its measured glass centre. ResolveFortuneOrb3D PROC uses ebx mov eax,fortunePhaseTime3D shr eax,6 and eax,15 mov fortuneOrbFrame3D,eax mov ebx,eax mov fortuneOrbX3D,214 mov fortuneOrbY3D,195 cmp fortunePhase3D,1 jne fate_orb_anchor mov eax,fortunePhaseTime3D cmp eax,1400 jbe @F mov eax,1400 @@: imul eax,135 xor edx,edx mov ecx,1400 div ecx mov ecx,330 sub ecx,eax mov fortuneOrbY3D,ecx fate_orb_anchor: mov eax,fortuneOrbAnchorX3D[ebx*4] add eax,128 shr eax,8 add eax,fortuneOrbX3D mov fortuneOrbCenterX3D,eax mov eax,fortuneOrbAnchorY3D[ebx*4] add eax,128 shr eax,8 add eax,fortuneOrbY3D mov fortuneOrbCenterY3D,eax ret ResolveFortuneOrb3D ENDP DrawFortuneFateResult3D PROC uses ebx esi edi local numberX:DWORD,numberY:DWORD cmp fortuneFateResult3D,100 ja fate_result_done invoke FormatNumber,fortuneFateResult3D,OFFSET fortuneFateNumber3D lea edi,fortuneFateNumber3D xor ebx,ebx fate_result_length: cmp BYTE PTR [edi+ebx],0 je fate_result_place inc ebx jmp fate_result_length fate_result_place: dec ebx imul ebx,20 add ebx,32 shr ebx,1 neg ebx add ebx,fortuneOrbCenterX3D mov numberX,ebx mov eax,fortuneOrbCenterY3D sub eax,24 mov numberY,eax ; Fade into the mist at a constant size; preserve opacity for later draws. push animOpacity3D mov eax,fortunePhaseTime3D cmp eax,900 jbe @F mov eax,900 @@: imul eax,255 xor edx,edx mov ecx,900 div ecx mov animOpacity3D,eax fate_result_digit: movzx esi,BYTE PTR [edi] test esi,esi jz fate_result_restore sub esi,'0' invoke DrawAnimSprite3D,OFFSET fortuneSessionNumerals3D,esi,numberX,numberY,32,48 add numberX,20 inc edi jmp fate_result_digit fate_result_restore: pop animOpacity3D fate_result_done: ret DrawFortuneFateResult3D ENDP