EBLAN_ENGINE/main.asm
Artvasil09 a1d1baa869 feat: complete engine and editor subsystems (Stages 1-5 from TODO_ARTEM)
- Stage 1: Eliminate fish-eye distortion with precomputed tangential ray offset table and fractional DDA depth
- Stage 2: Full asset management in EblanStudio (stb_image/stb_image_write, 128x128 BGRA texture packaging, WAV sound manager)
- Stage 3: Multi-level room streaming, LevelTransition/CELL_PORTAL trigger system, 16-frame screen fade to black
- Stage 4: 60Hz cutscene engine in pure x86 ASM (v4 binary track, waypoint camera motion, shortest-arc pan, CP1251 Russian subtitles, letterbox overlay, audio)
- Stage 5: EblanStudio cutscene timeline (F6) and windowless Live ASM Viewport over shared-memory IPC directly rendered into DX11
- Tooling & Tests: comprehensive regression test suite (94 editor + 107 engine tests), IPC tests, and cutscene demo
2026-10-22 16:24:22 +03:00

464 lines
13 KiB
NASM

; ==============================================================================
; EBLAN ENGINE 3D - Standalone Retro Raycaster Engine
; Pure x86 Assembly (MASM32) | Software 2.5D DIBits Raycaster
; ==============================================================================
.386
.model flat, stdcall
option casemap:none
; Standard Windows Libraries
includelib kernel32.lib
includelib user32.lib
includelib gdi32.lib
includelib winmm.lib
; Windows API Prototypes
ExitProcess PROTO :DWORD
GetModuleHandleA PROTO :DWORD
GetCommandLineA PROTO
GetFileSize PROTO :DWORD, :DWORD
GetForegroundWindow PROTO
RegisterClassExA PROTO :DWORD
CreateWindowExA PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
ShowWindow PROTO :DWORD, :DWORD
UpdateWindow PROTO :DWORD
GetDC PROTO :DWORD
ReleaseDC PROTO :DWORD, :DWORD
DefWindowProcA PROTO :DWORD, :DWORD, :DWORD, :DWORD
PeekMessageA PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
TranslateMessage PROTO :DWORD
DispatchMessageA PROTO :DWORD
PostQuitMessage PROTO :DWORD
StretchDIBits PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
GetAsyncKeyState PROTO :DWORD
GetTickCount PROTO
Sleep PROTO :DWORD
timeBeginPeriod PROTO :DWORD
timeEndPeriod PROTO :DWORD
AdjustWindowRect PROTO :DWORD, :DWORD, :DWORD
LoadCursorA PROTO :DWORD, :DWORD
CreateFileA PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
ReadFile PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
WriteFile PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
CloseHandle PROTO :DWORD
PlaySoundA PROTO :DWORD, :DWORD, :DWORD
OpenFileMappingA PROTO :DWORD, :DWORD, :DWORD
MapViewOfFile PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
UnmapViewOfFile PROTO :DWORD
OpenProcess PROTO :DWORD, :DWORD, :DWORD
WaitForSingleObject PROTO :DWORD, :DWORD
; Structures
POINT STRUCT
x dd 0
y dd 0
POINT ENDS
MSG STRUCT
hwnd dd 0
message dd 0
wParam dd 0
lParam dd 0
time dd 0
pt POINT <>
MSG ENDS
RECT STRUCT
left dd 0
top dd 0
right dd 0
bottom dd 0
RECT ENDS
WNDCLASSEXA STRUCT
cbSize dd 0
style dd 0
lpfnWndProc dd 0
cbClsExtra dd 0
cbWndExtra dd 0
hInstance dd 0
hIcon dd 0
hCursor dd 0
hbrBackground dd 0
lpszMenuName dd 0
lpszClassName dd 0
hIconSm dd 0
WNDCLASSEXA ENDS
BITMAPINFOHEADER STRUCT
biSize dd 0
biWidth dd 0
biHeight dd 0
biPlanes dw 0
biBitCount dw 0
biCompression dd 0
biSizeImage dd 0
biXPelsPerMeter dd 0
biYPelsPerMeter dd 0
biClrUsed dd 0
biClrImportant dd 0
BITMAPINFOHEADER ENDS
; ------------------------------------------------------------------------------
; Engine Includes
; ------------------------------------------------------------------------------
include include/engine_defs.inc
include include/font8x8.inc
include include/engine_data.inc
include include/engine_math.inc
include include/engine_map.inc
include include/engine_textures.inc
include include/engine_input.inc
include include/engine_physics.inc
include include/engine_raycaster.inc
include include/engine_render2d.inc
include include/engine_cutscene.inc
include include/engine_preview.inc
.code
; ------------------------------------------------------------------------------
; Window Procedure Callback
; ------------------------------------------------------------------------------
WndProc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
cmp uMsg, 0002h ; WM_DESTROY
je handle_destroy
cmp uMsg, 0100h ; WM_KEYDOWN
je handle_keydown
invoke DefWindowProcA, hWnd, uMsg, wParam, lParam
ret
handle_keydown:
cmp wParam, VK_KEY_ESCAPE
jne check_f12
invoke PostQuitMessage, 0
mov isRunning, 0
xor eax, eax
ret
check_f12:
; Movement and action edges are handled only by PollInput.
invoke DefWindowProcA, hWnd, uMsg, wParam, lParam
ret
handle_destroy:
invoke PostQuitMessage, 0
mov isRunning, 0
xor eax, eax
ret
WndProc ENDP
; ------------------------------------------------------------------------------
; Engine_Init: Initializes subsystem, window, math tables and level
; ------------------------------------------------------------------------------
Engine_Init PROC uses ebx esi edi
LOCAL wc:WNDCLASSEXA
LOCAL rc:RECT
invoke timeBeginPeriod, 1
; 1. Initialize Engine Subsystems
call InitMathTables
call GenerateDefaultTextures ; Procedural baseline (always valid)
call LoadTexturePack ; Optional: override from assets\textures\texturepack.bin
call LoadMapBinary ; Loads assets/map.dat (falls back to InitMap if missing)
; 2. Register Window Class
invoke GetModuleHandleA, 0
mov hEngineInstance, eax
mov wc.cbSize, sizeof WNDCLASSEXA
mov wc.style, 0003h ; CS_HREDRAW | CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, 0
mov wc.cbWndExtra, 0
push hEngineInstance
pop wc.hInstance
mov wc.hIcon, 0
invoke LoadCursorA, 0, 32512 ; IDC_ARROW
mov wc.hCursor, eax
mov wc.hbrBackground, 0 ; NULL brush (we draw entire frame)
mov wc.lpszMenuName, 0
mov wc.lpszClassName, OFFSET szClassName
mov wc.hIconSm, 0
invoke RegisterClassExA, ADDR wc
; 3. Adjust Window Client Rect to 1280x960
mov rc.left, 0
mov rc.top, 0
mov rc.right, WINDOW_W
mov rc.bottom, WINDOW_H
invoke AdjustWindowRect, ADDR rc, 00CF0000h, 0 ; WS_OVERLAPPEDWINDOW
; Calculate dimensions with borders
mov eax, rc.right
sub eax, rc.left
mov ebx, rc.bottom
sub ebx, rc.top
; 4. Create Main Window
invoke CreateWindowExA, 0, OFFSET szClassName, OFFSET szWindowTitle,\
00CF0000h, 100, 50, eax, ebx, 0, 0, hEngineInstance, 0
mov hEngineWnd, eax
invoke ShowWindow, hEngineWnd, 1 ; SW_SHOWNORMAL
invoke UpdateWindow, hEngineWnd
invoke GetDC, hEngineWnd
mov hEngineDC, eax
invoke GetTickCount
mov lastTickCount, eax
mov fpsTimer, eax
ret
Engine_Init ENDP
; ------------------------------------------------------------------------------
; Engine_RenderHUD: Draws 2D overlay text, controls, minimap, and FPS
; ------------------------------------------------------------------------------
Engine_RenderHUD PROC uses ebx ecx edx
cmp isCutsceneActive, 0
je normal_engine_hud
call RenderCutsceneOverlay
ret
normal_engine_hud:
; 1. Top HUD Header
invoke DrawString8x8, 10, 10, OFFSET strEngineName, COLOR_YELLOW
; 2. FPS Counter
invoke DrawString8x8, 10, 24, OFFSET szFpsLabel, COLOR_CYAN
invoke DrawNumber8x8, 42, 24, currentFps, COLOR_CYAN
; 3. Controls Bar at Bottom
invoke DrawRect, 0, 215, SCREEN_W, 25, 00101010h
invoke DrawString8x8, 10, 218, OFFSET strControls1, COLOR_WHITE
invoke DrawString8x8, 10, 228, OFFSET strControls3, COLOR_GREEN
; 4. Door / Portal Interaction Hint (center screen)
call CheckDoorHint
cmp eax, 1
je show_door_hint
cmp eax, 2
je show_portal_hint
jmp check_minimap
show_door_hint:
invoke DrawString8x8, 128, 150, OFFSET strDoorHint, COLOR_YELLOW
jmp check_minimap
show_portal_hint:
invoke DrawString8x8, 110, 150, OFFSET strPortalHint, COLOR_CYAN
check_minimap:
cmp mapErrorFrames, 0
je hud_no_map_error
invoke DrawString8x8, 44, 164, OFFSET strMapLoadError, COLOR_RED
hud_no_map_error:
; 5. Minimap Radar (Toggle with 'M')
call DrawMinimap
ret
Engine_RenderHUD ENDP
; ------------------------------------------------------------------------------
; Engine_Frame: Updates single tick and presents frame buffer to window
; ------------------------------------------------------------------------------
Engine_Frame PROC uses ebx esi edi
; 1. Input & Physics Update
cmp mapErrorFrames, 0
je no_error_tick
dec mapErrorFrames
no_error_tick:
call PollInput
call UpdatePlayer
; 2. 3D Raycasting & 2D HUD Render
call Render3DScene
call Engine_RenderHUD
call ApplyCutsceneFade
; 2b. Screenshot request (F12): dump fresh framebuffer to BMP
cmp keyScreenshot, 0
je @F
mov keyScreenshot, 0
call SaveScreenshot
@@:
; 2c. Screen Fade effect for sublevel transitions
call ApplyScreenFade
; 3. Present Framebuffer to Screen via StretchDIBits (320x240 -> 1280x960)
invoke StretchDIBits, hEngineDC,\
0, 0, WINDOW_W, WINDOW_H,\
0, 0, SCREEN_W, SCREEN_H,\
OFFSET screenBuffer, OFFSET bmiEngine,\
0, 00CC0020h ; DIB_RGB_COLORS, SRCCOPY
; 4. Update FPS Counter
inc fpsCounter
invoke GetTickCount
mov edx, eax
sub edx, fpsTimer
cmp edx, 1000
jl frame_done
; 1 second elapsed: update currentFps
mov eax, fpsCounter
mov currentFps, eax
mov fpsCounter, 0
invoke GetTickCount
mov fpsTimer, eax
frame_done:
; Reset one-frame input triggers
mov keyForward, 0
mov keyBackward, 0
mov keyStrafeLeft, 0
mov keyStrafeRight, 0
mov keyTurnLeft, 0
mov keyTurnRight, 0
mov keyAction, 0
ret
Engine_Frame ENDP
; ------------------------------------------------------------------------------
; SaveScreenshot: Dumps screenBuffer to screenshot.bmp (32-bit top-down BMP)
; ------------------------------------------------------------------------------
SaveScreenshot PROC uses ebx
LOCAL hFile:DWORD, bytesWritten:DWORD
invoke CreateFileA, OFFSET szScreenshotPath, GENERIC_WRITE, 0, 0,\
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
mov hFile, eax
cmp eax, INVALID_HANDLE_VALUE
je ss_done
invoke WriteFile, hFile, OFFSET screenshotHeader, 54, ADDR bytesWritten, 0
invoke WriteFile, hFile, OFFSET screenBuffer, FRAME_BUFFER_SIZE, ADDR bytesWritten, 0
invoke CloseHandle, hFile
ss_done:
ret
SaveScreenshot ENDP
; Optional first argument: a quoted or unquoted starting map path.
; Assets and linked map basenames remain relative to the working directory.
ParseStartupMap PROC uses ebx ecx edx esi edi
invoke GetCommandLineA
mov esi, eax
mov startupMapPath, 0
cmp byte ptr [esi], 34
jne skip_executable_word
inc esi
skip_executable_quote:
lodsb
test al, al
jz startup_arg_done
cmp al, 34
jne skip_executable_quote
jmp skip_argument_spaces
skip_executable_word:
lodsb
test al, al
jz startup_arg_done
cmp al, 32
jne skip_executable_word
skip_argument_spaces:
cmp byte ptr [esi], 32
je consume_argument_space
cmp byte ptr [esi], 9
jne copy_startup_argument
consume_argument_space:
inc esi
jmp skip_argument_spaces
copy_startup_argument:
xor ebx, ebx
cmp byte ptr [esi], 34
jne startup_argument_loop_init
mov ebx, 1
inc esi
startup_argument_loop_init:
mov edi, OFFSET startupMapPath
mov ecx, MAX_PATH_BYTES - 1
startup_argument_loop:
lodsb
test al, al
jz terminate_startup_arg
cmp al, 34
je terminate_startup_arg
test ebx, ebx
jnz write_startup_char
cmp al, 32
je terminate_startup_arg
cmp al, 9
je terminate_startup_arg
write_startup_char:
stosb
dec ecx
jnz startup_argument_loop
mov startupMapPath, 0
ret
terminate_startup_arg:
mov byte ptr [edi], 0
startup_arg_done:
ret
ParseStartupMap ENDP
; ------------------------------------------------------------------------------
; main: Application Entry Point & Game Loop
; ------------------------------------------------------------------------------
main PROC
LOCAL msg:MSG
LOCAL curTick:DWORD, elapsed:DWORD
call ParseStartupMap
call TryEnginePreview
call Engine_Init
main_loop:
cmp isRunning, 0
je exit_loop
; Process Window Messages (Non-blocking)
@@:
invoke PeekMessageA, ADDR msg, 0, 0, 0, 1 ; PM_REMOVE
test eax, eax
jz process_game_tick
cmp msg.message, 0012h ; WM_QUIT
je exit_loop
invoke TranslateMessage, ADDR msg
invoke DispatchMessageA, ADDR msg
jmp @B
process_game_tick:
; Frame Rate Limiter (~60 FPS)
invoke GetTickCount
mov curTick, eax
sub eax, lastTickCount
mov elapsed, eax
mov eax, targetFrameTimeMs
cmp elapsed, eax
jge do_tick
invoke Sleep, 1
jmp main_loop
do_tick:
mov eax, curTick
mov lastTickCount, eax
call Engine_Frame
jmp main_loop
exit_loop:
call StopCutscene
invoke ReleaseDC, hEngineWnd, hEngineDC
invoke timeEndPeriod, 1
invoke ExitProcess, 0
main ENDP
END main