Decouple gameplay from presentation and preserve signed movement fractions. Separate cinematic camera ownership with safe body handoff and transactional current-room restarts. Resample input latches on focus regain, discard modal timing debt, and attach cinematic lighting to the view to match editor preview. Verify editor 137/137 and ASM 288/288, cinematic traces, RGB bake and IPC framebuffer parity without changing project assets.
27 KiB
AGENTS.md - EBLAN ENGINE 3D
Project Identity
EBLAN ENGINE 3D is a lightweight, modular, pure x86 assembly (MASM32) 2.5D retro raycasting game engine with software rendering to a 320x240 framebuffer stretched to a 1280x960 Windows GDI window.
- Language: Pure x86 Assembly (MASM32 / MSVC
ml.exe). - Dependencies: Windows API only (
kernel32.lib,user32.lib,gdi32.lib,winmm.lib). No external DLLs, DirectX, OpenGL, or C runtimes. - Rendering: Software DIBits raycaster (Wolfenstein 3D style DDA grid stepping, texture mapping, distance shading, billboard sprites, 2D UI canvas with 8x8 bitmap font).
- Coordinate Space: Player/entity positions are raw world units (64 per cell). Trigonometry and sprite scale use 16.16; DDA distances retain 8 fractional bits (24.8) until projection. Trigonometry uses 512-entry lookup tables.
Directory Structure
EBLAN_ENGINE/
│
├── main.asm # Entry point, Win32 window, input/tick/render loop
├── build.bat # 1-click build script (supports MASM32 and MSVC BuildTools)
├── engine.exe # Compiled game executable
│
├── include/
│ ├── engine_simulation.inc # Fixed 60 Hz QPC scheduler, states and tick commands
│ ├── engine_defs.inc # Screen resolution, colors, key codes, constants, structs
│ ├── engine_data.inc # Framebuffer, depthbuffer, player state, map array, textures
│ ├── engine_math.inc # FPU Sin/Cos table generator, RNG, fixed-point math
│ ├── engine_map.inc # Grid map accessors, wall collision queries, door interaction
│ ├── engine_textures.inc # Procedural textures + external texturepack.bin loader
│ ├── engine_input.inc # Keyboard polling (WASD, arrows, actions)
│ ├── engine_physics.inc # Player movement vector, strafe, wall-sliding collision
│ ├── engine_raycaster.inc # 320-ray DDA raycasting, fog, textured sorted sprites
│ ├── engine_render2d.inc # Pixel, Rect, Outline, 8x8 Font (CP1251), Minimap radar
│ └── font8x8.inc # 8x8 font bitmap glyphs (256 chars, CP1251)
│
├── tools/
│ └── make_texturepack.py # Builds assets/textures/texturepack.bin (from KAZIK pack or procedural)
├── assets/ # map.dat (level), textures/texturepack.bin (optional texture override)
└── docs/
└── ENGINE_ARCHITECTURE.md # Detailed deep-dive into engine math and rendering pipeline
Build System
Run build.bat from command line or IDE:
build.bat
- Assembler:
ml.exe /c /coff /Cp /nologo /I. /Iinclude main.asm - Linker:
link.exe /NOLOGO /SUBSYSTEM:WINDOWS /OUT:engine.exe main.obj kernel32.lib winmm.lib user32.lib gdi32.lib
Core Engine Architecture
1. Frame Lifecycle
Each message-loop pass polls input, pumps fixed simulation, then draws independently:
PollInput: samples held movement and latches pending command edges until consumed by one tick. Focus regain resamples physical latches without emitting actions.PumpSimulationClock/Engine_Tick: QPC accumulator at exactly 60 Hz, up to eight catch-up ticks. Stalls >250 ms and pause/focus/modal/restart boundaries discard timing debt. Playing updates player, entities, combat, gameplay hook, lights and toast; other states freeze or update only their own systems.UpdatePlayeruses signed 16.16 runtime remainders, normalized diagonals and full-body unit-step wall-sliding collision. Portals remain walkable; occupied doors cannot close.Render3DScene:- Clears ceiling (
COLOR_CEILING) and floor (COLOR_FLOOR). - Selects
viewX/Y/Anglefrom player or separatecsCameraX/Y/Angle, then casts 320 rays across FOV (~67.5°). - Traverses grid using DDA until a solid cell is hit.
- Computes perpendicular wall distance (fish-eye corrected).
- Samples 128x128 texture stripe, applies side shading (Y-walls darkened for 3D depth) and distance fog.
- Populates
depthBuffer[screenX]. - Renders billboard sprites from
spriteListwith depth testing.
- Clears ceiling (
Engine_RenderHUD: draws 2D overlay (title, FPS counter, minimap radar, controls).StretchDIBits: copiesscreenBuffer(320x240 BGRA) to window DC (1280x960).
Drawing never advances gameplay timers, closes dialogs or switches rooms.
UpdateScreenFade advances transitions on ticks; ApplyScreenFade only darkens
pixels. State precedence: Unfocused, Paused, Dialog, Transition, Dead, Cutscene,
Playing. Detailed rules and regression coverage: docs/STABILIZATION.md.
How to Extend This Engine (Guidelines for AI Agents)
A. How to Add a New Wall Texture
- Open
include/engine_defs.incand define a new cell ID:CELL_MYWALL equ 8 - Open
include/engine_textures.incand generate pixels intotextureBuffer + (8 * TEX_SIZE_BYTES). Each pixel is a 32-bit DWORD in00RRGGBBhformat. - Place
CELL_MYWALLintogameMapininclude/engine_map.inc.
B. How to Add a New Sprite / Entity (e.g. Enemy, Coin, Prop)
Sprite positions use raw world units (1 cell = 64 units), the same scale as
playerX/playerY. In your initialization code (e.g. Engine_Init):
mov eax, spriteCount
cmp eax, MAX_SPRITES
jge @F
mov ebx, eax
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
mov [ebx].SpriteStruct.x, (15 * 64 + 32) ; X in raw world units (cell 15.5)
mov [ebx].SpriteStruct.y, (10 * 64 + 32) ; Y in raw world units (cell 10.5)
mov [ebx].SpriteStruct.textureId, 11 ; texture slot (11 = barrel)
mov [ebx].SpriteStruct.scale, 65536 ; 1.0 scale (16.16 fixed)
inc spriteCount
@@:
Sprites are rendered as textured billboards: back-to-front sorted by distance,
per-column depth-tested against depthBuffer, floor-anchored, with distance fog.
Texel value 00FF00FFh (magenta, COLOR_CHROMA_KEY) is transparent.
textureId 0 samples slot 0 (empty/black) — use it as a debug marker.
scale multiplies the projected size (65536 = 1.0, 131072 = 2.0).
C. How to Draw Custom 2D HUD (Health Bar, Text, Dialogs)
Inside Engine_RenderHUD in main.asm:
; Draw Health Bar background (Red)
invoke DrawRect, 10, 40, 100, 10, 00000088h
; Draw Health Bar fill (Green)
invoke DrawRect, 10, 40, playerHealth, 10, 0000FF00h
; Draw Outline
invoke DrawRectOutline, 10, 40, 100, 10, COLOR_WHITE
; Print String
invoke DrawString8x8, 10, 55, OFFSET myMessageStr, COLOR_YELLOW
; Print Number
invoke DrawNumber8x8, 120, 42, playerHealth, COLOR_WHITE
D. Math Rules
- All angles are integers in range
0..511(ANGLE_512). Always mask withand eax, ANGLE_MASK(511). - Trigonometry:
[tableCos + eax*4]and[tableSin + eax*4]return 16.16 signed fixed point (65536 = 1.0). - Player and sprite positions (
playerX,playerY,SpriteStruct.x/y) are raw world units: 1 cell = 64 units. Convert to cell index withsar eax, 6. - Multiplying a raw distance by cos/sin:
mov eax, playerAngle and eax, ANGLE_MASK mov ebx, [tableCos + eax*4] imul ebx, distance sar ebx, 16 ; result in ebx (raw units)
E. Binary Level Format (assets/map.dat) & EblanStudio
The engine supports both procedural default generation and binary level loading. When assets/map.dat exists, LoadMapBinary in include/engine_map.inc automatically loads it:
- Header (
MapHeader, 38 bytes, packed):magic(uint32):0x4E4C4245("EBLN")version(uint16):5(readers also accept v1 fixed sprite coordinates and v2/v3/v4 raw-coordinate maps, including early v2 maps with transitions)mapWidth(uint16):32mapHeight(uint16):32playerStartX(uint16): cell X coordinate (e.g. 2)playerStartY(uint16): cell Y coordinate (e.g. 2)playerStartAngle(uint16): 0..511 anglespriteCount(uint16): count of sprite entriesceilingColor(uint32): 00RRGGBBhfloorColor(uint32): 00RRGGBBhtransitionCount(uint16): 0..16 (v1 ignores this formerly reserved field)cutsceneCount(uint16): 0..128 commands (offset 28, v4+)cutsceneStringCount(uint16): 0..32 strings (offset 30, v4+)cutsceneFlags(uint16): bit 0 = autoplay on room entry (offset 32, v4+)lightCount(uint16): 0..13 authored sources (offset 34, v5 only)lightingReserved(uint16): zero (offset 36, v5 only)
- Grid Data:
32 * 32 = 1024bytes (row-major order). Cell value = texture slot (1 brick, 2 wood, 3 stone, 4 metal, 5 door, 7 pillar, 8 glass, 9 marble). - Sprite Array:
spriteCount * 24bytes (SpriteStruct:x,yin raw world units,textureId,scale16.16,dist,flags). - Transition Array:
transitionCount * 42bytes (LevelTransitionin bothengine_defs.incandeditor/src/level_format.h). Target names are ASCII basenames, 5..31 bytes including.dat, with a terminating NUL insidetargetMap[32]. Put linked maps in the same assets directory. Triggers must be unique and occupy empty, open-door or portal cells; every portal cell needs a transition. - Files are validated completely before replacing the active room. Counts above capacity, truncated/extra payload, invalid coordinates/scales, blocked spawns and invalid transition names are rejected. Black ceiling/floor colors are valid. A failed runtime transition keeps the current room; a failed startup load creates the procedural map.
- Cutscene Array (v4): after transitions,
cutsceneCount * 16bytes; followed bycutsceneStringCount * 96bytes of NUL-terminated CP1251 strings. No serialized pointers. Earlier versions ignore the new header fields and load an empty track. - New saves use v5 without rewriting existing map files until explicitly saved. Sprite positions must remain inside the map; scales accepted by loaders are 0..16.0 (0 keeps the legacy 1.0 behavior).
Human users can design levels visually using run_editor.bat (editor/editor.exe, written in C++17 + ImGui + DirectX 11), with 1-click Playtest (F5).
AI agents can directly read or write assets/map.dat following this specification.
F5 copies neighboring maps and resources into a dedicated temporary directory, writes the current unsaved map there under its original filename, validates all reachable destinations, and launches engine.exe "assets\selected-map.dat". It never replaces the source assets/map.dat or changes the editor's current file path. Starting the engine without an argument still loads assets/map.dat.
F. Texture Pack (assets/textures/texturepack.bin)
At startup the engine always generates procedural textures first, then
LoadTexturePack tries to override them from assets/textures/texturepack.bin:
- Header (8 bytes):
magic(uint32) =0x58544245("EBTX"),version(uint16) = 1,slotCount(uint16) ≤ 16. - Pixels:
slotCount * 65536bytes — slot 0 first, each slot is 128x128 BGRA32. Rebuild the pack from the KAZIK 48-slot atlas (casino3d-hd.bin) or fully procedurally:
python tools\make_texturepack.py
If the pack is missing or invalid, the engine silently keeps procedural textures.
The loader stages and validates the complete pack before replacing pixels. Slots 10..15 are sprite slots; CELL_PORTAL uses a separate portalTextureBuffer, so importing slot 10 never changes portals. First import creates missing directories and seeds untouched slots with procedural defaults. Import/reset writes are atomic; an invalid existing pack is preserved and reported to the user.
G. Regression Checks
Run python tools\test_regressions.py on Windows with the build toolchains installed. The scenarios compile the real editor logic and ASM procedures, exercise file I/O, playtest isolation, transitions, keyboard edges, renderer geometry, cutscene validation and editor undo in TEMP, and verify that project assets remain unchanged. C++ preview and ASM camera/angle/fade/subtitle states are compared frame by frame. Use --keep to retain generated framebuffers and fixtures. These checks do not replace a manual UI playtest.
H. Cutscenes (v4)
include/engine_cutscene.inc owns validation, playback and overlays. CutsceneEvent is packed: uint16 tickTimestamp, uint8 opCode, uint8 param8, int32 paramA, int32 paramB, int32 dataId. Time uses 60 Hz ticks, timestamps 0..65535, individual durations 0..3600 ticks. Commands are sorted by timestamp; a nonempty track ends with a single final END.
| Opcode | paramA | paramB | dataId / param8 |
|---|---|---|---|
| 0 END | ignored | ignored | Returns control, retains camera pose |
| 1 WAIT | duration | ignored | Delays dispatch; existing tweens continue |
| 2 MOVE_CAM_TO | raw world X | raw world Y | dataId = duration (0 snaps) |
| 3 PAN_CAM_TO | target angle 0..511 | duration | Shortest arc |
| 4 SHOW_SUBTITLE | string index | duration (0 persistent) | CP1251, up to 95 characters |
| 5 HIDE_SUBTITLE | ignored | ignored | Clears subtitle |
| 6 PLAY_SOUND | string index, or -1 to stop | ignored | param8 bit 0 = loop |
| 7 FADE_SCREEN | darkness 0..16 | duration | 0 clear, 16 black |
| 8 CHANGE_SUBLEVEL | transition index | ignored | Uses existing transition destination/spawn/fade |
MOVE and PAN interpolate linearly using signed integer math. Camera endpoints must be walkable; movement segments ignore collisions. Each tick advances existing tweens, expires text and waits, then dispatches due commands in stored order. Same-timestamp commands can start independent movement, pan and fade together. WAIT delays overdue commands without shifting their timestamps. A replacement tween starts from the current pose.
Autoplay begins after a room transition finishes. C starts/replays the current map's track; Tab skips it. Movement/actions are blocked during playback. The camera owns csCameraX/Y/Angle, leaving the gameplay body stationary; lighting and flashlight follow the selected view. End/skip/level changes stop cinematic sound and clear subtitles/fade. END/Tab transfer the camera pose only if the entire player collision body is walkable; otherwise retain the previous safe player pose. Letterbox is 2.35:1, subtitles use up to 3 centered rows of 38 glyphs over a half-brightness panel. WAV names are ASCII basenames of up to 63 characters in assets/sounds; playback uses one asynchronous PlaySound channel.
RestartCurrentRoom is shared by death R and pause item 4. It reloads the current
room with the actual entry spawn, then clears gameplay transients and timing debt
only after success. Failed reload preserves the room, death/pause and statistics.
Genre, weapon/HUD configuration and selected weapon are preserved; authored room
lighting is reloaded. EBLN coordinates and IPC layouts are unchanged.
F6 opens EblanStudio's timeline. Author commands with buttons, drag diamonds to change timestamps, select overlapping events through the command list, and place MOVE points by clicking the map. The inspector edits parameters and strings. Autoplay, scene data and strings participate in save/load and undo/redo. Preview shows the actual game perspective in the embedded viewport with the camera path on the map; F5 runs the full game, validates referenced WAV files and stages them with linked maps. Editor preview stops at a level-change command. F5/F6 are handled before ImGui keyboard-capture checks.
examples/cutscene_demo/run_demo.bat runs an isolated two-room example with camera waypoints, Russian subtitles, WAV, fades and a scripted transition. Regenerate it with python tools/make_cutscene_demo.py.
I. Embedded Game View
The x64 editor uses a windowless x86 engine.exe --preview:Local\EblanPreview-... process to execute the actual Render3DScene, cinematic overlay and fade procedures. No duplicate raycaster. editor/src/preview_protocol.h and include/engine_preview.inc define a versioned 1,362,408-byte shared-memory mailbox (protocol v2): 128-byte header, 1024 cells, 32 sprites, a 96-byte CP1251 subtitle, 16 texture slots, a 320x240 DIB frame, 13 authored light records and 1024 baked RGB cells. Header state is 0 idle, 1 engine owns payload, 2 editor owns result, 3 shutdown. Publish with InterlockedExchange/xchg; never modify payload while state is 1. All fields are fixed-width; do not put pointers or size_t into the wire layout.
The renderer validates camera and sprite bounds, uploads textures only when their revision changes, and exits if its parent process ends. The editor has process-exit/timeout checks and a retry button, converts DIB padding to opaque alpha, uploads BGRA to DX11 and displays with point sampling at 4:3. Map/inspector changes appear without saving. Timeline playback/scrubbing drives the viewport camera, subtitles and fade. Free-camera inspection affects no authored data. The viewport shows the scene and cinematic overlays; normal game HUD, door interactions and room switching are tested in F5.
tools/test_preview_ipc.py exercises the actual renderer without a visible window. The full regression suite also checks the x86-to-x64-to-DX11 path on a WARP device and compares the live viewport's pixels with a direct ASM rendering. Build engine.exe before running these tests.
J. Game Studio IDE, Entity Archetypes & Combat Subsystems
EblanStudio has evolved from a pure level editor into a Full Game Engine Studio / IDE for retro x86 Assembly (MASM32) games:
-
Entity Archetypes & AI (
include/engine_entity.inc):0: Prop: static decoration / obstacles.1: Guard AI: patrolling enemy guard that detects the player within an aggro radius, chases, and deals melee damage.2: Medkit: collectible healing item (+25 HP up to 100).3: Ammo Box: collectible ammunition pickup (+20 rounds up to 100).4: Key: key card for unlocking locked doors.5: Switch: interactive trigger/lever toggled by proximity action ([E]).6: Explosive Barrel: destructible barrel that explodes on weapon hit with 60 AOE damage to surrounding entities and fiery screen flash.- Packed into
SpriteStruct.flagswithout altering binary map compatibility (sizeof(SpriteEntry) == 24).
-
Player Combat & Multi-Weapon Arsenal (
include/engine_combat.inc):- Weapon switching via keys
[1],[2],[3]:- [1] Боевой нож (Combat Knife): fast melee attack (cooldown 8, 30 damage, no ammo required).
- [2] Пистолет (Pistol): standard handgun (cooldown 12, 35 damage, 1 ammo).
- [3] Дробовик (Pump Shotgun): heavy scatter weapon (cooldown 22, 70 damage, 2 ammo, wide spread).
- Dynamic Muzzle Flash Room Illumination: shooting dynamically brightens ceiling and floor shading across the whole 3D environment for realistic muzzle flare lighting.
- Center crosshair and hitscan raycasting against active enemy guards and barrels.
- Damage screen flash (red overlay), death state screen, and instant level restart with
[R]. - In-Game Retro Pause Menu via
[ESC]: sleek semi-transparent pause menu with[1] ПРОДОЛЖИТЬ,[2] ЗАНОВО,[3] МИНИ-КАРТА,[4] ВЫХОД В WINDOWS. - Comprehensive combat HUD: Active weapon badge with slot indicators
[1] [2] [3], Health bar, Ammo counter, Score counter, and Key indicator.
- Weapon switching via keys
-
In-Editor MASM32 IDE (
F7/ "Код / IDE"):- Integrated in-engine code editor (
editor/src/editor_code.inl) accessible viaF7,Ctrl+B, or the main menu. - MASM syntax highlighting (instructions, registers, directives, comments, strings, numbers).
- Project file navigator (
engine_entity.inc,engine_combat.inc,main.asm, etc.). - Engine API Reference Catalog: built-in searchable catalog of all engine procedures with 1-click "Вставить" snippet insertion.
- Real-time search bar (
Поиск в коде...) with instant match counter. - Script templates (Custom Guard AI, Mega-Medkit, Secret Door Trigger, Explosive Barrel, Custom HUD).
- Asynchronous 1-click compiler runner (
build.bat/ml.exe) with interactive output console and clickable error lines.
- Integrated in-engine code editor (
-
Editor UI, 2D Canvas & Project Exporter (
editor/src/editor_app.cpp):- Modern Game Studio dark theme with Electric Cyan and Neon Amber accents.
- 2D Canvas Guard Aggro Circles: visualizes guard detection and chase radius on the 2D map.
- Quick Entity Spawner Palette: 1-click spawn buttons (
+ Враг,+ Аптечка,+ Патроны,+ Ключ,+ Рычаг,+ Бочка,+ Проп). - Canvas Zoom buttons (
[-],[100%],[+]) and pan controls. - Project configuration file (
project.eblanproj) defining title, author, version, window title, and startup map. - "Экспорт автономной игры (.exe)" builds a standalone release folder containing the renamed game executable, packaged assets (maps, textures, sounds), launcher batch file, and readme.
-
Gameplay Scripting Subsystem & Interactive Modals (
include/engine_gameplay.inc):ShowGameToast(pszText, durationTicks, color): floating framed notification banners (auto-centered, custom CP1251 string, colored outline, auto-dismiss).ShowGameDialog(pszSpeaker, pszText): modal dialogue & lore note reader freezing player movement until dismissed with[E]/[ENTER].- Extensible engine lifecycle hooks:
Game_OnTick: 60 Hz frame tick hook for custom game logic, timers, and puzzle triggers.Game_OnPlayerInteract(entityIdx, entityType): intercepts[E]interactions before default behavior.Game_OnEntityKilled(entityIdx, entityType): triggers on guard death or barrel explosion for boss encounters.Game_OnCustomHUD: custom 2D canvas drawing (health bars, gauges, quest markers).
PlayGameSFX(pszRelativePath): safe asynchronous WinMM audio player.- Built-in automatic gameplay toasts for keys, medkits, ammo, switches, doors, and explosions.
-
Game Genres, Peaceful Adventure Mode & Immersive Clean HUD:
GAME_MODE_ADVENTURE (0): pure exploration / quest / horror / puzzle mode without weapons, ammo, crosshair, or combat overlays.GAME_MODE_ACTION (1): full combat shooter mode with weapon models, crosshair, health bar, and ammo counter.HUD_STYLE_CLEAN (0): 100% immersive full-screen view (no debug banners, no text collisions, no bottom controls clutter).HUD_STYLE_DEBUG (1): developer mode displaying FPS counter at top-right (X=250), engine title, score, and controls hints.- Instant in-game switching:
[ESC] -> [2]: toggle weapons / peaceful mode on the fly with toast notifications.[ESC] -> [3]: toggle clean vs debug HUD.[F1]or[H]: quick hotkey to toggle clean HUD vs debug HUD.
- Editor toolbar 1-click genre switcher:
☮ КВЕСТ / БЕЗ ОРУЖИЯvs⚔ ЭКШЕН / С ОРУЖИЕМ. - Project settings (
project.eblanproj) persistGameMode,ShowWeapons, andShowCrosshair.
-
Static Lightmap Baking & Realtime Dynamic Lighting (
include/engine_light.inc):BakeMapLighting: precomputes 1024 RGB cells inlightGridRGBfrom active STATIC sources only, with exact supercover cell-boundary occlusion, approximate distance attenuation and an out-of-place, wall-aware cross filter.lightGridis a derived grayscale view. Realtime sources are never baked or counted twice.- Dynamic point lights (
LIGHT_TYPE_POINT) with customizable color, radius, intensity, and organic torch flame flicker (LIGHT_FLAG_FLICKER). - Dynamic burst lights (
LIGHT_TYPE_BURST): weapon muzzle flare on pistol/shotgun shots and high-intensity fiery explosion blast on explosive barrel detonation. - Player directional spotlight flashlight
[F]: conical falloff (flashlightConeAngle), distance attenuation, and wall occlusion physics. - Dynamic lighting mode cycle
[L]:LIGHTING_MODE_NONE (0): classic flat Wolf3D retro fullbright.LIGHTING_MODE_BAKED (1): static baked lightmap shadows and ambient shading.LIGHTING_MODE_DYNAMIC (2): full dynamic physics (point lights + player flashlight + muzzle flares + barrel blasts + lightmap).
- EblanStudio Studio & Editor Lighting Tools:
- 1-Click Toolbar Button:
💡 ЗАПЕЧЬ СВЕТ(Bake Lightmap) for instant baking. - Toolbar Lighting Mode Switcher:
[☀ Свет: ВЫКЛ],[🕯 Свет: ЗАПЕЧЁН],[⚡ Свет: ДИНАМИКА]. - Toolbar Flashlight Toggle:
[🔦 Фонарик: ВКЛ/ВЫКЛ]. - 2D Canvas Lightmap Heatmap Overlay:
[🗺 Карта: ВКЛ/ВЫКЛ]visualizing light and shadow gradients across the level grid. - Light Source Entities on 2D Canvas: interactive light markers with RGB tint, glow halo, radius circle, click-to-select, and real-time mouse dragging.
- Quick Spawner Palette:
+ Источник светаbutton. - Scene Hierarchy: dedicated
💡 Источники света (Lights)tree with selection and "+ Добавить свет". - Property Inspector: position X/Y sliders, radius slider, intensity slider, RGB color picker, light type dropdown (Static, Point, Torch flicker, Pulse), wall shadow-casting toggle, and torch flicker toggle.
- Real-Time 3D Viewport synchronization via
EnginePreviewPacketshared-memory protocol.
- 1-Click Toolbar Button:
K. Authored Lighting Format (EBLN v5)
After the v4 cutscene strings, v5 appends LightingSettings (32 bytes), lightCount * 40 bytes of LightSource, and 1024 DWORD baked RGB cells (00RRGGBB, 4096 bytes). Settings are eight DWORDs: mode (0..2), ambient (0..255), flashlight (0/1), flashlight radius (32..2048), intensity (0..255), half cone angle (1..96 angle units), baked-valid (1), reserved (0). Source layouts match EditorLightSource and MASM LightSource. Counts/settings/coordinates/radii/intensities/flags/colors and exact file size are validated before committing a room. v1..v4 load no authored lights and mode NONE; the old reserved header bytes are ignored.
Sources and baked data are map-local and travel with save/load, undo/redo, F5 and standalone exports. Static lighting is baked in the editor with BakeLightingRGB and in ASM with BakeMapLighting; all 1024 RGB cells are regression-compared. Realtime point/pulse/flicker lights use only the live source array on top of the persisted static map. Shadow flags are respected. Slot 0 belongs to the player flashlight, 1..13 to authored sources, 14 to muzzle flare, 15 to explosions; combat never overwrites authored lights. Both wall stripes and sprites receive RGB coefficients. Flat ceiling/floor colors use a camera-cell light sample, not per-pixel floor projection.
Editor light settings are in the property inspector and toolbar. A stale-bake indicator tracks static sources, geometry and ambient, excluding realtime movement. Saving or F5 automatically rebuilds a stale static bake; runtime and preview consume persisted RGB without baking each frame. The IPC v2 header includes flashlight parameters and a deterministic light tick; sources begin at offset 1,357,792 and baked RGB at 1,358,312. Update the C++, ASM and Python layouts together.
Lighting verification (24.10.2026): 117 editor checks and 131 ASM checks passed; all 1024 RGB bake cells match, and both lit and classic viewport framebuffers match direct ASM rendering. Project assets were verified unchanged.