; ============================================================================== ; EBLAN ENGINE - Player Combat, Multi-Weapon Arsenal & 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 ShowGameToast PROTO :DWORD, :DWORD, :DWORD Game_OnEntityKilled PROTO :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 LOCAL weaponDamage:DWORD, maxHitDist:DWORD, hitHalfWidth: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, game is paused, or in peaceful adventure mode, cannot attack cmp isPlayerDead, 0 jne combat_done cmp isGamePaused, 0 jne combat_done cmp gameMode, GAME_MODE_ADVENTURE je combat_done cmp enableWeapons, 0 je combat_done cmp playerWeapon, WEAPON_NONE je combat_done ; 2. Check attack input cmp keyAttack, 0 je combat_done cmp playerAttackCooldown, 0 jne combat_done ; Set weapon parameters based on playerWeapon mov weaponDamage, 35 mov maxHitDist, 999999 mov hitHalfWidth, 16 cmp playerWeapon, WEAPON_KNIFE je setup_knife cmp playerWeapon, WEAPON_SHOTGUN je setup_shotgun jmp setup_pistol setup_knife: ; Knife: no ammo needed, close range (within 64 units = 1 cell), fast cooldown mov weaponDamage, 30 mov maxHitDist, 64 mov hitHalfWidth, 24 mov playerAttackCooldown, 8 mov playerFlashTimer, 3 jmp fire_hitscan setup_shotgun: ; Shotgun: needs 2 ammo, high damage (70), wider world hitbox, long cooldown cmp playerAmmo, 2 jl combat_dry_fire sub playerAmmo, 2 mov weaponDamage, 70 mov maxHitDist, 999999 mov hitHalfWidth, 32 mov playerAttackCooldown, 22 mov playerFlashTimer, 7 invoke SpawnMuzzleFlashLight, playerX, playerY jmp fire_hitscan setup_pistol: ; Pistol: needs 1 ammo, accurate world hitbox, medium cooldown (12) cmp playerAmmo, 1 jl combat_dry_fire dec playerAmmo mov weaponDamage, 35 mov maxHitDist, 999999 mov hitHalfWidth, 16 mov playerAttackCooldown, 12 mov playerFlashTimer, 5 invoke SpawnMuzzleFlashLight, playerX, playerY fire_hitscan: ; 3. Hitscan check against enemies & destructible barrels in 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] ; Check if Guard or Barrel cmp eax, ENTITY_TYPE_GUARD je hitscan_target_valid cmp eax, ENTITY_TYPE_BARREL je hitscan_target_valid jmp next_hitscan_target hitscan_target_valid: 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 ; Must be closer than maximum weapon hit distance mov eax, transY cmp eax, maxHitDist jg 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: weapon-specific width, 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, hitHalfWidth 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 mov ecx, closestIdx mov eax, [entityType + ecx*4] ; Check if hit an explosive barrel! cmp eax, ENTITY_TYPE_BARREL je explode_barrel ; Hit an enemy guard! mov eax, [entityHealth + ecx*4] sub eax, weaponDamage 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 invoke Game_OnEntityKilled, closestIdx, ENTITY_TYPE_GUARD ; Drop ammo reward mov eax, playerAmmo add eax, 10 cmp eax, playerMaxAmmo jle @F mov eax, playerMaxAmmo @@: mov playerAmmo, eax jmp combat_done explode_barrel: ; Exploding Barrel: Destroy barrel and trigger AOE blast! mov [entityState + ecx*4], ENTITY_STATE_DEAD mov ebx, closestIdx imul ebx, sizeof SpriteStruct add ebx, OFFSET spriteList mov [ebx].SpriteStruct.textureId, 0 ; disappear ; Fiery orange-red screen flash mov playerHitFlash, 8 add playerScore, 200 invoke ShowGameToast, OFFSET szBarrelExplodedToast, 60, 00FFA020h invoke Game_OnEntityKilled, closestIdx, ENTITY_TYPE_BARREL ; AOE damage to all surrounding guards within 128 units (2 cells) push esi push edi mov esi, [ebx].SpriteStruct.x mov edi, [ebx].SpriteStruct.y invoke SpawnExplosionLight, esi, edi mov i, 0 aoe_loop: mov eax, i cmp eax, spriteCount jge aoe_check_player mov ecx, i cmp ecx, closestIdx je next_aoe mov eax, [entityType + ecx*4] cmp eax, ENTITY_TYPE_GUARD jne next_aoe cmp [entityState + ecx*4], ENTITY_STATE_DEAD je next_aoe ; EDX is volatile scratch for CDQ below, not a safe sprite pointer. mov ebx, i imul ebx, sizeof SpriteStruct add ebx, OFFSET spriteList mov eax, [ebx].SpriteStruct.x sub eax, esi cdq xor eax, edx sub eax, edx cmp eax, 128 jg next_aoe mov eax, [ebx].SpriteStruct.y sub eax, edi cdq xor eax, edx sub eax, edx cmp eax, 128 jg next_aoe ; Within blast: deal 60 blast damage! mov eax, [entityHealth + ecx*4] sub eax, 60 jns @F xor eax, eax @@: mov [entityHealth + ecx*4], eax test eax, eax jnz next_aoe mov [entityState + ecx*4], ENTITY_STATE_DEAD add playerScore, 150 next_aoe: inc i jmp aoe_loop aoe_check_player: ; If player was standing too close to the barrel (< 96 units), player takes blast damage! mov eax, playerX sub eax, esi cdq xor eax, edx sub eax, edx cmp eax, 96 jg aoe_done mov eax, playerY sub eax, edi cdq xor eax, edx sub eax, edx cmp eax, 96 jg aoe_done ; Player blast damage mov eax, playerHealth sub eax, 25 jns @F xor eax, eax @@: mov playerHealth, eax test eax, eax jnz aoe_done mov isPlayerDead, 1 aoe_done: pop edi pop esi 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 ; If in adventure mode, unarmed, or weapons disabled, skip weapon & combat stats cmp gameMode, GAME_MODE_ADVENTURE je hud_damage_flash cmp enableWeapons, 0 je hud_damage_flash cmp playerWeapon, WEAPON_NONE je hud_damage_flash ; 1. Center Crosshair (subtle cross at 160, 120) cmp showCrosshair, 0 je skip_crosshair invoke DrawRect, 156, 120, 9, 1, 00FFFFFFh invoke DrawRect, 160, 116, 1, 9, 00FFFFFFh invoke DrawRect, 160, 120, 1, 1, 0000FF00h skip_crosshair: ; 2. Render Active Weapon at bottom center cmp playerWeapon, WEAPON_KNIFE je render_knife_weapon cmp playerWeapon, WEAPON_SHOTGUN je render_shotgun_weapon jmp render_pistol_weapon render_knife_weapon: ; Knife Combat Animation: thrusts forward when cooldown active mov gunY, 190 cmp playerFlashTimer, 0 je draw_knife sub gunY, 12 ; thrust forward! draw_knife: ; Silver steel blade invoke DrawRect, 166, gunY, 6, 26, 00D0D0D0h invoke DrawRect, 168, gunY, 2, 26, 00FFFFFFh ; shine invoke DrawRect, 167, gunY - 4, 4, 4, 00B0B0B0h ; blade tip invoke DrawRect, 168, gunY - 7, 2, 3, 00E0E0E0h ; Brass crossguard mov eax, gunY add eax, 26 invoke DrawRect, 160, eax, 18, 5, 00D4AF37h ; Wooden grip mov eax, gunY add eax, 31 invoke DrawRect, 164, eax, 10, 22, 00502A10h jmp hud_stats_draw render_shotgun_weapon: ; Heavy Pump Shotgun mov gunY, 188 cmp playerFlashTimer, 0 je draw_shotgun_barrels add gunY, 4 ; heavy recoil! ; Massive dual blast flash! invoke DrawRect, 146, 155, 28, 22, 00FFAA00h invoke DrawRect, 150, 158, 20, 16, 00FFFF00h invoke DrawRect, 154, 162, 12, 10, 00FFFFFFh draw_shotgun_barrels: ; Twin heavy steel barrels invoke DrawRect, 148, gunY, 10, 32, 002B2B2Bh invoke DrawRect, 150, gunY, 6, 32, 00484848h invoke DrawRect, 162, gunY, 10, 32, 002B2B2Bh invoke DrawRect, 164, gunY, 6, 32, 00484848h ; Barrel rib connector invoke DrawRect, 158, gunY, 4, 32, 00222222h ; Wooden pump forend mov eax, gunY add eax, 20 invoke DrawRect, 144, eax, 32, 18, 00603818h invoke DrawRect, 146, eax, 28, 14, 007A4820h jmp hud_stats_draw render_pistol_weapon: mov gunY, 192 cmp playerFlashTimer, 0 je draw_pistol_barrel add gunY, 3 ; recoil offset ; 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_pistol_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 hud_stats_draw: ; 3. Health Bar at top-left: X=8, Y=8 invoke DrawString8x8, 8, 8, OFFSET strHpLabel, COLOR_GREEN invoke DrawNumber8x8, 34, 8, playerHealth, COLOR_WHITE invoke DrawRect, 66, 8, 50, 8, 00441010h invoke DrawRectOutline, 66, 8, 50, 8, COLOR_WHITE mov eax, playerHealth imul eax, 48 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, 48 jle @F mov barW, 48 @@: mov edx, 0000CC33h ; Green cmp playerHealth, 30 jg @F mov edx, 00FF2222h ; Red warning @@: invoke DrawRect, 67, 9, barW, 6, edx hud_ammo_draw: ; 4. Ammo Display: X=126, Y=8 invoke DrawString8x8, 126, 8, OFFSET strAmmoLabel, COLOR_YELLOW invoke DrawNumber8x8, 168, 8, playerAmmo, COLOR_YELLOW ; 5. Keys: X=204, Y=8 (if collected) cmp playerKeys, 0 jle hud_score_draw invoke DrawString8x8, 204, 8, OFFSET strKeysLabel, COLOR_MAGENTA invoke DrawNumber8x8, 238, 8, playerKeys, COLOR_MAGENTA hud_score_draw: ; 6. Score Display: only in Debug HUD style cmp hudStyle, HUD_STYLE_CLEAN je draw_weapon_badge invoke DrawString8x8, 8, 20, OFFSET strScoreLabel, COLOR_CYAN invoke DrawNumber8x8, 58, 20, playerScore, COLOR_CYAN draw_weapon_badge: ; 7. Active Weapon Badge at bottom-right corner: X=228, Y=222 invoke DrawRect, 228, 222, 88, 14, 00141414h invoke DrawRectOutline, 228, 222, 88, 14, 00444444h cmp playerWeapon, WEAPON_KNIFE jne @F invoke DrawString8x8, 233, 225, OFFSET strWeaponKnife, COLOR_CYAN jmp draw_weapon_slots @@: cmp playerWeapon, WEAPON_SHOTGUN jne @F invoke DrawString8x8, 233, 225, OFFSET strWeaponShotgun, 00FFAA00h jmp draw_weapon_slots @@: invoke DrawString8x8, 233, 225, OFFSET strWeaponPistol, COLOR_YELLOW draw_weapon_slots: ; Draw small weapon slot indicator boxes: [1] [2] [3] invoke DrawRectOutline, 287, 225, 6, 8, 00555555h invoke DrawRectOutline, 295, 225, 6, 8, 00555555h invoke DrawRectOutline, 303, 225, 6, 8, 00555555h ; Highlight active slot mov eax, playerWeapon imul eax, 8 add eax, 287 invoke DrawRectOutline, eax, 225, 6, 8, COLOR_YELLOW hud_damage_flash: ; 8. 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: ; 9. 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 ; ------------------------------------------------------------------------------ ; RenderPauseMenu: Renders stylish retro pause overlay when isGamePaused != 0 ; ------------------------------------------------------------------------------ RenderPauseMenu PROC uses ebx ecx edx cmp isGamePaused, 0 je pause_menu_done ; Centered semi-transparent panel 220x112 at (50, 64) invoke DrawRect, 50, 64, 220, 112, 00141416h invoke DrawRectOutline, 50, 64, 220, 112, COLOR_CYAN invoke DrawRectOutline, 52, 66, 216, 108, COLOR_WHITE ; Header invoke DrawString8x8, 112, 72, OFFSET strPauseTitle, COLOR_YELLOW ; Menu Options invoke DrawString8x8, 64, 88, OFFSET strPauseOpt1, COLOR_GREEN invoke DrawString8x8, 64, 102, OFFSET strPauseOpt2, COLOR_WHITE invoke DrawString8x8, 64, 116, OFFSET strPauseOpt3, COLOR_CYAN invoke DrawString8x8, 64, 130, OFFSET strPauseOpt4, COLOR_YELLOW invoke DrawString8x8, 64, 146, OFFSET strPauseOpt5, 00FF4444h pause_menu_done: ret RenderPauseMenu ENDP