EBLAN_ENGINE/main.asm

352 lines
9.9 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
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
; 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
.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 @F
invoke PostQuitMessage, 0
mov isRunning, 0
xor eax, eax
ret
@@:
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
; 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 Interaction Hint (center screen)
call CheckDoorHint
test eax, eax
jz @F
invoke DrawString8x8, 128, 150, OFFSET strDoorHint, COLOR_YELLOW
@@:
; 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
call PollInput
call UpdatePlayer
; 2. 3D Raycasting & 2D HUD Render
call Render3DScene
call Engine_RenderHUD
; 2b. Screenshot request (F12): dump fresh framebuffer to BMP
cmp keyScreenshot, 0
je @F
mov keyScreenshot, 0
call SaveScreenshot
@@:
; 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:
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
; ------------------------------------------------------------------------------
; main: Application Entry Point & Game Loop
; ------------------------------------------------------------------------------
main PROC
LOCAL msg:MSG
LOCAL curTick:DWORD, elapsed:DWORD
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:
invoke ReleaseDC, hEngineWnd, hEngineDC
invoke timeEndPeriod, 1
invoke ExitProcess, 0
main ENDP
END main