; ============================================================================== ; JACKBOX / JUKEBOX IMPLEMENTATION - MASM32 / CP1251 ; ============================================================================== .code LoadJackboxArt3D PROC invoke LoadAnimPack3D, OFFSET jackboxWorldArt3D, OFFSET jackboxWorldPath3D invoke ValidateCityPack3D, OFFSET jackboxWorldArt3D, 256, 512, 3 invoke LoadAnimPack3D, OFFSET jackboxScreenArt3D, OFFSET jackboxScreenPath3D invoke ValidateCityPack3D, OFFSET jackboxScreenArt3D, 640, 480, 1 ret LoadJackboxArt3D ENDP FreeJackboxArt3D PROC invoke FreeAnimPack3D, OFFSET jackboxWorldArt3D invoke FreeAnimPack3D, OFFSET jackboxScreenArt3D ret FreeJackboxArt3D ENDP ResolveJackboxSurface3D PROC material:DWORD cmp jackboxWorldArt3D.pixels, 0 je jackbox_surface_missing mov eax, material cmp eax, MATERIAL_JACKBOX_FRONT je jackbox_surface_front cmp eax, MATERIAL_JACKBOX_SIDE je jackbox_surface_side cmp eax, MATERIAL_JACKBOX_BACK jne jackbox_surface_missing ; Frame 2: rear panel (offset 256*512*4*2 = 1048576) mov eax, jackboxWorldArt3D.pixels add eax, 1048576 mov ecx, 256 mov edx, 512 ret jackbox_surface_side: ; Frame 1: side profile (offset 256*512*4 = 524288) mov eax, jackboxWorldArt3D.pixels add eax, 524288 mov ecx, 256 mov edx, 512 ret jackbox_surface_front: ; Frame 0: front face mov eax, jackboxWorldArt3D.pixels mov ecx, 256 mov edx, 512 ret jackbox_surface_missing: xor eax, eax ret ResolveJackboxSurface3D ENDP DrawJackboxCabinet3D PROC uses ebx esi edi wx:DWORD, wy:DWORD local x0:DWORD, x1:DWORD, y0:DWORD, y1:DWORD ; Solid rectangular cabinet against East wall beside billiards: ; Depth along X: 0.55m (x0 = 30.40m, x1 = 30.95m against wall at 31.0m) ; Width along Y: 0.85m (y0 = 23.075m, y1 = 23.925m, centered at Y = 23.5m) ; Leaves painting 116 at Y = [25.0 .. 26.5] completely visible and unblocked! ; Height Z: 1.85m mov x0, FP_1*304/10 mov x1, FP_1*3095/100 mov y0, FP_1*235/10 - FP_1*425/1000 mov y1, FP_1*235/10 + FP_1*425/1000 ; 1. Back Face (against East wall: X = x1) invoke DrawCityVertical3D, 0, x1, y0, y1, 0, FP_1*37/20, MATERIAL_JACKBOX_BACK ; 2. North Side (Y = y0) invoke DrawCityVertical3D, 1, y0, x0, x1, 0, FP_1*37/20, MATERIAL_JACKBOX_SIDE ; 3. South Side (Y = y1) invoke DrawCityVertical3D, 1, y1, x0, x1, 0, FP_1*37/20, MATERIAL_JACKBOX_SIDE ; 4. Front Face (facing West towards billiards: X = x0) invoke DrawCityVertical3D, 0, x0, y0, y1, 0, FP_1*37/20, MATERIAL_JACKBOX_FRONT ; 5. Top Lid (horizontal cap at z = 1.85m) invoke DrawCityHorizontal3D, x0, x1, y0, y1, FP_1*37/20, 110 ret DrawJackboxCabinet3D ENDP RenderJackboxCabinet3D PROC invoke DrawJackboxCabinet3D, JACKBOX_X, JACKBOX_Y ret RenderJackboxCabinet3D ENDP JackboxCanWalk3D PROC px:DWORD, py:DWORD mov eax, px cmp eax, FP_1*303/10 jb jackbox_walk_clear cmp eax, 31*FP_1+9830 ja jackbox_walk_clear mov eax, py sub eax, FP_1*235/10 test eax, eax jns @F neg eax @@: cmp eax, FP_1*55/100 ja jackbox_walk_clear xor eax, eax ret jackbox_walk_clear: mov eax, 1 ret JackboxCanWalk3D ENDP PlayJukeboxTrack3D PROC uses ebx trackIndex:DWORD invoke mciSendStringA, OFFSET jboxStopCmd, 0, 0, 0 invoke mciSendStringA, OFFSET jboxCloseCmd, 0, 0, 0 mov jboxIsPlaying, 0 mov ebx, trackIndex cmp ebx, 9 jae jbox_play_done cmp ebx, JACKBOX_TRACK_COUNT jae jbox_play_done mov eax, jboxOpenCommands[ebx*4] test eax, eax jz jbox_play_done invoke mciSendStringA, eax, 0, 0, 0 invoke mciSendStringA, OFFSET jboxPlayCmd, 0, 0, 0 mov jboxCurrentTrack, ebx mov jboxIsPlaying, 1 jbox_play_done: ret PlayJukeboxTrack3D ENDP StopJukeboxTrack3D PROC invoke mciSendStringA, OFFSET jboxStopCmd, 0, 0, 0 invoke mciSendStringA, OFFSET jboxCloseCmd, 0, 0, 0 mov jboxIsPlaying, 0 mov jboxCurrentTrack, -1 ret StopJukeboxTrack3D ENDP BeginJackbox3D PROC mov engineMode, MODE_JACKBOX call ClearKeys3D mov mouseClick3D, 0 ret BeginJackbox3D ENDP HandleJackbox3D PROC uses ebx esi ; 1. Mouse interactions cmp mouseClick3D, 0 je jbox_keys ; Exit via Top-Right LCD slot: x in [232..284], y in [12..26] cmp mouseY3D, 12 jb @F cmp mouseY3D, 26 ja @F cmp mouseX3D, 232 jb @F cmp mouseX3D, 284 jbe jbox_exit @@: ; Exit via Bottom Red CANCEL button: x in [224..256], y in [195..225] cmp mouseY3D, 195 jb @F cmp mouseY3D, 225 ja @F cmp mouseX3D, 224 jb @F cmp mouseX3D, 256 jbe jbox_exit @@: ; Bottom Green START button: x in [154..188], y in [195..225] -> Play selected cmp mouseY3D, 195 jb @F cmp mouseY3D, 225 ja @F cmp mouseX3D, 154 jb @F cmp mouseX3D, 188 jbe jbox_play_selected @@: ; Bottom Orange SELECT button: x in [190..222], y in [195..225] -> Next track & play cmp mouseY3D, 195 jb @F cmp mouseY3D, 225 ja @F cmp mouseX3D, 190 jb @F cmp mouseX3D, 222 ja @F inc jboxSelectedTrack cmp jboxSelectedTrack, JACKBOX_TRACK_COUNT jb jbox_play_selected mov jboxSelectedTrack, 0 jmp jbox_play_selected @@: ; Bottom Left Arrow: x in [256..274], y in [204..224] -> Prev track cmp mouseY3D, 204 jb @F cmp mouseY3D, 224 ja @F cmp mouseX3D, 256 jb @F cmp mouseX3D, 274 ja @F dec jboxSelectedTrack jns jbox_play_selected mov jboxSelectedTrack, JACKBOX_TRACK_COUNT - 1 jmp jbox_play_selected @@: ; Bottom Right Arrow: x in [276..294], y in [204..224] -> Next track cmp mouseY3D, 204 jb @F cmp mouseY3D, 224 ja @F cmp mouseX3D, 276 jb @F cmp mouseX3D, 294 ja @F inc jboxSelectedTrack cmp jboxSelectedTrack, JACKBOX_TRACK_COUNT jb jbox_play_selected mov jboxSelectedTrack, 0 jmp jbox_play_selected @@: ; Check track row click inside CRT screen: x in [46..274], y in [65..165] cmp mouseY3D, 65 jb jbox_keys cmp mouseY3D, 165 ja jbox_keys cmp mouseX3D, 46 jb jbox_keys cmp mouseX3D, 274 ja jbox_keys mov eax, mouseY3D sub eax, 65 xor edx, edx mov ebx, 10 div ebx cmp eax, JACKBOX_TRACK_COUNT jae jbox_keys mov jboxSelectedTrack, eax invoke PlayJukeboxTrack3D, eax jmp jbox_handled jbox_keys: ; Exit keys: ESC (1Bh) or 'E' (45h) invoke Pressed3D, 1Bh test eax, eax jnz jbox_exit invoke Pressed3D, 45h test eax, eax jnz jbox_exit ; Navigation: UP (26h) / DOWN (28h) invoke Pressed3D, 26h test eax, eax jz @F dec jboxSelectedTrack jns jbox_handled mov jboxSelectedTrack, JACKBOX_TRACK_COUNT - 1 jmp jbox_handled @@: invoke Pressed3D, 28h test eax, eax jz @F inc jboxSelectedTrack cmp jboxSelectedTrack, JACKBOX_TRACK_COUNT jb jbox_handled mov jboxSelectedTrack, 0 jmp jbox_handled @@: ; Play selection: ENTER (0Dh) or SPACE (20h) invoke Pressed3D, 0Dh test eax, eax jnz jbox_play_selected invoke Pressed3D, 20h test eax, eax jnz jbox_play_selected ; Stop key: 'S' (53h) invoke Pressed3D, 53h test eax, eax jz @F call StopJukeboxTrack3D jmp jbox_handled @@: ; Random key: 'R' (52h) invoke Pressed3D, 52h test eax, eax jz @F invoke RandomRange3D, 9 mov jboxSelectedTrack, eax invoke PlayJukeboxTrack3D, eax jmp jbox_handled @@: ; Direct digit keys '1'..'9' mov ebx, 31h ; '1' jbox_digit_loop: invoke Pressed3D, ebx test eax, eax jz jbox_digit_next mov eax, ebx sub eax, 31h mov jboxSelectedTrack, eax invoke PlayJukeboxTrack3D, eax jmp jbox_handled jbox_digit_next: inc ebx cmp ebx, 39h ; '9' jbe jbox_digit_loop ; '0' key for mute/stop invoke Pressed3D, 30h test eax, eax jz jbox_handled mov jboxSelectedTrack, 9 invoke PlayJukeboxTrack3D, 9 jmp jbox_handled jbox_play_selected: invoke PlayJukeboxTrack3D, jboxSelectedTrack jmp jbox_handled jbox_exit: mov engineMode, MODE_WALK call ClearKeys3D jbox_handled: mov mouseClick3D, 0 ret HandleJackbox3D ENDP RenderJackbox3D PROC uses ebx esi edi local rowY:DWORD, color:DWORD ; 1. Draw base background invoke DrawBox, 0, 0, 320, 240, 000B1815h ; 2. Overlay screen artwork (640x480 physical buffer) cmp jackboxScreenArt3D.pixels, 0 je @F invoke DrawAnimSprite3D, OFFSET jackboxScreenArt3D, 0, 0, 0, 640, 480 @@: ; 3. Top Digital LCD Slots ; Left slot: status text cmp jboxIsPlaying, 1 jne jbox_top_lcd_stopped cmp jboxCurrentTrack, -1 je jbox_top_lcd_stopped cmp jboxCurrentTrack, 9 jae jbox_top_lcd_stopped invoke DrawString8x8, 42, 17, OFFSET jboxLcdPlay, 0050FF90h ; Green "ÈÃÐÀÅÒ" jmp jbox_top_lcd_exit jbox_top_lcd_stopped: invoke DrawString8x8, 45, 17, OFFSET jboxLcdStop, 008CB8A5h ; Dim green "ÑÒÎÏ" jbox_top_lcd_exit: ; Right slot: exit button text invoke DrawString8x8, 241, 17, OFFSET jboxLcdExit, 00F2A648h ; Amber "ÂÛÕÎÄ" ; 4. CRT Monitor Header: Now Playing Status cmp jboxIsPlaying, 1 jne jbox_render_stopped cmp jboxCurrentTrack, -1 je jbox_render_stopped cmp jboxCurrentTrack, 9 jae jbox_render_stopped invoke DrawString8x8, 48, 52, OFFSET jboxHdrNow, 0050FF90h ; Green "ÑÅÉ×ÀÑ:" mov eax, jboxCurrentTrack mov edx, jboxTrackNames[eax*4] invoke DrawString8x8, 108, 52, edx, 00F0E6C3h jmp jbox_render_divider jbox_render_stopped: invoke DrawString8x8, 48, 52, OFFSET jboxHdrStop, 008CB8A5h ; Dim green "ÑÒÀÒÓÑ: ÎÑÒÀÍÎÂËÅÍ" jbox_render_divider: invoke DrawBox, 46, 62, 228, 1, 001C3828h ; 5. Track Selection List (10 rows, step Y = 10, from Y = 66) mov rowY, 66 xor esi, esi jbox_render_row: mov color, 008CB8A5h ; Default CRT phosphor pale cyan cmp esi, jboxSelectedTrack jne @F ; Selected row: highlight box, golden cursor, bright white text invoke DrawBox, 46, rowY, 228, 9, 00143526h invoke DrawString8x8, 48, rowY, OFFSET jboxCursor, 00FFD700h mov color, 00FFFFFFh @@: mov edx, jboxTrackNames[esi*4] invoke DrawString8x8, 58, rowY, edx, color ; Mark playing track with [>] tag cmp esi, jboxCurrentTrack jne jbox_row_next cmp jboxIsPlaying, 1 jne jbox_row_next invoke DrawString8x8, 238, rowY, OFFSET jboxTagPlay, 0050FF90h jbox_row_next: add rowY, 10 inc esi cmp esi, JACKBOX_TRACK_COUNT jb jbox_render_row ; 6. Footer Controls Help (strictly inside CRT) invoke DrawBox, 46, 166, 228, 1, 001C3828h invoke DrawString8x8, 56, 170, OFFSET jboxFooterHelp, 0070AA90h ret RenderJackbox3D ENDP