; ============================================================================== ; EBLAN ENGINE - Map System & World Grid ; ============================================================================== .code ; ------------------------------------------------------------------------------ ; InitMap: Creates a rich test level with rooms, hallways, pillars & doors ; ------------------------------------------------------------------------------ InitMap PROC uses eax ecx edi ; Clear map to empty mov edi, OFFSET gameMap mov ecx, MAP_SIZE xor eax, eax rep stosb ; 1. Build Outer Boundary Walls (Type 1: Brick) mov ecx, 0 loop_boundary_x: ; Top row (y = 0) mov byte ptr [gameMap + ecx], CELL_BRICK ; Bottom row (y = 31) mov byte ptr [gameMap + (31 * MAP_W) + ecx], CELL_BRICK inc ecx cmp ecx, MAP_W jl loop_boundary_x mov ecx, 0 loop_boundary_y: ; Left column (x = 0) mov eax, ecx shl eax, 5 ; eax = ecx * 32 mov byte ptr [gameMap + eax], CELL_BRICK ; Right column (x = 31) mov byte ptr [gameMap + eax + 31], CELL_BRICK inc ecx cmp ecx, MAP_H jl loop_boundary_y ; 2. Build Internal Room Walls (y = 8, x from 1 to 20, Stone Brick) mov ecx, 1 loop_wall1: mov byte ptr [gameMap + (8 * MAP_W) + ecx], CELL_STONE inc ecx cmp ecx, 20 jle loop_wall1 ; Create Door at (x = 10, y = 8) mov byte ptr [gameMap + (8 * MAP_W) + 10], CELL_DOOR ; 3. Build Vertical Dividing Wall (x = 20, y from 1 to 24, Wood) mov ecx, 1 loop_wall2: cmp ecx, 14 ; Leave opening at y = 14 je @F mov eax, ecx shl eax, 5 ; ecx * 32 mov byte ptr [gameMap + eax + 20], CELL_WOOD @@: inc ecx cmp ecx, 24 jle loop_wall2 ; 4. Add Pillars in the main courtyard (Type 4: Metal / Type 7: Pillar) mov byte ptr [gameMap + (4 * MAP_W) + 4], CELL_PILLAR mov byte ptr [gameMap + (4 * MAP_W) + 7], CELL_PILLAR mov byte ptr [gameMap + (12 * MAP_W) + 5], CELL_METAL mov byte ptr [gameMap + (12 * MAP_W) + 15], CELL_METAL mov byte ptr [gameMap + (18 * MAP_W) + 8], CELL_PILLAR mov byte ptr [gameMap + (18 * MAP_W) + 12], CELL_PILLAR ; 5. South wing decorative walls mov byte ptr [gameMap + (24 * MAP_W) + 5], CELL_STONE mov byte ptr [gameMap + (24 * MAP_W) + 6], CELL_STONE mov byte ptr [gameMap + (24 * MAP_W) + 7], CELL_DOOR mov byte ptr [gameMap + (24 * MAP_W) + 8], CELL_STONE mov byte ptr [gameMap + (24 * MAP_W) + 9], CELL_STONE ; Safe player spawn + clear entities (raw world units) mov playerX, 160 mov playerY, 160 mov playerAngle, 0 mov spriteCount, 0 ret InitMap ENDP ; ------------------------------------------------------------------------------ ; GetMapCell: Returns cell value at grid coordinates (gridX, gridY) ; ------------------------------------------------------------------------------ GetMapCell PROC mapX:DWORD, mapY:DWORD mov eax, mapX cmp eax, 0 jl out_of_bounds cmp eax, MAP_W jge out_of_bounds mov edx, mapY cmp edx, 0 jl out_of_bounds cmp edx, MAP_H jge out_of_bounds shl edx, 5 ; mapY * 32 add edx, eax movzx eax, byte ptr [gameMap + edx] ret out_of_bounds: mov eax, CELL_BRICK ; Solid wall outside bounds ret GetMapCell ENDP ; ------------------------------------------------------------------------------ ; SetMapCell: Sets cell value at grid coordinates (gridX, gridY) ; ------------------------------------------------------------------------------ SetMapCell PROC mapX:DWORD, mapY:DWORD, cellVal:DWORD mov eax, mapX cmp eax, 0 jl done_set cmp eax, MAP_W jge done_set mov edx, mapY cmp edx, 0 jl done_set cmp edx, MAP_H jge done_set shl edx, 5 add edx, eax mov al, byte ptr [cellVal] mov byte ptr [gameMap + edx], al done_set: ret SetMapCell ENDP ; ------------------------------------------------------------------------------ ; IsSolidCell: Returns 1 if cell blocks player and rays, 0 otherwise ; ------------------------------------------------------------------------------ IsSolidCell PROC cellType:DWORD mov eax, cellType cmp eax, CELL_EMPTY je not_solid cmp eax, CELL_DOOR_OPEN je not_solid mov eax, 1 ; Solid wall or closed door ret not_solid: xor eax, eax ret IsSolidCell ENDP ; ------------------------------------------------------------------------------ ; InteractDoor: Checks if player is facing a door and toggles open/close ; ------------------------------------------------------------------------------ InteractDoor PROC uses ebx ecx edx LOCAL targetX:DWORD, targetY:DWORD ; Compute point 60 units in front of player mov eax, playerAngle and eax, ANGLE_MASK ; dx = cos(angle) * 60 mov ebx, [tableCos + eax*4] imul ebx, 60 sar ebx, 16 add ebx, playerX sar ebx, 6 ; divide by CELL_SIZE (64) mov targetX, ebx ; dy = sin(angle) * 60 mov ecx, [tableSin + eax*4] imul ecx, 60 sar ecx, 16 add ecx, playerY sar ecx, 6 mov targetY, ecx invoke GetMapCell, targetX, targetY cmp eax, CELL_DOOR jne check_door_close ; Open the door! invoke SetMapCell, targetX, targetY, CELL_DOOR_OPEN ret check_door_close: cmp eax, CELL_DOOR_OPEN jne no_door ; Close the door! invoke SetMapCell, targetX, targetY, CELL_DOOR no_door: ret InteractDoor ENDP ; ------------------------------------------------------------------------------ ; CheckDoorHint: Returns eax=1 if player is facing a door (for HUD hint) ; ------------------------------------------------------------------------------ CheckDoorHint PROC uses ebx ecx edx LOCAL targetX:DWORD, targetY:DWORD ; Compute point 60 units in front of player mov eax, playerAngle and eax, ANGLE_MASK mov ebx, [tableCos + eax*4] imul ebx, 60 sar ebx, 16 add ebx, playerX sar ebx, 6 mov targetX, ebx mov ecx, [tableSin + eax*4] imul ecx, 60 sar ecx, 16 add ecx, playerY sar ecx, 6 mov targetY, ecx invoke GetMapCell, targetX, targetY cmp eax, CELL_DOOR je hint_yes cmp eax, CELL_DOOR_OPEN je hint_yes xor eax, eax ret hint_yes: mov eax, 1 ret CheckDoorHint ENDP ; ------------------------------------------------------------------------------ ; LoadMapBinary: Checks if assets/map.dat exists and loads custom map, ; player spawn position, angle, ceiling/floor colors, and sprites. ; Returns: eax = 1 if custom map loaded, 0 if fallback to procedural map ; ------------------------------------------------------------------------------ LoadMapBinary PROC uses ebx ecx edx esi edi LOCAL hFile:DWORD LOCAL bytesRead:DWORD LOCAL header:MapHeader LOCAL count:DWORD ; Try to open assets/map.dat invoke CreateFileA, OFFSET szMapFilePath, GENERIC_READ, FILE_SHARE_READ,\ 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 mov hFile, eax cmp hFile, INVALID_HANDLE_VALUE je load_fallback ; Read MapHeader (38 bytes, packed) invoke ReadFile, hFile, ADDR header, sizeof MapHeader, ADDR bytesRead, 0 test eax, eax jz close_and_fallback cmp bytesRead, sizeof MapHeader jl close_and_fallback ; Verify Magic 'EBLN' cmp header.magic, MAP_MAGIC jne close_and_fallback ; Validate version (1 = legacy 16.16 sprite coords, 2 = raw units) movzx eax, header.version cmp eax, 1 jl close_and_fallback cmp eax, 2 jg close_and_fallback ; Validate grid dimensions (engine is fixed at 32x32) cmp header.mapWidth, MAP_W jne close_and_fallback cmp header.mapHeight, MAP_H jne close_and_fallback ; Apply Player Spawn Position (in cells -> fixed-point world units) movzx eax, header.playerStartX shl eax, 6 ; * 64 add eax, 32 ; center in cell mov playerX, eax movzx eax, header.playerStartY shl eax, 6 ; * 64 add eax, 32 mov playerY, eax ; Apply Player Start Angle (0..511) movzx eax, header.playerStartAngle and eax, ANGLE_MASK mov playerAngle, eax ; Apply Custom Ceiling and Floor Colors if specified cmp header.ceilingColor, 0 je @F mov eax, header.ceilingColor mov mapCeilingColor, eax @@: cmp header.floorColor, 0 je @F mov eax, header.floorColor mov mapFloorColor, eax @@: ; Read 1024 bytes of gameMap invoke ReadFile, hFile, OFFSET gameMap, MAP_SIZE, ADDR bytesRead, 0 test eax, eax jz close_and_fallback cmp bytesRead, MAP_SIZE jne close_and_fallback ; Read Sprites movzx eax, header.spriteCount cmp eax, MAX_SPRITES jle @F mov eax, MAX_SPRITES @@: mov count, eax mov spriteCount, eax test eax, eax jz finish_load imul eax, sizeof SpriteStruct mov ecx, eax invoke ReadFile, hFile, OFFSET spriteList, ecx, ADDR bytesRead, 0 test eax, eax jz close_and_fallback mov eax, count imul eax, sizeof SpriteStruct cmp bytesRead, eax jne close_and_fallback ; Format v1 stored sprite x/y as 16.16 fixed point -> convert to raw units cmp header.version, 1 jne finish_load mov ecx, 0 convert_v1_sprites: cmp ecx, count jge finish_load mov eax, ecx imul eax, sizeof SpriteStruct add eax, OFFSET spriteList mov edx, [eax].SpriteStruct.x sar edx, 16 mov [eax].SpriteStruct.x, edx mov edx, [eax].SpriteStruct.y sar edx, 16 mov [eax].SpriteStruct.y, edx inc ecx jmp convert_v1_sprites finish_load: invoke CloseHandle, hFile mov eax, 1 ; Custom map loaded! ret close_and_fallback: invoke CloseHandle, hFile load_fallback: call InitMap ; Procedural fallback level xor eax, eax ret LoadMapBinary ENDP