LoadCharacterCast3D PROC cmp characterCast3D.pixels,0 jne cast_loaded invoke LoadAnimPack3D,OFFSET characterCast3D,OFFSET characterCastPath3D invoke ValidateCityPack3D,OFFSET characterCast3D,192,224,CAST_COUNT*CAST_STRIDE cast_loaded: ret LoadCharacterCast3D ENDP FreeCharacterCast3D PROC invoke FreeAnimPack3D,OFFSET characterCast3D ret FreeCharacterCast3D ENDP ; Identity chooses a fixed block. Action and camera-relative facing stay in it. ; Walking clocks are travelled distance, not wall time, so feet stop with the body. ConfigureCharacter3D PROC uses ebx esi edi object:DWORD,identity:DWORD,action:DWORD,clock:DWORD,velocityX:DWORD,velocityY:DWORD local base:DWORD,offsetFrame:DWORD,dot:DWORD,cross:DWORD,sideMagnitude:DWORD call LoadCharacterCast3D mov esi,object mov eax,identity xor edx,edx mov ecx,CAST_COUNT div ecx mov characterLastId3D,edx imul eax,edx,CAST_STRIDE mov base,eax mov eax,action cmp eax,CAST_WALK jbe @F xor eax,eax @@: mov characterLastAction3D,eax mov edi,eax mov eax,characterOffsets3D[eax*4] mov offsetFrame,eax cmp edi,CAST_WALK je cast_walk cmp edi,CAST_THROW je cast_throw cmp edi,CAST_SIT_DOWN je cast_transition cmp edi,CAST_STAND_UP je cast_transition cmp edi,CAST_PICK_UP je cast_pickup mov eax,clock xor edx,edx mov ecx,180 div ecx and eax,3 jmp cast_frame_ready cast_throw: mov ecx,100 mov ebx,23 jmp cast_clamped_time cast_pickup: mov ecx,112 mov ebx,7 jmp cast_clamped_time cast_transition: mov ecx,100 mov ebx,7 cast_clamped_time: mov eax,clock xor edx,edx div ecx cmp eax,ebx jbe @F mov eax,ebx @@: cmp edi,CAST_STAND_UP jne cast_frame_ready neg eax add eax,7 jmp cast_frame_ready cast_walk: mov eax,velocityX imul playerDirX shrd eax,edx,16 mov dot,eax mov eax,velocityY imul playerDirY shrd eax,edx,16 add dot,eax mov eax,velocityY imul playerDirX shrd eax,edx,16 mov cross,eax mov eax,velocityX imul playerDirY shrd eax,edx,16 sub cross,eax mov eax,cross cdq xor eax,edx sub eax,edx mov sideMagnitude,eax mov eax,dot cmp eax,sideMagnitude jg cast_walk_back neg eax cmp eax,sideMagnitude jg cast_walk_front mov offsetFrame,40 cmp cross,0 jge cast_walk_phase mov offsetFrame,48 jmp cast_walk_phase cast_walk_back: mov offsetFrame,32 jmp cast_walk_phase cast_walk_front: mov offsetFrame,24 cast_walk_phase: mov eax,clock xor edx,edx mov ecx,FP_1*3/32 div ecx and eax,7 cast_frame_ready: add eax,offsetFrame add eax,base mov characterLastFrame3D,eax mov [esi].CityProp3D.frame,eax mov [esi].CityProp3D.asset,OFFSET characterCast3D mov [esi].CityProp3D.worldW,CAST_WIDTH mov [esi].CityProp3D.worldH,CAST_HEIGHT mov [esi].CityProp3D.groundZ,-CAST_HEIGHT*4/224 ret ConfigureCharacter3D ENDP ConfigureRoofVisitor3D PROC uses ebx esi edi slot:DWORD local phase:DWORD,clock:DWORD,vx:DWORD,vy:DWORD mov esi,slot mov phase,CAST_TALK mov eax,roofGuestClock3D mov clock,eax shr eax,12 add eax,esi and eax,1 jz @F mov phase,CAST_DRINK @@: mov vx,0 mov vy,0 cmp roofGuestMoving3D[esi*4],0 je cast_roof_ready mov phase,CAST_WALK mov eax,roofGuestDistance3D[esi*4] mov clock,eax mov eax,roofGuestDir3D[esi*4] cmp eax,0 jne @F mov vx,FP_1 jmp cast_roof_ready @@: cmp eax,1 jne @F mov vx,-FP_1 jmp cast_roof_ready @@: mov vy,FP_1 cmp eax,2 je cast_roof_ready mov vy,-FP_1 cast_roof_ready: invoke ConfigureCharacter3D,OFFSET roofActor3D,roofGuestIdentity3D[esi*4],phase,clock,vx,vy ; The terrace uses a taller world scale than the hall; keep faces at eye level. mov eax,CAST_HEIGHT mov ecx,ROOF_STANDING_EYE imul ecx mov ecx,HALL_STANDING_EYE idiv ecx mov roofActor3D.worldH,eax imul eax,192 mov ecx,224 cdq idiv ecx mov roofActor3D.worldW,eax mov eax,roofActor3D.worldH neg eax cdq mov ecx,56 idiv ecx mov roofActor3D.groundZ,eax ret ConfigureRoofVisitor3D ENDP