367 lines
9.8 KiB
PHP
367 lines
9.8 KiB
PHP
; ==============================================================================
|
|
; 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
|
|
|