; ============================================================================== ; 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_entity.inc include include/engine_raycaster.inc include include/engine_render2d.inc include include/engine_combat.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 call UpdateEntities call UpdateCombat ; 2. 3D Raycasting & 2D HUD Render call Render3DScene call Engine_RenderHUD cmp isCutsceneActive, 0 jne @F call RenderCombatHUD @@: 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 mov keyAttack, 0 mov keyRestart, 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