; ============================================================================== ; EBLAN ENGINE - Entity & Gameplay Behavior System ; ============================================================================== .code ; ------------------------------------------------------------------------------ ; InitEntities: Initialize entity runtime state from spriteList flags/defaults ; ------------------------------------------------------------------------------ InitEntities PROC uses eax ebx ecx edx esi edi ; 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 13 = Enemy Guard, 14 = Medkit, 15 = Ammo mov edx, [ebx].SpriteStruct.textureId 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 ; Respawn player mov playerHealth, 100 mov playerAmmo, 50 mov isPlayerDead, 0 mov playerHitFlash, 0 call LoadMapBinary call InitEntities ret entities_active: mov i, 0 entity_loop: 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 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 jmp next_entity ; -------------------------------------------------------------------------- ; Key Pickup (+1 Key) ; -------------------------------------------------------------------------- check_key_pickup: cmp edx, ENTITY_TYPE_KEY jne check_guard_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 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 next_entity: inc i jmp entity_loop entities_done: ret UpdateEntities ENDP