; Scripted camera follows a real exterior map; simulation never consumes gameplay RNG. include src_graphics\city_traffic.inc include src_graphics\city_objects.inc include src_graphics\city_bins.inc include src_graphics\city_furniture.inc include src_graphics\city_skyline.inc include src_graphics\city_lighting.inc ValidateCityPack3D PROC uses esi pack:DWORD,widthPx:DWORD,heightPx:DWORD,count:DWORD mov esi, pack mov eax, widthPx cmp [esi].AnimPack3D.imageWidth, eax jne city_pack_reject mov eax, heightPx cmp [esi].AnimPack3D.height, eax jne city_pack_reject mov eax, count cmp [esi].AnimPack3D.frames, eax jne city_pack_reject ret city_pack_reject: invoke FreeAnimPack3D, esi ret ValidateCityPack3D ENDP LoadCityArrival3D PROC uses esi edi invoke LoadAnimPack3D, OFFSET cityTilesPack3D, OFFSET cityTilesPath3D invoke LoadAnimPack3D, OFFSET cityHandsPack3D, OFFSET cityHandsPath3D invoke LoadAnimPack3D, OFFSET cityPropsPack3D, OFFSET cityPropsPath3D invoke LoadAnimPack3D, OFFSET citySmokePack3D, OFFSET citySmokePath3D invoke LoadAnimPack3D, OFFSET cityCarsPack3D, OFFSET cityCarsPath3D invoke LoadAnimPack3D, OFFSET cityBinPack3D, OFFSET cityBinPath3D invoke LoadAnimPack3D, OFFSET cityCloudPack3D, OFFSET cityCloudPath3D invoke ValidateCityPack3D, OFFSET cityHandsPack3D, 256, 192, 16 invoke ValidateCityPack3D, OFFSET cityPropsPack3D, 256, 256, 6 invoke ValidateCityPack3D, OFFSET citySmokePack3D, 96, 128, 4 invoke ValidateCityPack3D, OFFSET cityCarsPack3D, 256, 256, 16 invoke ValidateCityPack3D, OFFSET cityBinPack3D, 128, 128, 4 invoke ValidateCityPack3D, OFFSET cityCloudPack3D, 192, 192, 4 call PrepareCityStreetCrops3D call BuildCityLighting3D cmp cityBinPack3D.pixels, 0 je @F mov esi, cityBinPack3D.pixels lea edi, texturesBuffer[73*128*128*4] mov ecx, 4*128*128 rep movsd @@: cmp cityTilesPack3D.pixels, 0 je city_load_done cmp cityTilesPack3D.imageWidth, 128 jne city_load_done cmp cityTilesPack3D.height, 128 jne city_load_done cmp cityTilesPack3D.frames, 16 jne city_load_done mov esi, cityTilesPack3D.pixels lea edi, texturesBuffer[57*128*128*4] mov ecx, 16*128*128 rep movsd city_load_done: ret LoadCityArrival3D ENDP HandleCityApproach3D PROC invoke Pressed3D, 0Dh test eax, eax jnz city_approach_complete mov eax, deltaTime3D add cityTime3D, eax city_approach_phase: mov eax, cityPhase3D mov ecx, cityDurations3D[eax*4] cmp cityTime3D, ecx jb city_approach_done sub cityTime3D, ecx cmp cityPhase3D, CITY_DOOR jae city_approach_complete inc cityPhase3D jmp city_approach_phase city_approach_complete: mov cityPhase3D, CITY_DOOR mov cityTime3D, 2800 mov arrivalState3D, ARRIVAL_STOP mov arrivalTime3D, 0 call ClearKeys3D city_approach_done: ret HandleCityApproach3D ENDP ; Hermite interpolation in 16.16: zero angular velocity at either end of a turn. CityEase3D PROC uses ebx timeMs:DWORD,durationMs:DWORD mov eax, timeMs cmp eax, durationMs jb @F mov eax, FP_1 ret @@: shl eax, 16 xor edx, edx div durationMs mov ebx, eax imul ebx shrd eax, edx, 16 shl ebx, 1 mov ecx, 3*FP_1 sub ecx, ebx imul ecx shrd eax, edx, 16 ret CityEase3D ENDP PositionCityCamera3D PROC uses ebx mov playerX, (8*FP_1+FP_HALF) mov playerY, (24*FP_1+FP_HALF) mov playerAngle, 128 mov viewHorizon3D, 240 cmp arrivalState3D, ARRIVAL_WALK jne city_camera_at_door mov ebx, cityTime3D cmp cityPhase3D, CITY_WAKE jne city_camera_turn_alley mov viewHorizon3D, 1600 cmp ebx, 1800 jb city_camera_done sub ebx, 1800 invoke CityEase3D, ebx, 1300 imul eax, 1360 shr eax, 16 sub viewHorizon3D, eax jmp city_camera_done city_camera_turn_alley: cmp cityPhase3D, CITY_TURN_ALLEY jb city_camera_done jne city_camera_alley invoke CityEase3D, ebx, 1200 shr eax, 8 add eax, 128 and eax, 511 mov playerAngle, eax jmp city_camera_done city_camera_alley: mov playerAngle, 384 cmp cityPhase3D, CITY_ALLEY jne city_camera_turn_street invoke CityEase3D, ebx, 5400 mov ecx, FP_1*243/20 imul ecx shrd eax, edx, 16 sub playerY, eax jmp city_camera_bob city_camera_turn_street: mov playerY, (12*FP_1+FP_1*7/20) cmp cityPhase3D, CITY_TURN_STREET jne city_camera_street invoke CityEase3D, ebx, 900 shr eax, 9 add eax, 384 and eax, 511 mov playerAngle, eax jmp city_camera_done city_camera_street: mov playerAngle, 0 cmp cityPhase3D, CITY_STREET jne city_camera_turn_door invoke CityEase3D, ebx, 7500 imul eax, 14 add playerX, eax jmp city_camera_bob city_camera_turn_door: mov playerX, (22*FP_1+FP_HALF) cmp cityPhase3D, CITY_TURN_DOOR jne city_camera_door_approach invoke CityEase3D, ebx, 900 shr eax, 9 neg eax and eax, 511 mov playerAngle, eax jmp city_camera_done city_camera_door_approach: mov playerAngle, 384 invoke CityEase3D, ebx, 2800 mov ecx, FP_1*37/20 imul ecx shrd eax, edx, 16 sub playerY, eax jmp city_camera_bob city_camera_at_door: mov playerX, (22*FP_1+FP_HALF) mov playerY, (10*FP_1+FP_HALF) mov playerAngle, 384 cmp arrivalState3D, ARRIVAL_ENTER jne city_camera_done invoke CityEase3D, arrivalTime3D, 1400 sub playerY, eax jmp city_camera_done city_camera_bob: mov eax, cityPhase3D mov eax, cityDurations3D[eax*4] sub eax, ebx cmp eax, ebx jbe @F mov eax, ebx @@: cmp eax, 500 jbe @F mov eax, 500 @@: mov ebx, eax mov eax, cityClock3D shr eax, 1 and eax, 511 mov eax, tableCos[eax*4] imul ebx mov ecx, 500*16384 idiv ecx add viewHorizon3D, eax city_camera_done: ret PositionCityCamera3D ENDP DrawCitySky3D PROC uses ebx esi edi local skyOffset:DWORD mov eax, cityClock3D shr eax, 8 mov ecx, playerAngle shr ecx, 2 add eax, ecx mov skyOffset, eax xor esi, esi lea edi, screenBuffer city_sky_row: mov eax, esi shr eax, 1 mov ebx, eax and ebx, 127 shl ebx, 7 xor ecx, ecx city_sky_pixel: mov eax, ecx shr eax, 1 add eax, skyOffset and eax, 255 cmp eax, 127 jbe @F xor eax, 255 @@: add eax, ebx mov eax, texturesBuffer[63*128*128*4+eax*4] ; Four identical destination pixels retain the exact 2x pixel-art sampling. mov [edi], eax mov [edi+4], eax mov [edi+640*4], eax mov [edi+640*4+4], eax add edi, 8 inc ecx cmp ecx, 320 jb city_sky_pixel inc esi add edi, 640*4 cmp esi, 240 jb city_sky_row ret DrawCitySky3D ENDP DrawCityRain3D PROC uses ebx esi xor esi, esi city_rain_drop: mov eax, esi imul eax, 137 mov ebx, cityClock3D shr ebx, 4 sub eax, ebx and eax, 511 imul eax, 5 shr eax, 3 mov ebx, eax mov eax, cityClock3D shr eax, 2 mov ecx, esi imul ecx, 83 add eax, ecx xor edx, edx mov ecx, 240 div ecx invoke DrawBox, ebx, edx, 1, 4, 00395468h inc esi cmp esi, 70 jb city_rain_drop ret DrawCityRain3D ENDP FadeCityFrame3D PROC uses ebx esi amount:DWORD mov ebx, amount cmp ebx, 256 jbe @F mov ebx, 256 @@: xor esi, esi city_fade_pixel: mov eax, screenBuffer[esi*4] movzx ecx, al imul ecx, ebx shr ecx, 8 movzx edx, ah imul edx, ebx shr edx, 8 shl edx, 8 or ecx, edx shr eax, 16 and eax, 255 imul eax, ebx shr eax, 8 shl eax, 16 or eax, ecx mov screenBuffer[esi*4], eax inc esi cmp esi, SCREEN_SIZE jb city_fade_pixel ret FadeCityFrame3D ENDP DrawCityCigarette3D PROC uses ebx esi px:DWORD,py:DWORD,angle:DWORD,pixelStep:DWORD local drawX:DWORD,drawY:DWORD,color:DWORD,u:DWORD,v:DWORD local lengthPx:DWORD,halfWidth:DWORD,along:DWORD mov esi, angle and esi, 511 mov eax, pixelStep shl eax, 4 mov lengthPx, eax mov eax, pixelStep mov halfWidth, eax mov u, 0 city_cigarette_column: mov eax, halfWidth neg eax mov v, eax city_cigarette_pixel: mov eax, v test eax, eax jns @F neg eax @@: cmp eax, halfWidth jne city_cigarette_solid_pixel cmp u, 2 jb city_cigarette_next_pixel mov eax, lengthPx sub eax, 2 cmp u, eax jae city_cigarette_next_pixel city_cigarette_solid_pixel: mov eax, pixelStep shl eax, 1 mov ebx, u sub ebx, eax mov along, ebx mov eax, ebx imul tableCos[esi*4] shrd eax, edx, 16 mov drawX, eax mov eax, v imul tableSin[esi*4] shrd eax, edx, 16 sub drawX, eax mov eax, drawX add eax, px sar eax, 1 mov drawX, eax mov eax, along imul tableSin[esi*4] shrd eax, edx, 16 mov drawY, eax mov eax, v imul tableCos[esi*4] shrd eax, edx, 16 add eax, drawY add eax, py sar eax, 1 mov drawY, eax mov color, 00DDD9C9h mov eax, pixelStep shl eax, 2 cmp u, eax jae city_cigarette_ash mov color, 00B0783Ch jmp city_cigarette_shade city_cigarette_ash: mov eax, lengthPx mov ecx, pixelStep shl ecx, 1 sub eax, ecx cmp u, eax jb city_cigarette_shade mov color, 00746E67h add eax, pixelStep cmp u, eax jb city_cigarette_shade mov color, 00EC6325h jmp city_cigarette_draw city_cigarette_shade: mov eax, v test eax, eax jns @F mov eax, color and eax, 00FEFEFEh shr eax, 1 add eax, 00484840h mov color, eax jmp city_cigarette_draw @@: cmp eax, halfWidth jl city_cigarette_draw mov eax, color and eax, 00FEFEFEh shr eax, 1 mov color, eax city_cigarette_draw: invoke DrawBox, drawX, drawY, 1, 1, color city_cigarette_next_pixel: inc v mov eax, v cmp eax, halfWidth jle city_cigarette_pixel inc u mov eax, u cmp eax, lengthPx jb city_cigarette_column ret DrawCityCigarette3D ENDP DrawCityCigaretteSmoke3D PROC uses ebx esi edi tipX:DWORD,tipY:DWORD local left:DWORD,top:DWORD mov eax, cityClock3D xor edx, edx mov ecx, 180 div ecx and eax, 3 mov esi, eax mov eax, citySmokeRootX3D[esi*4] imul eax, 180 xor edx, edx mov ecx, 96 div ecx mov ebx, tipX sub ebx, eax mov left, ebx mov eax, citySmokeRootY3D[esi*4] imul eax, 280 xor edx, edx mov ecx, 128 div ecx mov edi, tipY sub edi, eax mov top, edi mov animOpacity3D, 245 invoke DrawAnimSprite3D, OFFSET citySmokePack3D, esi, left, top, 180, 280 mov ebx, tipX sub ebx, 64 mov edi, tipY sub edi, 290 mov animOpacity3D, 105 invoke DrawAnimSprite3D, OFFSET cityCloudPack3D, esi, ebx, edi, 128, 180 mov animOpacity3D, 255 ret DrawCityCigaretteSmoke3D ENDP DrawCitySmokingHand3D PROC uses ebx esi edi local pose:DWORD,pinchX:DWORD,pinchY:DWORD,offsetY:DWORD,part:AnimPack3D local anchorX:DWORD,anchorY:DWORD cmp cityHandsPack3D.pixels, 0 je city_hand_done mov pose, 5 mov pinchX, 500 mov pinchY, 310 mov offsetY, 0 cmp cityPhase3D, CITY_WAKE jne city_hand_toss mov eax, 3100 sub eax, cityTime3D imul eax, 480 xor edx, edx mov ecx, 1300 div ecx mov offsetY, eax city_hand_toss: cmp cityPhase3D, CITY_TOSS jne city_hand_breath mov eax, cityTime3D cmp eax, 750 jae city_hand_release_pose imul eax, 3 xor edx, edx mov ecx, 750 div ecx add eax, 5 jmp city_hand_store_pose city_hand_release_pose: mov eax, 8 cmp cityTime3D, 900 jb city_hand_store_pose mov eax, cityTime3D sub eax, 900 imul eax, 7 xor edx, edx mov ecx, 600 div ecx add eax, 9 city_hand_store_pose: cmp eax, 15 jbe @F mov eax, 15 @@: mov pose, eax invoke CityEase3D, cityTime3D, 750 mov ebx, eax imul eax, 80 shr eax, 16 sub pinchX, eax mov eax, ebx imul eax, 30 shr eax, 16 sub pinchY, eax cmp cityTime3D, 1000 jb city_hand_position mov eax, cityTime3D sub eax, 1000 invoke CityEase3D, eax, 500 imul eax, 300 shr eax, 16 add offsetY, eax jmp city_hand_position city_hand_breath: mov eax, cityClock3D shr eax, 3 and eax, 511 mov eax, tableCos[eax*4] sar eax, 14 add pinchY, eax city_hand_position: mov ebx, pose mov eax, cityFingerX3D[ebx*4] mov anchorX, eax mov eax, cityFingerY3D[ebx*4] mov anchorY, eax cmp cityPhase3D, CITY_TOSS jne city_hand_project_anchor cmp cityTime3D, 750 jb city_hand_project_anchor cmp cityTime3D, 900 jae city_hand_project_anchor ; Preserve the grip location while the fingers first open, instead of jumping the wrist. mov eax, cityTime3D sub eax, 750 invoke CityEase3D, eax, 150 mov edi, eax imul eax, 10 shr eax, 16 neg eax add eax, 110 mov anchorX, eax mov eax, edi imul eax, 42 shr eax, 16 add eax, 27 mov anchorY, eax city_hand_project_anchor: mov eax, anchorX imul eax, 360 xor edx, edx mov ecx, 256 div ecx mov edx, pinchX sub edx, eax mov cityHandX3D, edx mov eax, anchorY imul eax, 270 xor edx, edx mov ecx, 192 div ecx mov edx, pinchY sub edx, eax add edx, offsetY mov cityHandY3D, edx cmp cityPhase3D, CITY_TOSS jne city_hand_draw_held cmp cityTime3D, 750 jae city_hand_draw_sprite city_hand_draw_held: mov eax, pinchX mov cityCigaretteX3D, eax mov eax, pinchY add eax, offsetY mov cityCigaretteY3D, eax mov eax, tableCos[320*4] imul eax, 55 sar eax, 16 add eax, cityCigaretteX3D mov ebx, eax mov eax, tableSin[320*4] imul eax, 55 sar eax, 16 add eax, cityCigaretteY3D invoke DrawCityCigaretteSmoke3D, ebx, eax invoke DrawCityCigarette3D, cityCigaretteX3D, cityCigaretteY3D, 320, 4 city_hand_draw_sprite: ; The opaque fingers cover the filter, with the paper extending out of the grip. invoke DrawAnimSprite3D, OFFSET cityHandsPack3D, pose, cityHandX3D, cityHandY3D, 360, 270 ; Continue only the sleeve's bottom rows, never a translucent full-screen matte. mov eax, cityHandY3D add eax, 270 cmp eax, 480 jae city_hand_cigarette mov edi, eax mov esi, 480 sub esi, eax mov part.imageWidth, 256 mov part.height, 8 mov part.frames, 1 mov eax, pose imul eax, 256*192*4 add eax, cityHandsPack3D.pixels add eax, 256*184*4 mov part.pixels, eax invoke DrawAnimSprite3D, ADDR part, 0, cityHandX3D, edi, 360, esi city_hand_cigarette: cmp cityPhase3D, CITY_TOSS jne city_hand_done cmp cityTime3D, 750 jae city_hand_flight ret city_hand_flight: cmp cityTime3D, 1200 jae city_hand_done ; Independent ballistic screen-space arc terminates inside the projected bin mouth. mov eax, cityTime3D sub eax, 750 shl eax, 16 xor edx, edx mov ecx, 450 div ecx mov ebx, eax mov eax, cityMouthX3D sub eax, 420 imul ebx shrd eax, edx, 16 add eax, 420 mov cityCigaretteX3D, eax mov eax, cityMouthY3D sub eax, 280 imul ebx shrd eax, edx, 16 add eax, 280 mov cityCigaretteY3D, eax mov eax, FP_1 sub eax, ebx imul ebx shrd eax, edx, 16 imul eax, 180 shr eax, 16 sub cityCigaretteY3D, eax mov eax, cityTime3D sub eax, 750 imul eax, 2 add eax, 320 mov ebx, 4 cmp cityTime3D, 900 jb @F dec ebx cmp cityTime3D, 1050 jb @F dec ebx cmp cityTime3D, 1125 jb @F dec ebx @@: invoke DrawCityCigarette3D, cityCigaretteX3D, cityCigaretteY3D, eax, ebx call DrawCityBinBodies3D city_hand_done: ret DrawCitySmokingHand3D ENDP DrawCityExhaledSmoke3D PROC uses ebx esi edi mov esi, cityTime3D cmp esi, 2300 jae city_exhale_done mov eax, esi xor edx, edx mov ecx, 600 div ecx cmp eax, 3 jbe @F mov eax, 3 @@: mov ebx, eax mov animOpacity3D, 155 cmp esi, 300 jae city_exhale_fade mov eax, esi imul eax, 155 xor edx, edx mov ecx, 300 div ecx mov animOpacity3D, eax city_exhale_fade: cmp esi, 1500 jb city_exhale_draw mov eax, 2300 sub eax, esi imul eax, 155 xor edx, edx mov ecx, 800 div ecx mov animOpacity3D, eax city_exhale_draw: mov eax, esi xor edx, edx mov ecx, 7 div ecx mov edi, 285 sub edi, eax invoke DrawAnimSprite3D, OFFSET cityCloudPack3D, ebx, 175, edi, 360, 400 mov eax, esi xor edx, edx mov ecx, 6 div ecx mov edi, 355 sub edi, eax invoke DrawAnimSprite3D, OFFSET cityCloudPack3D, ebx, 335, edi, 250, 310 mov animOpacity3D, 255 city_exhale_done: ret DrawCityExhaledSmoke3D ENDP RenderCityArrival3D PROC uses ebx esi edi local savedX:DWORD,savedY:DWORD,savedAngle:DWORD,savedMap:DWORD local savedHorizon:DWORD,savedFloor:DWORD,savedOutdoor:DWORD,savedEye:DWORD,bar:DWORD cmp arrivalState3D, ARRIVAL_WALK jne @F cmp cityPhase3D, CITY_BLACK jne @F invoke DrawBox, 0, 0, 320, 240, 0 ret @@: mov eax, playerX mov savedX, eax mov eax, playerY mov savedY, eax mov eax, playerAngle mov savedAngle, eax mov eax, worldMap3D mov savedMap, eax mov eax, viewHorizon3D mov savedHorizon, eax mov eax, floorTexture3D mov savedFloor, eax mov eax, outdoorRender3D mov savedOutdoor, eax mov eax, viewEye3D mov savedEye, eax call PositionCityCamera3D mov worldMap3D, OFFSET cityMap3D mov floorTexture3D, 60 mov outdoorRender3D, 1 mov viewEye3D, FP_1*17/20 call UpdateCamera3D call DrawCitySky3D cmp arrivalState3D, ARRIVAL_WALK jne city_render_world cmp cityPhase3D, CITY_WAKE jne city_render_world cmp cityTime3D, 1800 jb city_render_sky_smoke city_render_world: call RenderCeilingAndFloor call DrawCitySkyline3D call RenderWalls call PrepareCityDepth3D call RenderCityObjects3D call RenderCityFurniture3D call RenderCityBins3D call RenderCityFacade3D call RenderCityGuards3D call DrawCityRain3D cmp arrivalState3D, ARRIVAL_WALK jne city_render_restore cmp cityPhase3D, CITY_TOSS ja city_render_restore cmp cityPhase3D, CITY_WAKE jne @F call DrawCityExhaledSmoke3D @@: call DrawCitySmokingHand3D cmp cityPhase3D, CITY_WAKE jne city_render_restore jmp city_wake_eye_mask city_render_sky_smoke: call DrawCityRain3D call DrawCityExhaledSmoke3D city_wake_eye_mask: mov eax, cityTime3D cmp eax, 900 jae city_wake_blink imul eax, 240 xor edx, edx mov ecx, 900 div ecx neg eax add eax, 240 jmp city_wake_bars city_wake_blink: cmp eax, 1120 jae city_wake_reopen sub eax, 900 imul eax, 120 xor edx, edx mov ecx, 220 div ecx jmp city_wake_bars city_wake_reopen: cmp eax, 1380 jae city_render_restore sub eax, 1120 imul eax, 120 xor edx, edx mov ecx, 260 div ecx neg eax add eax, 120 city_wake_bars: shr eax, 1 mov bar, eax invoke DrawBox, 0, 0, 320, bar, 0 mov eax, 240 sub eax, bar invoke DrawBox, 0, eax, 320, bar, 0 city_render_restore: mov eax, savedX mov playerX, eax mov eax, savedY mov playerY, eax mov eax, savedAngle mov playerAngle, eax mov eax, savedMap mov worldMap3D, eax mov eax, savedHorizon mov viewHorizon3D, eax mov eax, savedFloor mov floorTexture3D, eax mov eax, savedOutdoor mov outdoorRender3D, eax mov eax, savedEye mov viewEye3D, eax call UpdateCamera3D ret RenderCityArrival3D ENDP RenderArrival3D PROC uses ebx esi edi local guardFrame:DWORD,guardW:DWORD,guardH:DWORD,guardY:DWORD,leftX:DWORD,rightX:DWORD call RenderCityArrival3D cmp arrivalState3D, ARRIVAL_PROFILE jne city_arrival_name call DrawEntryProfileRows3D ret city_arrival_name: cmp arrivalState3D, ARRIVAL_NAME jne city_arrival_fade invoke DrawBoxBorder, 172, 65, 140, 117, 0017191Ch, 00BD985Ah, 1 invoke DrawString8x8, 180, 74, OFFSET entryNameText3D, 00F0D7ABh invoke DrawBoxBorder, 177, 92, 130, 22, 002D3934h, 00B6D9B4h, 1 invoke DrawString8x8, 180, 99, OFFSET arrivalName3D, 00FFFFFFh invoke DrawString8x8, 180, 124, OFFSET entryNameHint3D, 00BCC6BAh invoke DrawString8x8, 180, 161, OFFSET entryCancel3D, 00BCC6BAh invoke DrawString8x8, 263, 161, OFFSET entryConfirm3D, 00F0D7ABh cmp arrivalError3D, 0 je city_arrival_done invoke DrawString8x8, 180, 143, OFFSET entryNameError3D, 00EC8875h ret city_arrival_fade: cmp arrivalState3D, ARRIVAL_ENTER jne city_arrival_done cmp arrivalTime3D, 1000 jb city_arrival_done mov eax, arrivalTime3D cmp eax, 1400 jbe @F mov eax, 1400 @@: sub eax, 1000 shl eax, 8 xor edx, edx mov ecx, 400 div ecx neg eax add eax, 256 invoke FadeCityFrame3D, eax city_arrival_done: ret RenderArrival3D ENDP