EBLAN_ENGINE/include/engine_entity.inc
Игорь Федосеев 7a56403d65 feat: add authored history and door interactions
Group level edits across inspector and canvas gestures, preserve legacy maps, and expose opt-in switch/key door bindings without changing wire layouts.

Refresh studio and pause UI with UE5-inspired surfaces and content drawer. Verify 162 editor and 307 ASM checks plus renderer parity.
2026-10-31 22:51:08 +03:00

458 lines
11 KiB
PHP

; ==============================================================================
; EBLAN ENGINE - Entity & Gameplay Behavior System
; ==============================================================================
.code
ShowGameToast PROTO :DWORD, :DWORD, :DWORD
Game_OnPlayerInteract PROTO :DWORD, :DWORD
RestartCurrentRoom PROTO
; ------------------------------------------------------------------------------
; InitEntities: Initialize entity runtime state from spriteList flags/defaults
; ------------------------------------------------------------------------------
InitEntities PROC uses eax ebx ecx edx esi edi
xor eax, eax
mov ecx, MAP_SIZE
mov edi, OFFSET doorUnlocked
rep stosb
; 1. Clear runtime arrays
xor eax, eax
mov ecx, MAX_SPRITES
mov edi, OFFSET entityType
rep stosd
mov ecx, MAX_SPRITES
mov eax, 100
mov edi, OFFSET entityHealth
rep stosd
xor eax, eax
mov ecx, MAX_SPRITES
mov edi, OFFSET entityState
rep stosd
mov ecx, MAX_SPRITES
mov edi, OFFSET entityTimer
rep stosd
mov ecx, MAX_SPRITES
mov eax, 2
mov edi, OFFSET entitySpeed
rep stosd
mov ecx, MAX_SPRITES
mov eax, 384
mov edi, OFFSET entityAggro
rep stosd
; 2. Iterate spriteList and configure entity archetypes
xor ecx, ecx
init_spr_loop:
cmp ecx, spriteCount
jge init_spr_done
mov ebx, ecx
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
mov eax, [ebx].SpriteStruct.flags
test eax, eax
jz fallback_archetype
; Parse flags:
; Byte 0: Entity Type (0=Prop, 1=Guard, 2=Medkit, 3=Ammo, 4=Key, 5=Switch)
; Byte 1: Health (if Guard)
; Byte 2: Speed (1..8)
; Byte 3: Aggro multiplier (1..15 -> cells * 64)
mov edx, eax
and edx, 0FFh
mov [entityType + ecx*4], edx
mov edx, eax
shr edx, 8
and edx, 0FFh
test edx, edx
jz @F
mov [entityHealth + ecx*4], edx
@@:
mov edx, eax
shr edx, 16
and edx, 0FFh
test edx, edx
jz @F
mov [entitySpeed + ecx*4], edx
@@:
mov edx, eax
shr edx, 24
and edx, 0FFh
test edx, edx
jz next_spr_slot
shl edx, 6 ; cells -> raw units
mov [entityAggro + ecx*4], edx
jmp next_spr_slot
fallback_archetype:
; If flags == 0, infer archetype from textureId:
; Slot 11 = Explosive Barrel, 13 = Guard, 14 = Medkit, 15 = Ammo
mov edx, [ebx].SpriteStruct.textureId
cmp edx, 11
jne check_guard_slot
mov [entityType + ecx*4], ENTITY_TYPE_BARREL
jmp next_spr_slot
check_guard_slot:
cmp edx, 13
jne check_medkit_slot
mov [entityType + ecx*4], ENTITY_TYPE_GUARD
mov [entityHealth + ecx*4], 100
mov [entitySpeed + ecx*4], 2
jmp next_spr_slot
check_medkit_slot:
cmp edx, 14
jne check_ammo_slot
mov [entityType + ecx*4], ENTITY_TYPE_MEDKIT
jmp next_spr_slot
check_ammo_slot:
cmp edx, 15
jne next_spr_slot
mov [entityType + ecx*4], ENTITY_TYPE_AMMO
next_spr_slot:
inc ecx
jmp init_spr_loop
init_spr_done:
ret
InitEntities ENDP
; ------------------------------------------------------------------------------
; UpdateEntities: Updates AI, pickups, collision and interactions each tick
; ------------------------------------------------------------------------------
UpdateEntities PROC uses ebx ecx edx esi edi
LOCAL i:DWORD
LOCAL sprX:DWORD, sprY:DWORD
LOCAL deltaX:DWORD, deltaY:DWORD
LOCAL absDX:DWORD, absDY:DWORD
LOCAL distSq:DWORD
LOCAL speed:DWORD
LOCAL stepX:DWORD, stepY:DWORD
LOCAL cellX:DWORD, cellY:DWORD
; If player died and pressed Restart (R)
cmp isPlayerDead, 0
je entities_active
cmp keyRestart, 0
je entities_done
call RestartCurrentRoom
ret
entities_active:
call IsGameplayBlocked
test eax, eax
jnz entities_done
mov i, 0
entity_loop:
call IsGameplayBlocked
test eax, eax
jnz entities_done
mov eax, i
cmp eax, spriteCount
jge entities_done
mov ebx, i
imul ebx, sizeof SpriteStruct
add ebx, OFFSET spriteList
; If sprite is inactive (textureId == 0), skip
mov eax, [ebx].SpriteStruct.textureId
test eax, eax
jz next_entity
mov eax, [ebx].SpriteStruct.x
mov sprX, eax
mov eax, [ebx].SpriteStruct.y
mov sprY, eax
; Distance to player
mov eax, playerX
sub eax, sprX
mov deltaX, eax
cdq
xor eax, edx
sub eax, edx
mov absDX, eax
mov eax, playerY
sub eax, sprY
mov deltaY, eax
cdq
xor eax, edx
sub eax, edx
mov absDY, eax
; distSq = deltaX^2 + deltaY^2
mov eax, deltaX
imul eax, eax
mov ecx, eax
mov eax, deltaY
imul eax, eax
add eax, ecx
mov distSq, eax
mov ecx, i
mov edx, [entityType + ecx*4]
; --------------------------------------------------------------------------
; Medkit Pickup (+25 HP)
; --------------------------------------------------------------------------
cmp edx, ENTITY_TYPE_MEDKIT
jne check_ammo_pickup
cmp absDX, 32
jge next_entity
cmp absDY, 32
jge next_entity
mov eax, playerHealth
cmp eax, playerMaxHealth
jge next_entity
add eax, 25
cmp eax, playerMaxHealth
jle @F
mov eax, playerMaxHealth
@@:
mov playerHealth, eax
mov [ebx].SpriteStruct.textureId, 0 ; Hide item
mov ecx, i
mov [entityType + ecx*4], ENTITY_TYPE_PROP
add playerScore, 25
invoke ShowGameToast, OFFSET szMedkitPickedToast, 75, COLOR_GREEN
jmp next_entity
; --------------------------------------------------------------------------
; Ammo Pickup (+20 Ammo)
; --------------------------------------------------------------------------
check_ammo_pickup:
cmp edx, ENTITY_TYPE_AMMO
jne check_key_pickup
cmp absDX, 32
jge next_entity
cmp absDY, 32
jge next_entity
mov eax, playerAmmo
add eax, 20
cmp eax, playerMaxAmmo
jle @F
mov eax, playerMaxAmmo
@@:
mov playerAmmo, eax
mov [ebx].SpriteStruct.textureId, 0
mov ecx, i
mov [entityType + ecx*4], ENTITY_TYPE_PROP
add playerScore, 10
invoke ShowGameToast, OFFSET szAmmoPickedToast, 75, COLOR_YELLOW
jmp next_entity
; --------------------------------------------------------------------------
; Key Pickup (+1 Key)
; --------------------------------------------------------------------------
check_key_pickup:
cmp edx, ENTITY_TYPE_KEY
jne check_switch_entity
cmp absDX, 32
jge next_entity
cmp absDY, 32
jge next_entity
inc playerKeys
mov [ebx].SpriteStruct.textureId, 0
mov ecx, i
mov [entityType + ecx*4], ENTITY_TYPE_PROP
add playerScore, 50
invoke ShowGameToast, OFFSET szKeyPickedToast, 90, COLOR_CYAN
jmp next_entity
; --------------------------------------------------------------------------
; Switch Trigger (Toggle on [E])
; --------------------------------------------------------------------------
check_switch_entity:
cmp edx, ENTITY_TYPE_SWITCH
jne check_guard_entity
cmp absDX, 48
jge next_entity
cmp absDY, 48
jge next_entity
cmp keyAction, 0
je next_entity
mov keyAction, 0
; Check if handled by user gameplay script hook
invoke Game_OnPlayerInteract, i, edx
test eax, eax
jnz next_entity
; Opt-in authored door binding: byte 1 = A8/A9, X/Y in bytes 2/3.
mov eax, [ebx].SpriteStruct.flags
mov edx, eax
and edx, 0FEFFh
cmp edx, 0A805h
jne legacy_switch_toggle
mov edx, eax
shr edx, 24
shr eax, 16
and eax, 0FFh
invoke ToggleDoorCell, eax, edx
test eax, eax
jz next_entity
; ToggleDoorCell returns the resulting cell, or zero if blocked/invalid.
cmp eax, CELL_DOOR_OPEN
sete al
movzx eax, al
mov ecx, i
mov [entityState + ecx*4], eax
jmp switch_show_state
legacy_switch_toggle:
; Legacy switches still toggle state and run the public gameplay hook.
mov ecx, i
mov eax, [entityState + ecx*4]
xor eax, 1
mov [entityState + ecx*4], eax
switch_show_state:
test eax, eax
jz switch_off
invoke ShowGameToast, OFFSET szSwitchOnToast, 60, COLOR_GREEN
jmp next_entity
switch_off:
invoke ShowGameToast, OFFSET szSwitchOffToast, 60, COLOR_YELLOW
jmp next_entity
; --------------------------------------------------------------------------
; Guard Enemy AI
; --------------------------------------------------------------------------
check_guard_entity:
cmp edx, ENTITY_TYPE_GUARD
jne next_entity
mov ecx, i
mov eax, [entityState + ecx*4]
cmp eax, ENTITY_STATE_DEAD
jne guard_alive
; Dead guard: tick timer until disappear
inc DWORD PTR [entityTimer + ecx*4]
cmp DWORD PTR [entityTimer + ecx*4], 120
jl next_entity
mov [ebx].SpriteStruct.textureId, 0
jmp next_entity
guard_alive:
; Check aggro radius
mov eax, [entityAggro + ecx*4]
imul eax, eax ; aggroSq
cmp distSq, eax
jg next_entity
; Guard is alert & chasing player
mov ecx, i
mov [entityState + ecx*4], ENTITY_STATE_CHASE
mov eax, [entitySpeed + ecx*4]
mov speed, eax
; Calculate movement direction
mov stepX, 0
mov stepY, 0
cmp deltaX, 16
jle check_neg_x
mov eax, speed
mov stepX, eax
jmp check_step_y
check_neg_x:
cmp deltaX, -16
jge check_step_y
mov eax, speed
neg eax
mov stepX, eax
check_step_y:
cmp deltaY, 16
jle check_neg_y
mov eax, speed
mov stepY, eax
jmp apply_guard_step
check_neg_y:
cmp deltaY, -16
jge apply_guard_step
mov eax, speed
neg eax
mov stepY, eax
apply_guard_step:
; Try stepping X
cmp stepX, 0
je try_step_y
mov eax, sprX
add eax, stepX
sar eax, 6
mov cellX, eax
mov eax, sprY
sar eax, 6
mov cellY, eax
invoke GetMapCell, cellX, cellY
invoke IsSolidCell, eax
test eax, eax
jnz try_step_y
mov eax, stepX
add [ebx].SpriteStruct.x, eax
mov eax, [ebx].SpriteStruct.x
mov sprX, eax
try_step_y:
cmp stepY, 0
je check_guard_attack
mov eax, sprX
sar eax, 6
mov cellX, eax
mov eax, sprY
add eax, stepY
sar eax, 6
mov cellY, eax
invoke GetMapCell, cellX, cellY
invoke IsSolidCell, eax
test eax, eax
jnz check_guard_attack
mov eax, stepY
add [ebx].SpriteStruct.y, eax
check_guard_attack:
; Attack range check: within 48 raw units
cmp distSq, 2304 ; 48 * 48
jg next_entity
mov ecx, i
inc DWORD PTR [entityTimer + ecx*4]
cmp DWORD PTR [entityTimer + ecx*4], 35
jl next_entity
mov DWORD PTR [entityTimer + ecx*4], 0
; Guard hits player!
mov eax, playerHealth
sub eax, 8
jns @F
xor eax, eax
@@:
mov playerHealth, eax
mov playerHitFlash, 6
test eax, eax
jnz next_entity
mov isPlayerDead, 1
; Nothing else may collect items or attack after a lethal hit this tick.
jmp entities_done
next_entity:
inc i
jmp entity_loop
entities_done:
ret
UpdateEntities ENDP