; Four additional visitors do not expand the six legacy poker personality records. NpcEntity3D PROC slot:DWORD mov eax,slot cmp eax,6 jae nv_entity_extra imul eax,SIZEOF NpcMindEntity add eax,OFFSET casinoNpcs3D ret nv_entity_extra: sub eax,6 imul eax,SIZEOF NpcMindEntity add eax,OFFSET npcExtraActors3D ret NpcEntity3D ENDP NpcVisitRandom3D PROC slot:DWORD,limit:DWORD mov ecx,slot mov eax,npcVisitRng3D[ecx*4] imul eax,1664525 add eax,1013904223 mov npcVisitRng3D[ecx*4],eax shr eax,8 xor edx,edx div limit mov eax,edx ret NpcVisitRandom3D ENDP InitNpcVisits3D PROC uses ebx esi edi lea edi,npcStationOwner3D mov ecx,NPC_DEST_COUNT mov eax,-1 rep stosd xor esi,esi nv_init_person: imul ebx,esi,SIZEOF NpcLife3D imul eax,esi,7919 add eax,071C4A931h mov npcVisitRng3D[esi*4],eax mov eax,npcVisitInitialChips3D[esi*4] mov npcVisitChips3D[esi*4],eax mov npcVisitRounds3D[esi*4],0 mov npcVisitThirst3D[esi*4],0 mov npcVisitMood3D[esi*4],0 mov npcVisitBlocked3D[esi*4],0 mov npcRemark3D[esi*4],0 mov npcRemarkTime3D[esi*4],0 mov eax,npcLife3D[ebx].destination mov npcStationOwner3D[eax*4],esi invoke NpcArrive3D,esi ; Different initial waits prevent a synchronized crowd surge. imul eax,esi,1100 add npcLife3D[ebx].remaining,eax inc esi cmp esi,NPC_LIFE_COUNT jb nv_init_person ret InitNpcVisits3D ENDP NpcReleaseStation3D PROC uses esi slot:DWORD xor esi,esi nv_release_station: mov eax,slot cmp npcStationOwner3D[esi*4],eax jne @F mov npcStationOwner3D[esi*4],-1 @@: inc esi cmp esi,NPC_DEST_COUNT jb nv_release_station ret NpcReleaseStation3D ENDP NpcArrive3D PROC uses ebx esi slot:DWORD mov esi,slot imul ebx,esi,SIZEOF NpcLife3D mov npcLife3D[ebx].state,NPC_STAND mov npcLife3D[ebx].poseClock,0 mov npcSitTime3D[esi*4],0 mov npcRemark3D[esi*4],0 mov npcRemarkTime3D[esi*4],0 mov npcVisitBlocked3D[esi*4],0 mov npcVisitRounds3D[esi*4],0 mov eax,npcLife3D[ebx].destination mov ecx,npcStationKind3D[eax*4] mov npcVisitActivity3D[esi*4],ecx cmp ecx,NV_LEAVE jne @F invoke NpcReleaseStation3D,esi mov npcLife3D[ebx].state,NPC_AWAY mov npcLife3D[ebx].remaining,8000 ret @@: cmp ecx,NV_ORDER jne @F mov npcLife3D[ebx].remaining,2400 mov npcRemark3D[esi*4],OFFSET npcRemarkOrder3D mov npcRemarkTime3D[esi*4],2200 ret @@: cmp ecx,NV_REST jne @F mov npcLife3D[ebx].state,NPC_SIT invoke NpcVisitRandom3D,esi,9000 add eax,14000 mov npcLife3D[ebx].remaining,eax ret @@: cmp ecx,NV_WATCH jne @F invoke NpcVisitRandom3D,esi,10000 add eax,12000 mov npcLife3D[ebx].remaining,eax ret @@: invoke NpcVisitRandom3D,esi,1800 add eax,2200 mov npcLife3D[ebx].remaining,eax ret NpcArrive3D ENDP ; Reserve a free destination before walking; neither a failed path nor a blocked ; walker keeps a permanent claim on furniture. Casino RNG and bankroll are unused. NpcChooseVisit3D PROC uses ebx esi edi slot:DWORD local candidate:DWORD,wasSeated:DWORD,attempt:DWORD mov esi,slot imul ebx,esi,SIZEOF NpcLife3D mov eax,npcLife3D[ebx].state mov wasSeated,eax mov attempt,0 nv_choose_candidate: cmp npcVisitChips3D[esi*4],20 jae nv_choose_thirst mov candidate,0 jmp nv_choose_free nv_choose_thirst: cmp npcVisitMood3D[esi*4],-2 jle nv_choose_break cmp npcVisitThirst3D[esi*4],3 jb nv_choose_random nv_choose_break: mov eax,6 test attempt,1 jz @F mov eax,10 @@: mov candidate,eax cmp attempt,2 jb nv_choose_free nv_choose_random: invoke NpcVisitRandom3D,esi,100 cmp eax,45 jae nv_choose_bar mov eax,npcVisitFavourite3D[esi*4] mov candidate,eax cmp attempt,0 je nv_choose_free invoke NpcVisitRandom3D,esi,5 inc eax mov candidate,eax jmp nv_choose_free nv_choose_bar: cmp eax,65 jae nv_choose_rest invoke NpcVisitRandom3D,esi,2 imul eax,4 add eax,6 mov candidate,eax jmp nv_choose_free nv_choose_rest: cmp eax,83 jae nv_choose_watch invoke NpcVisitRandom3D,esi,2 imul eax,4 add eax,7 mov candidate,eax jmp nv_choose_free nv_choose_watch: invoke NpcVisitRandom3D,esi,2 add eax,8 mov candidate,eax nv_choose_free: mov edi,candidate cmp edi,npcLife3D[ebx].destination je nv_choose_next cmp npcStationOwner3D[edi*4],-1 jne nv_choose_next invoke NpcPlanPath3D,esi,edi test eax,eax jz nv_choose_next invoke NpcReleaseStation3D,esi mov npcStationOwner3D[edi*4],esi mov npcLife3D[ebx].state,NPC_WALK mov npcLife3D[ebx].poseClock,0 mov npcVisitActivity3D[esi*4],NV_IDLE mov npcRise3D[esi*4],0 cmp wasSeated,NPC_SIT jne @F mov npcRise3D[esi*4],800 @@: inc npcLife3D[ebx].routeStep mov eax,1 ret nv_choose_next: inc attempt cmp attempt,16 jb nv_choose_candidate ; Keep a seated visitor seated when all useful destinations are busy. mov eax,wasSeated cmp eax,NPC_AWAY jne @F mov eax,NPC_STAND @@: mov npcLife3D[ebx].state,eax mov npcLife3D[ebx].remaining,1800 xor eax,eax ret NpcChooseVisit3D ENDP NpcAdvanceVisit3D PROC uses ebx esi edi slot:DWORD mov esi,slot imul ebx,esi,SIZEOF NpcLife3D mov eax,npcVisitActivity3D[esi*4] cmp eax,NV_YIELD jne @F mov npcLife3D[ebx].state,NPC_WALK mov npcLife3D[ebx].poseClock,0 ret @@: cmp eax,NV_GAME je nv_play_result cmp eax,NV_WIN je nv_after_result cmp eax,NV_LOSS je nv_after_result cmp eax,NV_ORDER je nv_order_ready cmp eax,NV_DRINK je nv_social cmp eax,NV_REST je nv_social cmp eax,NV_LEAVE jne @F mov eax,npcVisitInitialChips3D[esi*4] mov npcVisitChips3D[esi*4],eax mov npcVisitMood3D[esi*4],0 mov npcVisitThirst3D[esi*4],0 @@: invoke NpcChooseVisit3D,esi ret nv_play_result: mov edi,npcVisitStake3D[esi*4] invoke NpcVisitRandom3D,esi,100 cmp eax,42 jae nv_play_loss add npcVisitChips3D[esi*4],edi cmp npcVisitMood3D[esi*4],3 jge @F inc npcVisitMood3D[esi*4] @@: mov npcVisitActivity3D[esi*4],NV_WIN mov npcRemark3D[esi*4],OFFSET npcRemarkWin3D jmp nv_result_done nv_play_loss: sub npcVisitChips3D[esi*4],edi jns @F mov npcVisitChips3D[esi*4],0 @@: cmp npcVisitMood3D[esi*4],-3 jle @F dec npcVisitMood3D[esi*4] @@: mov npcVisitActivity3D[esi*4],NV_LOSS mov npcRemark3D[esi*4],OFFSET npcRemarkLoss3D nv_result_done: inc npcVisitRounds3D[esi*4] inc npcVisitThirst3D[esi*4] mov npcLife3D[ebx].poseClock,0 mov npcLife3D[ebx].remaining,1800 mov npcRemarkTime3D[esi*4],1800 ret nv_after_result: cmp npcVisitMood3D[esi*4],-2 jle nv_take_break mov eax,npcVisitRounds3D[esi*4] cmp eax,npcVisitSession3D[esi*4] jae nv_take_break mov eax,npcVisitStake3D[esi*4] cmp eax,npcVisitChips3D[esi*4] ja nv_take_break mov npcVisitActivity3D[esi*4],NV_GAME mov npcLife3D[ebx].poseClock,0 invoke NpcVisitRandom3D,esi,1800 add eax,2200 mov npcLife3D[ebx].remaining,eax ret nv_take_break: mov npcRemark3D[esi*4],OFFSET npcRemarkBreak3D mov npcRemarkTime3D[esi*4],1600 invoke NpcChooseVisit3D,esi ret nv_order_ready: sub npcVisitChips3D[esi*4],20 jns @F mov npcVisitChips3D[esi*4],0 @@: mov npcVisitThirst3D[esi*4],0 mov npcVisitMood3D[esi*4],0 mov npcVisitActivity3D[esi*4],NV_DRINK mov npcLife3D[ebx].poseClock,0 mov npcLife3D[ebx].remaining,6500 ret nv_social: cmp npcVisitMood3D[esi*4],0 jge @F inc npcVisitMood3D[esi*4] @@: ; Only chat when another stationary visitor is actually nearby. invoke NpcEntity3D,esi mov edi,eax xor ecx,ecx nv_social_find: cmp ecx,esi je nv_social_next push ecx invoke NpcEntity3D,ecx mov edx,[edi].NpcMindEntity.posX sub edx,[eax].NpcMindEntity.posX jns @F neg edx @@: cmp edx,FP_1*5/2 ja nv_social_far mov edx,[edi].NpcMindEntity.posY sub edx,[eax].NpcMindEntity.posY jns @F neg edx @@: cmp edx,FP_1*5/2 ja nv_social_far pop ecx imul eax,ecx,SIZEOF NpcLife3D cmp npcLife3D[eax].state,NPC_WALK je nv_social_next cmp npcLife3D[eax].state,NPC_AWAY je nv_social_next mov npcVisitActivity3D[esi*4],NV_CHAT mov npcLife3D[ebx].poseClock,0 mov npcLife3D[ebx].remaining,6000 mov npcRemark3D[esi*4],OFFSET npcRemarkChat3D mov npcRemarkTime3D[esi*4],1800 ret nv_social_far: pop ecx nv_social_next: inc ecx cmp ecx,NPC_LIFE_COUNT jb nv_social_find invoke NpcChooseVisit3D,esi ret NpcAdvanceVisit3D ENDP NpcResolveVisitPose3D PROC uses ebx esi slot:DWORD mov esi,slot imul ebx,esi,SIZEOF NpcLife3D mov eax,npcLife3D[ebx].state cmp eax,NPC_SIT je nv_pose_seat cmp npcRise3D[esi*4],0 jne nv_pose_rising mov eax,npcVisitActivity3D[esi*4] cmp eax,NV_WIN je nv_pose_cheer cmp eax,NV_DRINK je nv_pose_drink cmp eax,NV_CHAT je nv_pose_talk cmp eax,NV_ORDER je nv_pose_talk mov eax,CAST_IDLE ret nv_pose_cheer: mov eax,CAST_CHEER ret nv_pose_drink: mov eax,CAST_DRINK ret nv_pose_talk: mov eax,CAST_TALK ret nv_pose_seat: invoke NpcSeatOffset3D,npcSitTime3D[esi*4] mov eax,CAST_SEAT cmp npcVisitActivity3D[esi*4],NV_CHAT jne @F mov eax,CAST_SEAT_TALK @@: ret nv_pose_rising: invoke NpcSeatOffset3D,npcRise3D[esi*4] mov eax,CAST_IDLE ret NpcResolveVisitPose3D ENDP ; Smoothstep into/out of the existing seat anchor. Activity clocks cannot replay it. NpcSeatOffset3D PROC uses ebx milliseconds:DWORD mov eax,milliseconds cmp eax,800 jb nv_seat_fraction mov eax,FP_1 jmp nv_seat_offset nv_seat_fraction: shl eax,16 xor edx,edx mov ecx,800 div ecx mov ebx,eax imul eax shrd eax,edx,16 mov ecx,3*FP_1 sub ecx,ebx sub ecx,ebx imul ecx shrd eax,edx,16 nv_seat_offset: mov ebx,eax mov ecx,FP_HALF imul ecx shrd eax,edx,16 sub npcSprite3D.posX,eax mov eax,ebx mov ecx,FP_1*35/100 imul ecx shrd eax,edx,16 sub npcSprite3D.posY,eax ret NpcSeatOffset3D ENDP NpcDrawRemark3D PROC uses ebx esi edi slot:DWORD local px:DWORD,py:DWORD,depth:DWORD,boxW:DWORD,textPtr:DWORD mov esi,slot cmp npcRemarkTime3D[esi*4],0 je nv_remark_done imul ebx,esi,SIZEOF NpcLife3D cmp npcLife3D[ebx].state,NPC_WALK je nv_remark_done invoke ProjectCityPoint3D,npcSprite3D.posX,npcSprite3D.posY cmp edx,FP_1/2 jle nv_remark_done cmp edx,5*FP_1 jg nv_remark_done cmp eax,24 jl nv_remark_done cmp eax,SCREEN_W-24 jg nv_remark_done cmp edx,zBuffer[eax*4] jg nv_remark_done mov depth,edx mov px,eax mov eax,viewEye3D sub eax,CAST_HEIGHT imul eax,SCREEN_H cdq idiv depth add eax,viewHorizon3D sar eax,1 sub eax,20 cmp eax,22 jl nv_remark_done cmp eax,194 jg nv_remark_done mov py,eax mov edi,npcRemark3D[esi*4] test edi,edi jz nv_remark_done mov textPtr,edi xor ecx,ecx nv_remark_length: cmp BYTE PTR [edi+ecx],0 je nv_remark_place inc ecx cmp ecx,24 jb nv_remark_length ret nv_remark_place: lea ebx,[ecx*8+12] mov boxW,ebx shr ebx,1 mov eax,px sar eax,1 sub eax,ebx cmp eax,4 jge @F mov eax,4 @@: mov ecx,316 sub ecx,boxW cmp eax,ecx jle @F mov eax,ecx @@: mov px,eax invoke DrawBoxBorder,px,py,boxW,18,00151D22h,00899599h,1 mov eax,px add eax,6 mov ecx,py add ecx,5 invoke DrawString8x8,eax,ecx,textPtr,00E8D7ADh nv_remark_done: ret NpcDrawRemark3D ENDP