; ============================================================================== ; EBLAN ENGINE - Player Combat, Weapon Shooting & Combat HUD ; ============================================================================== .code DrawPixel PROTO :DWORD, :DWORD, :DWORD DrawRect PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD DrawRectOutline PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD DrawString8x8 PROTO :DWORD, :DWORD, :DWORD, :DWORD DrawNumber8x8 PROTO :DWORD, :DWORD, :DWORD, :DWORD ; Trace the current world along the player's forward direction. EAX = first ; blocking boundary in 24.8 world units. Glass blocks shots (unlike rendering). ; No framebuffer/depth-buffer dependencies. Out-of-map cells are solid. TraceShotWall PROC uses ebx ecx edx esi edi LOCAL cellX:DWORD, cellY:DWORD, stepX:DWORD, stepY:DWORD LOCAL deltaX:DWORD, deltaY:DWORD, sideX:DWORD, sideY:DWORD LOCAL hitDistance:DWORD, remaining:DWORD mov eax, playerX sar eax, 6 mov cellX, eax mov eax, playerY sar eax, 6 mov cellY, eax invoke GetMapCell, cellX, cellY invoke IsSolidCell, eax test eax, eax jnz shot_wall_at_origin mov eax, playerAngle and eax, ANGLE_MASK mov esi, [tableCos+eax*4] mov edi, [tableSin+eax*4] mov deltaX, 3FFFFFFFh mov sideX, 3FFFFFFFh mov stepX, 1 test esi, esi jz shot_setup_y mov eax, esi cdq xor eax, edx sub eax, edx mov ecx, eax mov eax, (64 shl 24) xor edx, edx div ecx mov deltaX, eax mov eax, playerX and eax, 63 test esi, esi js shot_negative_x neg eax add eax, 64 jmp shot_side_x shot_negative_x: mov stepX, -1 shot_side_x: imul deltaX shrd eax, edx, 6 mov sideX, eax shot_setup_y: mov deltaY, 3FFFFFFFh mov sideY, 3FFFFFFFh mov stepY, 1 test edi, edi jz shot_traverse_start mov eax, edi cdq xor eax, edx sub eax, edx mov ecx, eax mov eax, (64 shl 24) xor edx, edx div ecx mov deltaY, eax mov eax, playerY and eax, 63 test edi, edi js shot_negative_y neg eax add eax, 64 jmp shot_side_y shot_negative_y: mov stepY, -1 shot_side_y: imul deltaY shrd eax, edx, 6 mov sideY, eax shot_traverse_start: mov remaining, MAP_W+MAP_H+2 shot_traverse: mov eax, sideX cmp eax, sideY jb shot_step_x jne shot_step_y ; At a corner also test the X-adjacent cell: no shots through wall seams. mov ebx, cellX add ebx, stepX invoke GetMapCell, ebx, cellY invoke IsSolidCell, eax test eax, eax jz shot_step_y mov eax, sideX ret shot_step_x: mov hitDistance, eax add eax, deltaX mov sideX, eax mov eax, stepX add cellX, eax jmp shot_test_cell shot_step_y: mov eax, sideY mov hitDistance, eax add eax, deltaY mov sideY, eax mov eax, stepY add cellY, eax shot_test_cell: invoke GetMapCell, cellX, cellY invoke IsSolidCell, eax test eax, eax jnz shot_wall_found dec remaining jnz shot_traverse ; Defensive bound: a valid ray exits a 32x32 map within 64 crossings. shot_wall_found: mov eax, hitDistance ret shot_wall_at_origin: xor eax, eax ret TraceShotWall ENDP ; ------------------------------------------------------------------------------ ; UpdateCombat: Process weapon firing, hitscan raycasting, cooldowns & damage ; ------------------------------------------------------------------------------ UpdateCombat PROC uses ebx ecx edx esi edi LOCAL camCos:DWORD, camSin:DWORD LOCAL i:DWORD, closestIdx:DWORD, closestDist:DWORD LOCAL sprX:DWORD, sprY:DWORD LOCAL transX:DWORD, transY:DWORD LOCAL targetHalfWidth:DWORD, wallDepth:DWORD call IsGameplayBlocked test eax, eax jnz combat_done ; 1. Decrement cooldowns cmp playerAttackCooldown, 0 je @F dec playerAttackCooldown @@: cmp playerFlashTimer, 0 je @F dec playerFlashTimer @@: cmp playerHitFlash, 0 je @F dec playerHitFlash @@: ; If player is dead, cannot attack cmp isPlayerDead, 0 jne combat_done ; 2. Check attack input cmp keyAttack, 0 je combat_done cmp playerAttackCooldown, 0 jne combat_done ; Check ammo cmp playerAmmo, 0 jle combat_dry_fire ; Fire weapon! dec playerAmmo mov playerAttackCooldown, 10 mov playerFlashTimer, 5 ; 3. Hitscan check against enemies in center crosshair ; Camera basis for -playerAngle mov eax, ANGLE_512 sub eax, playerAngle and eax, ANGLE_MASK mov ecx, [tableCos + eax*4] mov camCos, ecx mov edx, [tableSin + eax*4] mov camSin, edx ; Query current geometry, never the previous rendered frame. call TraceShotWall mov wallDepth, eax mov closestIdx, -1 mov closestDist, 999999 mov i, 0 hitscan_loop: mov eax, i cmp eax, spriteCount jge hitscan_eval mov ecx, i mov eax, [entityType + ecx*4] cmp eax, ENTITY_TYPE_GUARD jne next_hitscan_target mov eax, [entityState + ecx*4] cmp eax, ENTITY_STATE_DEAD je next_hitscan_target mov ebx, i imul ebx, sizeof SpriteStruct add ebx, OFFSET spriteList mov eax, [ebx].SpriteStruct.textureId test eax, eax jz next_hitscan_target ; Transform to camera space mov eax, [ebx].SpriteStruct.x sub eax, playerX mov sprX, eax mov eax, [ebx].SpriteStruct.y sub eax, playerY mov sprY, eax ; transY = sprX * camCos - sprY * camSin mov eax, sprX imul camCos shrd eax, edx, 16 mov ecx, eax mov eax, sprY imul camSin shrd eax, edx, 16 sub ecx, eax mov transY, ecx ; Must be in front of player cmp transY, 16 jle next_hitscan_target ; Compare in 24.8 so a target on a wall boundary is not hit through it. mov eax, transY shl eax, 8 cmp eax, wallDepth jge next_hitscan_target ; transX = sprX * camSin + sprY * camCos mov eax, sprX imul camSin shrd eax, edx, 16 mov ecx, eax mov eax, sprY imul camCos shrd eax, edx, 16 add ecx, eax mov transX, ecx ; World-space billboard hitbox: 32 units wide at scale 1.0, scaled with ; the authored sprite, never with screen distance. Zero means legacy 1.0. mov eax, [ebx].SpriteStruct.scale test eax, eax jnz @F mov eax, FIXED_ONE @@: imul eax, 16 sar eax, 16 mov targetHalfWidth, eax mov eax, transX cdq xor eax, edx sub eax, edx cmp eax, targetHalfWidth jg next_hitscan_target ; Check if closer than previous target mov eax, transY cmp eax, closestDist jge next_hitscan_target mov closestDist, eax mov eax, i mov closestIdx, eax next_hitscan_target: inc i jmp hitscan_loop hitscan_eval: cmp closestIdx, -1 je combat_done ; Apply damage to hit guard mov ecx, closestIdx mov eax, [entityHealth + ecx*4] sub eax, 35 jns @F xor eax, eax @@: mov [entityHealth + ecx*4], eax test eax, eax jnz combat_done ; Guard died! mov [entityState + ecx*4], ENTITY_STATE_DEAD mov DWORD PTR [entityTimer + ecx*4], 0 add playerScore, 100 ; Drop 10 ammo reward mov eax, playerAmmo add eax, 10 cmp eax, playerMaxAmmo jle @F mov eax, playerMaxAmmo @@: mov playerAmmo, eax jmp combat_done combat_dry_fire: mov playerAttackCooldown, 12 combat_done: ret UpdateCombat ENDP ; ------------------------------------------------------------------------------ ; RenderCombatHUD: Renders health bar, ammo, crosshair, weapon & death screen ; ------------------------------------------------------------------------------ RenderCombatHUD PROC uses ebx ecx edx esi edi LOCAL barW:DWORD, gunY:DWORD ; 1. Center Crosshair (subtle cross at 160, 120) invoke DrawRect, 156, 120, 9, 1, 00FFFFFFh invoke DrawRect, 160, 116, 1, 9, 00FFFFFFh invoke DrawRect, 160, 120, 1, 1, 0000FF00h ; 2. Render Retro Weapon at bottom center mov gunY, 192 cmp playerFlashTimer, 0 je draw_gun_barrel ; Recoil offset add gunY, 3 ; Muzzle Flash Burst (Bright yellow & white star) invoke DrawRect, 157, 165, 7, 18, 00FFFF00h invoke DrawRect, 152, 170, 17, 8, 00FFAA00h invoke DrawRect, 158, 168, 5, 12, 00FFFFFFh draw_gun_barrel: ; Weapon barrel & slide invoke DrawRect, 154, gunY, 13, 28, 003A3A3Ah invoke DrawRect, 156, gunY, 9, 28, 00555555h invoke DrawRect, 158, gunY, 5, 28, 00777777h ; highlight strip ; Gun frame & grip mov eax, gunY add eax, 24 invoke DrawRect, 150, eax, 21, 24, 00252525h mov eax, gunY add eax, 26 invoke DrawRect, 152, eax, 17, 22, 00453020h ; brown wood grip plates ; 3. Health Bar invoke DrawString8x8, 8, 42, OFFSET strHpLabel, COLOR_GREEN invoke DrawNumber8x8, 34, 42, playerHealth, COLOR_WHITE invoke DrawRect, 68, 42, 60, 8, 00441010h invoke DrawRectOutline, 68, 42, 60, 8, COLOR_WHITE mov eax, playerHealth imul eax, 58 xor edx, edx mov ecx, playerMaxHealth test ecx, ecx jnz @F mov ecx, 100 @@: div ecx mov barW, eax cmp barW, 0 jle hud_ammo_draw cmp barW, 58 jle @F mov barW, 58 @@: mov edx, 0000CC33h ; Green cmp playerHealth, 30 jg @F mov edx, 00FF2222h ; Red warning @@: invoke DrawRect, 69, 43, barW, 6, edx hud_ammo_draw: ; 4. Ammo Display invoke DrawString8x8, 140, 42, OFFSET strAmmoLabel, COLOR_YELLOW invoke DrawNumber8x8, 182, 42, playerAmmo, COLOR_YELLOW ; 5. Score Display invoke DrawString8x8, 222, 42, OFFSET strScoreLabel, COLOR_CYAN invoke DrawNumber8x8, 272, 42, playerScore, COLOR_CYAN ; 6. Keys (if collected) cmp playerKeys, 0 jle hud_damage_flash invoke DrawString8x8, 222, 10, OFFSET strKeysLabel, COLOR_MAGENTA invoke DrawNumber8x8, 258, 10, playerKeys, COLOR_MAGENTA hud_damage_flash: ; 7. Damage Screen Flash cmp playerHitFlash, 0 jle hud_death_check ; Red border vignette invoke DrawRect, 0, 0, SCREEN_W, 3, 00FF0000h invoke DrawRect, 0, 237, SCREEN_W, 3, 00FF0000h invoke DrawRect, 0, 0, 3, SCREEN_H, 00FF0000h invoke DrawRect, 317, 0, 3, SCREEN_H, 00FF0000h hud_death_check: ; 8. Death Overlay Screen cmp isPlayerDead, 0 je combat_hud_done invoke DrawRect, 20, 95, 280, 50, 00880000h invoke DrawRectOutline, 20, 95, 280, 50, COLOR_WHITE invoke DrawString8x8, 32, 114, OFFSET strYouDied, COLOR_YELLOW combat_hud_done: ret RenderCombatHUD ENDP