KAZIK/src_graphics/fortune_room.inc
Artvasil09 ce06659ab2 feat: implement offline neural fortune teller and native tarot divination
- Offline neural worker based on llama.cpp (fortune-engine.exe) with local Qwen3-4B model support

- Native assembly Tarot 3-card spread divination and teacher grade divination mode

- Text input dialog in MODE_FORTUNE communicating over anonymous pipes

- Clean process lifecycle management, WM_CHAR dispatch and fallback handling

- Test suites and packaging tools for offline release
2026-10-21 13:53:06 +03:00

539 lines
15 KiB
PHP

LoadFortuneArt3D PROC
invoke LoadAnimPack3D,OFFSET fortuneArt3D,OFFSET fortuneArtPath3D
invoke ValidateCityPack3D,OFFSET fortuneArt3D,256,256,24
invoke LoadAnimPack3D,OFFSET fortuneMaterialArt3D,OFFSET fortuneMaterialPath3D
invoke ValidateCityPack3D,OFFSET fortuneMaterialArt3D,256,256,4
ret
LoadFortuneArt3D ENDP
FreeFortuneArt3D PROC
invoke FreeAnimPack3D,OFFSET fortuneArt3D
invoke FreeAnimPack3D,OFFSET fortuneMaterialArt3D
ret
FreeFortuneArt3D ENDP
ResolveFortuneMaterial3D PROC material:DWORD
mov eax,material
sub eax,132
cmp eax,3
ja fortune_material_sprite
cmp fortuneMaterialArt3D.pixels,0
je fortune_material_missing
shl eax,18
add eax,fortuneMaterialArt3D.pixels
mov ecx,256
mov edx,256
ret
fortune_material_sprite:
sub eax,4
cmp eax,23
ja fortune_material_missing
cmp fortuneArt3D.pixels,0
je fortune_material_missing
shl eax,18
add eax,fortuneArt3D.pixels
mov ecx,256
mov edx,256
ret
fortune_material_missing:
xor eax,eax
ret
ResolveFortuneMaterial3D ENDP
UpdateFortunePassage3D PROC
mov eax,animationClock3D
cmp eax,fortunePassageClock3D
je fortune_update_done
mov fortunePassageClock3D,eax
mov eax,deltaTime3D
cmp eax,100
jbe @F
mov eax,100
@@:
cmp fortunePassageTarget3D,0
je fortune_update_close
add eax,fortunePassageTime3D
cmp eax,1400
jbe fortune_update_store
mov eax,1400
jmp fortune_update_store
fortune_update_close:
; A pending close waits for the visitor to leave the entire secret branch.
cmp hallLevel3D,3
je fortune_update_done
cmp engineMode,MODE_STAIRS
jne fortune_update_hall_guard
cmp stairRoute3D,3
jne fortune_update_subtract
jmp fortune_update_done
fortune_update_hall_guard:
cmp hallLevel3D,0
jne fortune_update_subtract
cmp playerX,2*FP_1
ja fortune_update_subtract
cmp playerY,FP_1*85/10
jb fortune_update_subtract
cmp playerY,FP_1*115/10
jbe fortune_update_done
fortune_update_subtract:
mov edx,fortunePassageTime3D
sub edx,eax
jae @F
xor edx,edx
@@:
mov eax,edx
fortune_update_store:
mov fortunePassageTime3D,eax
fortune_update_done:
ret
UpdateFortunePassage3D ENDP
FortuneEntranceCanWalk3D PROC px:DWORD,py:DWORD
cmp px,FP_1*17/10
ja fortune_entrance_clear
cmp py,9*FP_1
jb fortune_entrance_clear
cmp py,11*FP_1
ja fortune_entrance_clear
cmp fortunePassageTime3D,1400
jb fortune_entrance_block
cmp py,FP_1*92/10
jb fortune_entrance_block
cmp py,FP_1*108/10
ja fortune_entrance_block
fortune_entrance_clear:
mov eax,1
ret
fortune_entrance_block:
xor eax,eax
ret
FortuneEntranceCanWalk3D ENDP
TryFortuneInteraction3D PROC
cmp interactTargetId,INTERACT_SECRET_LAMP
jne fortune_try_seer
cmp hallLevel3D,0
jne fortune_interaction_none
xor fortunePassageTarget3D,1
call ClearKeys3D
mov eax,1
ret
fortune_try_seer:
cmp interactTargetId,INTERACT_FORTUNE
jne fortune_interaction_none
cmp hallLevel3D,3
jne fortune_interaction_none
call BeginFortuneDialog3D
mov eax,1
ret
fortune_interaction_none:
xor eax,eax
ret
TryFortuneInteraction3D ENDP
CheckFortuneInteractions3D PROC
cmp hallLevel3D,3
je fortune_check_room
cmp fortunePassageTime3D,1400
jne @F
invoke HallTarget3D,FP_1*13/10,10*FP_1,0,OFFSET fortuneStairHint3D
@@:
cmp fortunePassageTarget3D,0
jne fortune_check_release
invoke HallTarget3D,FP_1*14/10,FP_1*87/10,INTERACT_SECRET_LAMP,OFFSET fortuneLampPull3D
ret
fortune_check_release:
invoke HallTarget3D,FP_1*14/10,FP_1*87/10,INTERACT_SECRET_LAMP,OFFSET fortuneLampReturn3D
ret
fortune_check_room:
mov interactTargetId,0
mov interactPromptPtr,0
invoke HallTarget3D,FP_1*173/10,11*FP_1,0,OFFSET fortuneExitHint3D
invoke HallTarget3D,FP_1*141/10,11*FP_1,INTERACT_FORTUNE,OFFSET fortuneTalk3D
fortune_check_done:
ret
CheckFortuneInteractions3D ENDP
RenderFortuneEntrance3D PROC uses ebx esi
local panelLo:DWORD,panelHi:DWORD,bodyLow:DWORD,bodyHigh:DWORD
; The real wall remains behind this aperture; the portal compositor clips it.
invoke DrawCityBoxSides3D,FP_1*128/100,FP_1*138/100,FP_1*88/10,9*FP_1,0,FP_1*5/2,102
invoke DrawCityBoxSides3D,FP_1*128/100,FP_1*138/100,11*FP_1,FP_1*112/10,0,FP_1*5/2,102
invoke DrawCityBoxSides3D,FP_1*128/100,FP_1*138/100,9*FP_1,11*FP_1,FP_1*23/10,FP_1*5/2,102
mov eax,fortunePassageTime3D
sub eax,280
jns @F
xor eax,eax
@@:
imul eax,2*FP_1
xor edx,edx
mov ecx,1120
div ecx
add eax,9*FP_1
mov panelLo,eax
add eax,2*FP_1
mov panelHi,eax
invoke DrawCityVertical3D,0,FP_1*135/100,panelLo,panelHi,0,FP_1*23/10,1
; A rigid brass arm stays fixed while the lantern itself is pulled down.
mov eax,fortunePassageTime3D
cmp eax,280
jbe @F
mov eax,280
@@:
imul eax,FP_1/5
xor edx,edx
mov ecx,280
div ecx
mov ebx,FP_1*14/10
sub ebx,eax
mov bodyLow,ebx
add ebx,FP_1/2
mov bodyHigh,ebx
invoke DrawCityBoxSides3D,FP_1*131/100,FP_1*155/100,FP_1*857/100,FP_1*883/100,bodyLow,bodyHigh,120
invoke DrawCityHorizontal3D,FP_1*131/100,FP_1*155/100,FP_1*857/100,FP_1*883/100,bodyHigh,110
invoke DrawCityHorizontal3D,FP_1*131/100,FP_1*155/100,FP_1*857/100,FP_1*883/100,bodyLow,110
invoke DrawCityBoxSides3D,FP_1*102/100,FP_1*143/100,FP_1*866/100,FP_1*874/100,FP_1*194/100,2*FP_1,110
invoke DrawCityBoxSides3D,FP_1*140/100,FP_1*146/100,FP_1*866/100,FP_1*874/100,bodyHigh,2*FP_1,110
ret
RenderFortuneEntrance3D ENDP
DrawFortuneFixedProp3D PROC uses esi prop:DWORD,axis:DWORD
local coordinate:DWORD,lo:DWORD,hi:DWORD,zTop:DWORD,material:DWORD
mov esi,prop
mov eax,[esi].CityProp3D.posX
mov ecx,[esi].CityProp3D.posY
cmp axis,0
je @F
xchg eax,ecx
@@:
mov coordinate,eax
mov eax,[esi].CityProp3D.worldW
sar eax,1
mov edx,ecx
sub edx,eax
mov lo,edx
add ecx,eax
mov hi,ecx
mov eax,[esi].CityProp3D.groundZ
add eax,[esi].CityProp3D.worldH
mov zTop,eax
mov eax,[esi].CityProp3D.frame
add eax,136
mov material,eax
invoke DrawCityVertical3D,axis,coordinate,lo,hi,[esi].CityProp3D.groundZ,zTop,material
ret
DrawFortuneFixedProp3D ENDP
RenderFortuneFurnishings3D PROC uses ebx
; Layered cloth, a worn rug and irregular floor clutter make a working den.
invoke DrawCityHorizontal3D,FP_1*135/10,FP_1*176/10,FP_1*97/10,FP_1*124/10,FP_1/100,135
; Furniture fronts have fixed wall axes and solid depth, never billboards.
invoke DrawCityBoxSides3D,FP_1*134/10,FP_1*148/10,FP_1*902/100,FP_1*931/100,0,FP_1*21/10,132
invoke DrawCityHorizontal3D,FP_1*134/10,FP_1*148/10,FP_1*902/100,FP_1*931/100,FP_1*21/10,134
invoke DrawCityBoxSides3D,FP_1*154/10,FP_1*176/10,FP_1*1266/100,FP_1*129/10,FP_1*2/10,FP_1*6/10,135
invoke DrawCityBoxSides3D,FP_1*1771/100,FP_1*1798/100,FP_1*1225/100,FP_1*1285/100,0,FP_1*19/10,132
invoke DrawCityBoxSides3D,FP_1*14,FP_1*152/10,FP_1*1271/100,FP_1*129/10,0,FP_1*6/10,132
; Both poses keep the face hidden; the hands and mantle change instead.
mov eax,animationClock3D
xor edx,edx
mov ecx,6000
div ecx
xor eax,eax
cmp edx,4800
jb @F
inc eax
@@:
mov fortuneSeer3D.frame,eax
invoke DrawFortuneFixedProp3D,OFFSET fortuneTapestry3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneShelf3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneBench3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneMirror3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneCandleA3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneCandleB3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneScrolls3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneHerbs3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneHerbsB3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneHerbsC3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneHanging3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneHanging3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortunePotions3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneBooks3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneChest3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneBookPileA3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneBookPileB3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneCauldron3D,0
invoke DrawFortuneFixedProp3D,OFFSET fortuneTray3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneFloorCandlesA3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneFloorCandlesB3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneNorthLamp3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneSouthLamp3D,1
invoke DrawFortuneFixedProp3D,OFFSET fortuneSeer3D,0
ret
RenderFortuneFurnishings3D ENDP
FortuneLightAt3D PROC uses ebx esi edi wx:DWORD,wy:DWORD,wz:DWORD
local best:DWORD
mov best,0
xor esi,esi
fortune_light_source:
mov eax,wx
sub eax,fortuneLights3D[esi]
imul eax,eax
mov ebx,eax
mov eax,wy
sub eax,fortuneLights3D[esi+4]
imul eax,eax
add ebx,eax
mov eax,wz
sub eax,fortuneLights3D[esi+8]
imul eax,eax
add eax,ebx
cmp eax,900
jae fortune_light_source_next
neg eax
add eax,900
imul eax,fortuneLights3D[esi+12]
xor edx,edx
mov ecx,900
div ecx
cmp eax,best
jbe fortune_light_source_next
mov best,eax
fortune_light_source_next:
add esi,16
cmp esi,FORTUNE_LIGHT_COUNT*16
jb fortune_light_source
mov eax,best
ret
FortuneLightAt3D ENDP
ApplyFortuneLighting3D PROC uses ebx esi edi
local row:DWORD,column:DWORD,planeDepth:DWORD,depth:DWORD
local wx:DWORD,wy:DWORD,wz:DWORD,light:DWORD
local gainR:DWORD,gainG:DWORD,gainB:DWORD,subRow:DWORD,subColumn:DWORD
mov row,0
fortune_light_row:
mov eax,row
cmp eax,viewHorizon3D
jge fortune_light_floor
mov ecx,viewHorizon3D
sub ecx,eax
mov eax,viewCeiling3D
sub eax,viewEye3D
jmp fortune_light_plane
fortune_light_floor:
sub eax,viewHorizon3D
inc eax
mov ecx,eax
mov eax,viewEye3D
fortune_light_plane:
imul eax,SCREEN_H
cdq
idiv ecx
mov planeDepth,eax
mov column,0
fortune_light_column:
imul edi,row,SCREEN_W
add edi,column
mov eax,cityDepthBuffer3D[edi*4]
cmp eax,planeDepth
jle @F
mov eax,planeDepth
@@:
; Limit the reconstructed sample at the horizon before squaring distances.
cmp eax,16*FP_1
jle @F
mov eax,16*FP_1
@@:
mov depth,eax
mov ebx,column
imul cityRayX3D[ebx*4]
shrd eax,edx,16
add eax,playerX
sar eax,12
mov wx,eax
mov eax,depth
imul cityRayY3D[ebx*4]
shrd eax,edx,16
add eax,playerY
sar eax,12
mov wy,eax
mov eax,viewHorizon3D
sub eax,row
imul eax,depth
cdq
mov ecx,SCREEN_H
idiv ecx
add eax,viewEye3D
sar eax,12
mov wz,eax
invoke FortuneLightAt3D,wx,wy,wz
mov light,eax
mov ebx,eax
xor edx,edx
mov ecx,3
div ecx
add eax,48
mov gainR,eax
imul eax,ebx,3
shr eax,2
add eax,64
mov gainG,eax
add ebx,82
mov gainB,ebx
mov subRow,0
fortune_light_block_row:
mov subColumn,0
fortune_light_pixel:
imul esi,subRow,SCREEN_W
add esi,subColumn
add esi,edi
mov ebx,screenBuffer[esi*4]
; Preserve the small blue-white flame cores only beside an actual light.
cmp light,100
jb fortune_light_shade
cmp bl,210
jb fortune_light_shade
cmp bh,180
jae fortune_light_pixel_next
fortune_light_shade:
movzx eax,bl
imul eax,gainB
shr eax,8
mov ecx,eax
movzx eax,bh
imul eax,gainG
shr eax,8
shl eax,8
or ecx,eax
mov eax,ebx
shr eax,16
and eax,255
imul eax,gainR
shr eax,8
shl eax,16
or eax,ecx
mov screenBuffer[esi*4],eax
fortune_light_pixel_next:
inc subColumn
cmp subColumn,2
jb fortune_light_pixel
inc subRow
cmp subRow,2
jb fortune_light_block_row
add column,2
cmp column,SCREEN_W
jb fortune_light_column
add row,2
cmp row,SCREEN_H
jb fortune_light_row
ret
ApplyFortuneLighting3D ENDP
RenderFortuneRoom3D PROC
call RenderFortuneFurnishings3D
call ApplyFortuneLighting3D
ret
RenderFortuneRoom3D ENDP
FortuneCanWalk3D PROC px:DWORD,py:DWORD
cmp px,FP_1*1485/100
ja fortune_walk_north
cmp py,FP_1*98/10
jb fortune_walk_north
cmp py,FP_1*118/10
jbe fortune_walk_block
fortune_walk_north:
cmp py,FP_1*1005/100
ja @F
cmp px,FP_1*146/10
jb @F
cmp px,FP_1*1565/100
jbe fortune_walk_block
@@:
cmp py,FP_1*965/100
ja fortune_walk_south
cmp px,15*FP_1
jb fortune_walk_block
cmp px,FP_1*155/10
jb fortune_walk_south
cmp px,FP_1*171/10
jbe fortune_walk_block
fortune_walk_south:
cmp px,FP_1*172/10
jb @F
cmp py,FP_1*122/10
jae fortune_walk_block
@@:
cmp py,FP_1*1175/100
jb fortune_walk_clear
cmp px,FP_1*146/10
jb fortune_walk_block
cmp px,15*FP_1
jb @F
cmp px,FP_1*158/10
jbe fortune_walk_block
@@:
cmp py,FP_1*121/10
jae fortune_walk_block
fortune_walk_clear:
mov eax,1
ret
fortune_walk_block:
xor eax,eax
ret
FortuneCanWalk3D ENDP
RenderFortuneStairs3D PROC uses ebx esi
local x0:DWORD,x1:DWORD,z0:DWORD,z1:DWORD
; A west-facing descent: the player controls every step, with level landings.
xor esi,esi
fortune_stair_step:
imul eax,esi,FP_1/3
add eax,19*FP_1
mov x0,eax
add eax,FP_1/3
mov x1,eax
imul eax,esi,FP_1/16
mov z0,eax
add eax,FP_1/16
mov z1,eax
invoke DrawCityHorizontal3D,x0,x1,9*FP_1,11*FP_1,z0,134
invoke DrawCityVertical3D,0,x1,9*FP_1,11*FP_1,z0,z1,132
inc esi
cmp esi,24
jb fortune_stair_step
invoke DrawCityHorizontal3D,27*FP_1,31*FP_1,9*FP_1,11*FP_1,FP_1*3/2,134
invoke DrawCityHorizontal3D,16*FP_1,19*FP_1,9*FP_1,11*FP_1,0,134
ret
RenderFortuneStairs3D ENDP
RenderFortuneViews3D PROC
cmp engineMode,MODE_STAIRS
je fortune_views_stairs
cmp hallLevel3D,3
je fortune_views_room
cmp hallLevel3D,0
jne fortune_views_done
cmp fortunePassageTime3D,280
jbe fortune_views_done
cmp playerX,FP_1*14/10
jbe fortune_views_done
invoke DrawInteriorView3D,0,FP_1*138/100,9*FP_1,11*FP_1,0,FP_1*23/10,28*FP_1,0,FP_1*3/2,OFFSET fortuneStairMap3D,MODE_STAIRS,0,134,3
ret
fortune_views_room:
cmp playerX,FP_1*175/10
jae fortune_views_done
invoke DrawInteriorView3D,0,FP_1*174/10,10*FP_1,12*FP_1,0,FP_1*5/2,0,-FP_1,0,OFFSET fortuneStairMap3D,MODE_STAIRS,3,134,3
ret
fortune_views_stairs:
cmp playerX,FP_1*2955/100
jae @F
invoke DrawInteriorView3D,0,FP_1*295/10,9*FP_1,11*FP_1,FP_1*3/2,4*FP_1,-28*FP_1,0,-FP_1*3/2,OFFSET casinoMap,MODE_WALK,0,100,0
@@:
cmp playerX,FP_1*172/10
jbe fortune_views_done
invoke DrawInteriorView3D,0,FP_1*173/10,9*FP_1,11*FP_1,0,FP_1*5/2,0,FP_1,0,OFFSET fortuneRoomMap3D,MODE_WALK,3,134,0
fortune_views_done:
ret
RenderFortuneViews3D ENDP