PyramidSelect3D PROC uses ebx ball:DWORD cmp poolBreak3D,0 je @F mov pyrCue3D,0 ret @@: mov ebx,ball cmp ebx,15 ja pyr_select_default cmp poolActive3D[ebx*4],1 jne pyr_select_default mov pyrCue3D,ebx mov poolCalled3D,ebx ret pyr_select_default: mov ebx,pyrCue3D cmp poolActive3D[ebx*4],1 je pyr_select_done xor ebx,ebx pyr_select_scan: cmp poolActive3D[ebx*4],1 je pyr_select_found inc ebx cmp ebx,16 jb pyr_select_scan ret pyr_select_found: mov pyrCue3D,ebx mov poolCalled3D,ebx pyr_select_done: ret PyramidSelect3D ENDP PyramidPick3D PROC uses ebx esi px:DWORD,py:DWORD xor esi,esi pyr_pick_ball: cmp poolActive3D[esi*4],1 jne pyr_pick_next mov eax,px sub eax,poolX3D[esi*4] sar eax,12 imul eax,eax mov ebx,eax mov eax,py sub eax,poolY3D[esi*4] sar eax,12 imul eax,eax add eax,ebx cmp eax,36*16*16 ja pyr_pick_next mov eax,esi ret pyr_pick_next: inc esi cmp esi,16 jb pyr_pick_ball mov eax,-1 ret PyramidPick3D ENDP PyramidDirection3D PROC uses ebx esi mov ecx,poolAngle3D and ecx,511 cmp pyrFineActive3D,0 jne pyr_direction_fine mov eax,tableCos[ecx*4] mov pyrDirX3D,eax mov eax,tableSin[ecx*4] mov pyrDirY3D,eax ret pyr_direction_fine: mov ecx,pyrFine3D and ecx,4095 mov ebx,ecx and ebx,7 shr ecx,3 lea esi,[ecx+1] and esi,511 mov eax,tableCos[esi*4] sub eax,tableCos[ecx*4] imul eax,ebx sar eax,3 add eax,tableCos[ecx*4] mov pyrDirX3D,eax mov eax,tableSin[esi*4] sub eax,tableSin[ecx*4] imul eax,ebx sar eax,3 add eax,tableSin[ecx*4] mov pyrDirY3D,eax ret PyramidDirection3D ENDP PoolAimPoint3D PROC uses ebx px:DWORD,py:DWORD local deltaX:DWORD,deltaY:DWORD,phase:DWORD mov ebx,pyrCue3D mov eax,px sub eax,poolX3D[ebx*4] mov deltaX,eax mov ecx,py sub ecx,poolY3D[ebx*4] mov deltaY,ecx or eax,ecx jz pyr_aim_done fild deltaY fild deltaX fpatan fmul pyrAimFactor3D fistp phase mov eax,phase and eax,4095 mov pyrFine3D,eax mov pyrFineActive3D,1 call PyramidDirection3D pyr_aim_done: ret PoolAimPoint3D ENDP PoolShoot3D PROC uses ebx esi cmp poolState3D,POOL_AIM jne pyr_shoot_done mov ebx,pyrCue3D cmp ebx,15 ja pyr_shoot_done cmp poolActive3D[ebx*4],1 jne pyr_shoot_done cmp pyrTablePack3D.pixels,0 je pyr_shoot_done cmp pyrBallsPack3D.pixels,0 je pyr_shoot_done cmp poolBreak3D,0 je @F test ebx,ebx jnz pyr_shoot_done cmp poolX3D,95*FP_1 jge pyr_shoot_done @@: mov poolFirst3D,-1 mov poolRail3D,0 mov poolBreakRails3D,0 mov poolPotted3D,0 mov poolFoul3D,0 mov pyrFoulReason3D,0 mov pyrValidEnd3D,0 mov pyrSettled3D,0 mov poolAccumulator3D,0 mov poolTime3D,0 mov poolState3D,POOL_STRIKE xor esi,esi pyr_shoot_history: mov pyrHistoryRails3D[esi*4],0 mov pyrHistoryCross3D[esi*4],0 mov pyrRailHalf3D[esi*4],0 inc esi cmp esi,16 jb pyr_shoot_history call PyramidDirection3D pyr_shoot_done: ret PoolShoot3D ENDP ; Obstruction checks use Q4 geometry with a 64-bit projection numerator. PyramidPathClear3D PROC uses ebx esi edi sx:DWORD,sy:DWORD,ex:DWORD,ey:DWORD,skipA:DWORD,skipB:DWORD local deltaX:DWORD,deltaY:DWORD,length2:DWORD,projection:DWORD,wx:DWORD,wy:DWORD mov eax,ex sub eax,sx sar eax,12 mov deltaX,eax imul eax,eax mov ebx,eax mov eax,ey sub eax,sy sar eax,12 mov deltaY,eax imul eax,eax add eax,ebx mov length2,eax test eax,eax jz pyr_path_bad xor esi,esi pyr_path_ball: cmp esi,skipA je pyr_path_next cmp esi,skipB je pyr_path_next cmp poolActive3D[esi*4],1 jne pyr_path_next mov eax,poolX3D[esi*4] sub eax,sx sar eax,12 mov wx,eax imul eax,deltaX mov ebx,eax mov eax,poolY3D[esi*4] sub eax,sy sar eax,12 mov wy,eax imul eax,deltaY add eax,ebx test eax,eax jle pyr_path_next cmp eax,length2 jge pyr_path_next mov ecx,FP_1 imul ecx idiv length2 mov projection,eax imul deltaX shrd eax,edx,16 sub eax,wx imul eax,eax mov ebx,eax mov eax,projection imul deltaY shrd eax,edx,16 sub eax,wy imul eax,eax add eax,ebx cmp eax,25*16*16 jb pyr_path_bad pyr_path_next: inc esi cmp esi,16 jb pyr_path_ball mov eax,1 ret pyr_path_bad: xor eax,eax ret PyramidPathClear3D ENDP ; Search every cue/target/pocket triple; no hidden outcomes or teleported pots. PoolChooseAI3D PROC uses ebx esi edi local cue:DWORD,target:DWORD,pocket:DWORD,deltaX:DWORD,deltaY:DWORD,distance:DWORD local gx:DWORD,gy:DWORD,best:DWORD,bestCue:DWORD,bestX:DWORD,bestY:DWORD,bestPower:DWORD local shotLength:DWORD cmp poolBreak3D,0 je pyr_ai_search mov pyrCue3D,0 mov poolAngle3D,0 mov pyrFineActive3D,0 mov poolPower3D,85000 ret pyr_ai_search: mov best,7FFFFFFFh mov bestCue,-1 mov cue,0 pyr_ai_cue: mov ecx,cue cmp poolActive3D[ecx*4],1 jne pyr_ai_next_cue mov target,0 pyr_ai_target: mov esi,target cmp esi,cue je pyr_ai_next_target cmp poolActive3D[esi*4],1 jne pyr_ai_next_target mov pocket,0 pyr_ai_pocket: mov edi,pocket mov eax,poolPocketX3D[edi*4] sub eax,poolX3D[esi*4] mov deltaX,eax mov eax,poolPocketY3D[edi*4] sub eax,poolY3D[esi*4] mov deltaY,eax invoke PyramidDistance3D,deltaX,deltaY test eax,eax jz pyr_ai_next_pocket mov distance,eax mov eax,deltaX mov ecx,POOL_DIAMETER imul ecx idiv distance mov ebx,poolX3D[esi*4] sub ebx,eax mov gx,ebx mov eax,deltaY mov ecx,POOL_DIAMETER imul ecx idiv distance mov ebx,poolY3D[esi*4] sub ebx,eax mov gy,ebx cmp gx,30*FP_1+POOL_RADIUS jl pyr_ai_next_pocket cmp gx,290*FP_1-POOL_RADIUS jg pyr_ai_next_pocket cmp gy,50*FP_1+POOL_RADIUS jl pyr_ai_next_pocket cmp gy,180*FP_1-POOL_RADIUS jg pyr_ai_next_pocket mov ecx,cue mov eax,gx sub eax,poolX3D[ecx*4] sar eax,12 mov ebx,deltaX sar ebx,12 imul eax,ebx mov ebx,eax mov eax,gy sub eax,poolY3D[ecx*4] sar eax,12 mov edx,deltaY sar edx,12 imul eax,edx add eax,ebx test eax,eax jle pyr_ai_next_pocket invoke PyramidPathClear3D,poolX3D[ecx*4],poolY3D[ecx*4],gx,gy,cue,target test eax,eax jz pyr_ai_next_pocket mov edi,pocket invoke PyramidPathClear3D,poolX3D[esi*4],poolY3D[esi*4],poolPocketX3D[edi*4],poolPocketY3D[edi*4],cue,target test eax,eax jz pyr_ai_next_pocket mov ecx,cue mov eax,gx sub eax,poolX3D[ecx*4] mov ebx,gy sub ebx,poolY3D[ecx*4] invoke PyramidDistance3D,eax,ebx mov shotLength,eax add eax,distance cmp eax,best jae pyr_ai_next_pocket mov best,eax mov ecx,cue mov bestCue,ecx mov ecx,gx mov bestX,ecx mov ecx,gy mov bestY,ecx ; v^2/(2*rolling loss) estimates the travel budget, with margin for a cut. sar eax,16 imul eax,220 add eax,22000 cmp eax,85000 jbe @F mov eax,85000 @@: mov bestPower,eax pyr_ai_next_pocket: inc pocket cmp pocket,6 jb pyr_ai_pocket pyr_ai_next_target: inc target cmp target,16 jb pyr_ai_target pyr_ai_next_cue: inc cue cmp cue,16 jb pyr_ai_cue cmp bestCue,-1 je pyr_ai_safety mov eax,bestCue mov pyrCue3D,eax invoke PoolAimPoint3D,bestX,bestY mov eax,bestPower mov poolPower3D,eax ret pyr_ai_safety: invoke PyramidSelect3D,-1 mov esi,pyrCue3D xor edi,edi pyr_ai_safety_target: cmp edi,esi je @F cmp poolActive3D[edi*4],1 je pyr_ai_safety_found @@: inc edi cmp edi,16 jb pyr_ai_safety_target ret pyr_ai_safety_found: invoke PoolAimPoint3D,poolX3D[edi*4],poolY3D[edi*4] mov poolPower3D,65000 ret PoolChooseAI3D ENDP HandlePool3D PROC uses ebx esi edi xor esi,esi pyr_sink_tick: mov eax,deltaTime3D sub poolSinkTime3D[esi*4],eax jnc @F mov poolSinkTime3D[esi*4],0 @@: inc esi cmp esi,16 jb pyr_sink_tick mov eax,deltaTime3D add poolTime3D,eax cmp mouseClick3D,0 je pyr_update cmp mouseY3D,3 jb pyr_update cmp mouseY3D,17 jae pyr_update cmp mouseX3D,252 jb pyr_update cmp mouseX3D,312 jae pyr_update mov engineMode,MODE_WALK call ClearKeys3D call SaveProgress3D ret pyr_update: cmp poolState3D,POOL_MOVING jne pyr_strike add poolAccumulator3D,eax pyr_fixed_tick: cmp poolAccumulator3D,2 jb pyr_handle_done sub poolAccumulator3D,2 call PoolStep3D cmp poolStill3D,0 je pyr_fixed_tick call PoolFinishShot3D ret pyr_strike: cmp poolState3D,POOL_STRIKE jne pyr_idle cmp poolTime3D,240 jb pyr_handle_done mov ebx,pyrCue3D mov eax,pyrDirX3D imul poolPower3D shrd eax,edx,16 mov poolVX3D[ebx*4],eax mov eax,pyrDirY3D imul poolPower3D shrd eax,edx,16 mov poolVY3D[ebx*4],eax mov poolState3D,POOL_MOVING mov poolAccumulator3D,0 ret pyr_idle: cmp poolState3D,POOL_RESULT jne pyr_actor invoke Pressed3D,20h test eax,eax jnz pyr_restart cmp mouseClick3D,0 je pyr_handle_done cmp mouseY3D,211 jb pyr_handle_done cmp mouseX3D,202 jb pyr_handle_done pyr_restart: call InitPool3D ret pyr_actor: cmp poolPlayer3D,1 jne pyr_human cmp poolAI3D,0 je pyr_human cmp poolTime3D,1000 jb pyr_handle_done cmp poolState3D,POOL_PENALTY jne @F xor esi,esi pyr_ai_penalty: cmp poolActive3D[esi*4],1 je pyr_ai_penalty_pick inc esi cmp esi,16 jb pyr_ai_penalty ret pyr_ai_penalty_pick: invoke PyramidPenalty3D,esi ret @@: cmp poolState3D,POOL_CHOICE jne @F invoke PyramidChoice3D,0 ret @@: call PoolChooseAI3D call PoolShoot3D ret pyr_human: cmp poolState3D,POOL_CHOICE jne pyr_human_penalty mov esi,0 pyr_choice_key: lea eax,[esi+31h] invoke Pressed3D,eax test eax,eax jnz pyr_choice_selected inc esi cmp esi,4 jb pyr_choice_key invoke Pressed3D,0Dh test eax,eax jz @F xor esi,esi jmp pyr_choice_selected @@: cmp mouseClick3D,0 je pyr_handle_done cmp mouseY3D,215 jb pyr_handle_done cmp mouseY3D,237 jae pyr_handle_done mov eax,mouseX3D sub eax,8 js pyr_handle_done xor edx,edx mov ecx,78 div ecx cmp edx,70 jae pyr_handle_done mov esi,eax pyr_choice_selected: invoke PyramidChoice3D,esi ret pyr_human_penalty: cmp poolState3D,POOL_PENALTY jne pyr_human_aim invoke Pressed3D,0Dh test eax,eax jnz pyr_penalty_skip cmp mouseClick3D,0 je pyr_handle_done cmp mouseY3D,215 jb @F cmp mouseY3D,238 jae @F cmp mouseX3D,202 jae pyr_penalty_skip @@: mov eax,mouseX3D shl eax,16 mov ebx,mouseY3D shl ebx,16 invoke PyramidPick3D,eax,ebx cmp eax,-1 je pyr_handle_done invoke PyramidPenalty3D,eax ret pyr_penalty_skip: mov poolState3D,POOL_CHOICE ret pyr_human_aim: cmp mouseClick3D,0 je pyr_keys mov mouseClick3D,0 mov eax,mouseX3D mov ebx,mouseY3D cmp ebx,50 jb pyr_keys cmp ebx,180 ja pyr_mouse_footer shl eax,16 shl ebx,16 cmp pyrPickClick3D,0 je @F mov pyrPickClick3D,0 invoke PyramidPick3D,eax,ebx cmp eax,-1 je pyr_handle_done invoke PyramidSelect3D,eax ret @@: cmp poolBreak3D,0 je pyr_mouse_aim cmp keys3D[10h],0 je pyr_mouse_aim cmp eax,95*FP_1 jge pyr_handle_done invoke PoolPlaceCue3D,eax,ebx ret pyr_mouse_aim: invoke PoolAimPoint3D,eax,ebx ret pyr_mouse_footer: cmp ebx,193 jb pyr_keys cmp ebx,209 ja pyr_mouse_command cmp eax,202 jb pyr_keys cmp eax,308 ja pyr_keys sub eax,202 imul eax,82000 xor edx,edx mov ecx,106 div ecx add eax,8000 mov poolPower3D,eax ret pyr_mouse_command: cmp ebx,211 jb pyr_keys cmp ebx,238 jae pyr_keys cmp eax,86 jb pyr_cycle_cue cmp eax,196 jb pyr_toggle_guide call PoolShoot3D ret pyr_keys: mov eax,deltaTime3D imul eax,120 cmp keys3D[10h],0 je @F mov eax,deltaTime3D imul eax,30 @@: add eax,pyrAimCarry3D xor edx,edx mov ecx,1000 div ecx mov pyrAimCarry3D,edx mov ebx,eax cmp pyrFineActive3D,0 jne @F mov eax,poolAngle3D shl eax,3 mov pyrFine3D,eax @@: cmp keys3D[25h],0 je @F sub pyrFine3D,ebx mov pyrFineActive3D,1 @@: cmp keys3D[27h],0 je @F add pyrFine3D,ebx mov pyrFineActive3D,1 @@: and pyrFine3D,4095 mov eax,deltaTime3D imul eax,100 cmp keys3D[26h],0 je @F add poolPower3D,eax @@: cmp keys3D[28h],0 je @F sub poolPower3D,eax @@: cmp poolPower3D,8000 jge @F mov poolPower3D,8000 @@: cmp poolPower3D,90000 jle @F mov poolPower3D,90000 @@: invoke Pressed3D,42h test eax,eax jnz pyr_cycle_cue invoke Pressed3D,50h test eax,eax jnz pyr_toggle_guide invoke Pressed3D,20h test eax,eax jz pyr_handle_done call PoolShoot3D ret pyr_toggle_guide: xor pyrGuideOn3D,1 ret pyr_cycle_cue: cmp poolBreak3D,0 jne pyr_handle_done mov esi,pyrCue3D mov edi,16 pyr_cycle_scan: inc esi and esi,15 cmp poolActive3D[esi*4],1 je pyr_cycle_found dec edi jnz pyr_cycle_scan ret pyr_cycle_found: invoke PyramidSelect3D,esi pyr_handle_done: ret HandlePool3D ENDP