MainHud/CombatManager.cs

CombatManager static class for a game, contains damage application, healing for player and AI, and simple RNG helpers for crits and blocks. It applies magic shields, armor mitigation and shatter logic, prestige-based special effects (rebound, counter-strike, defiance), and logs events to GameManager and engine Log.

NetworkingNative Interop
using System;
using System.Collections.Generic;
using Keyboard_Warriors_.Models;
using Keyboard_Warriors_.Systems; // Added to access your EventLog system

namespace Keyboard_Warriors_.Managers;

public static class CombatManager
{
	private static Random rand = new Random();

	public static void applyDamage( ref Player target, int damage )
	{
		Log.Info( $"[TRACE] Incoming Damage: {damage} | Current Armor: {target.defense} | Current HP: {target.hp}" );

		if ( damage <= 0 ) return;

		var manager = Sandbox.Game.ActiveScene.Get<GameManager>();
		bool isPlayer = (manager != null && target == manager.LocalPlayer);
		int prestige = manager != null ? manager.CurrentPrestige : 0;

		if ( manager != null && isPlayer )
		{
			manager.HasPlayerTakenAnyDamage = true;
		}

		if ( target.isMagicBlocking )
		{
			if ( target.activeMagicShield == "Fortify" )
			{
				if ( isPlayer ) GameManager.AddPlayerLog( "  Fortify blocks incoming damage!" );
				else GameManager.AddAILog( "  Fortify blocks incoming damage!" );

				damage = 0;
			}
			else if ( target.activeMagicShield == "Magic Wall" )
			{
				if ( isPlayer ) GameManager.AddPlayerLog( "  Magic Wall reduces incoming damage!" );
				else GameManager.AddAILog( "  Magic Wall reduces incoming damage!" );

				damage /= 2;
				Log.Info( $"[TRACE] After Magic Wall: {damage}" );
			}
		}

		// ARMOR LOGIC
		if ( target.defense > 0 && damage > 0 )
		{
			if ( manager != null && isPlayer && target.defense == 100 )
			{
				if ( manager.CurrentPrestige > 0 && target.defense < manager.LocalPlayer.maxDefense )
				{
					Log.Info( $"[FIX] Desync detected! Restoring Prestige {manager.CurrentPrestige} Armor from 100 to {manager.LocalPlayer.maxDefense}" );
					target.defense = manager.LocalPlayer.maxDefense;
				}
			}

			// Cache the target's current armor pool into a temporary calculation register
			int workingTargetDefense = target.defense;

			// 🚀 THE PERK INTERCEPT: Evaluate who the attacker is to apply armor-shredding parameters safely
			bool isAttackerHuman = (manager != null && target == manager.AIEnemy);

			if ( isAttackerHuman && manager.LocalPlayer.Perks != null && manager.LocalPlayer.Perks.IgnoresEnemyArmor )
			{
				workingTargetDefense = (int)(workingTargetDefense * 0.5f);
			}
			else if ( !isAttackerHuman && manager != null && manager.AIEnemy.Perks != null && manager.AIEnemy.Perks.IgnoresEnemyArmor )
			{
				workingTargetDefense = (int)(workingTargetDefense * 0.5f);
			}

			// 🛡️ LAYER 1: Inherent Armor Hardening (Armor takes 15% less damage inherently)
			int hardenedArmorDamage = (int)(damage * 0.85f);
			if ( hardenedArmorDamage < 1 && damage > 0 ) hardenedArmorDamage = 1;

			// The incoming hit checks against the perk-mitigated defense window safely
			if ( hardenedArmorDamage <= workingTargetDefense )
			{
				// The armor completely holds the impact! Deduct points from their true sheet
				target.defense = System.Math.Max( 0, target.defense - hardenedArmorDamage );
				damage = 0; // No damage spills over to the green HP bar

				Log.Info( $"[TRACE] Armor hardened (15% reduction). Absorbed: {hardenedArmorDamage} | Remaining Armor: {target.defense}" );

				if ( isPlayer )
				{
					GameManager.AddPlayerLog( $"🛡️ {target.name}'s armor hardened the impact! Shield absorbed {hardenedArmorDamage} damage." );
					if ( prestige >= 6 )
					{
						// 20% base at P6, climbing +3% per prestige up to 32% at P10
						int reboundChance = System.Math.Clamp( 20 + ((prestige - 6) * 3), 20, 35 );
						if ( Random.Shared.Next( 100 ) < reboundChance )
						{
							// Calculate 50% of the blocked damage to punch back into the AI
							int reboundDamage = (int)(hardenedArmorDamage * 0.5f);
							if ( reboundDamage < 1 ) reboundDamage = 1;

							// Flashes an emerald light bloom indicating success
							GameManager.AddButtonFeedbackLog( $"⚡ KINETIC REBOUND! ({reboundChance}% Success) Your shields flip the energy signature, bouncing {reboundDamage} damage right back to the AI Overlord out-of-turn!", isSuccess: true );

							// Punish the AI Overlord out of turn directly
							if ( manager != null && manager.AIEnemy != null )
							{
								var aiRef = manager.AIEnemy;
								aiRef.hp = System.Math.Max( 0, aiRef.hp - reboundDamage );
							}
						}
					}
				}
				else GameManager.AddAILog( $"🛡️ {target.name}'s armor hardened the impact! Shield absorbed {hardenedArmorDamage} damage." );
			}
			else
			{
				// 🛡️ LAYER 2: Kinetic Shield-Gate (Armor completely shatters, overflow cut by 50%)
				int rawDamageAbsorbedByPlating = (int)System.MathF.Ceiling( target.defense / 0.85f );

				int rawOverflowDamage = damage - rawDamageAbsorbedByPlating;
				if ( rawOverflowDamage < 0 ) rawOverflowDamage = 0;

				// Spillover protection rule kicks in to protect skin and bones
				int finalHPDamage = (int)(rawOverflowDamage * 0.5f);
				if ( finalHPDamage < 1 && rawOverflowDamage > 0 ) finalHPDamage = 1;

				Log.Info( $"[TRACE] Armor shattered! Raw overflow: {rawOverflowDamage} -> Mitigated HP damage: {finalHPDamage}" );

				if ( isPlayer ) GameManager.AddPlayerLog( $"🚨 ARMOR SHATTERED! {target.name}'s metal plating crushed! Overflow damage cut in half: -{finalHPDamage} HP." );
				else GameManager.AddAILog( $"🚨 ARMOR SHATTERED! {target.name}'s metal plating crushed! Overflow damage cut in half: -{finalHPDamage} HP." );

				target.defense = 0; // Armor is entirely stripped
				damage = finalHPDamage; // Pass the mitigated remainder down to the HP subtraction lines below!
			}
		}

		// HP LOGIC
		if ( damage > 0 )
		{

			prestige = manager != null ? manager.CurrentPrestige : 0;

			if ( isPlayer && (manager?.CombatLogs?.Count > 0) && prestige >= 4 )
			{
				var latestLog = manager.CombatLogs[manager.CombatLogs.Count - 1];
				if ( latestLog.AnimationClass == "flash-crit" )
				{
					// Base 15% chance at Prestige 4, climbing by +5% per level up to a hard 45% maximum cap at Prestige 10!
					int critCounterChance = System.Math.Clamp( 15 + ((prestige - 4) * 5), 15, 45 );
					bool isCounterTriggered = Random.Shared.Next( 100 ) < critCounterChance;

					if ( isCounterTriggered )
					{
						int counterDamage = 5 + (prestige * 3); // Base 5 + 3 per prestige tier

						GameManager.AddButtonFeedbackLog( $"👾 MALWARE COUNTER-STRIKE ACTIVE! ({critCounterChance}% Success) Your background scripts detected the critical breach and retaliated by blasting {counterDamage} virus damage back to the AI frame!", isSuccess: true );

						if ( manager != null && manager.AIEnemy != null )
						{
							var aiRef = manager.AIEnemy;
							aiRef.hp = System.Math.Max( 0, aiRef.hp - counterDamage );
						}
					}
					else
					{
						Log.Info( $"[MALWARE TRAFFIC] Passive counter script failed the transmission check. Roll missed the {critCounterChance}% probability window." );
					}
				}
			}

			// --- BRANCH 1: PLAYER DEFIANCE CHECK ---
			if ( isPlayer && (target.hp - damage) <= 0 && prestige >= 3 && !manager.HasDefiedDeathThisMatch )
			{
				// Player Scales: 10% base at P3, climbing up to a hard 25% max cap at P10
				int playerChance = System.Math.Clamp( 10 + ((prestige - 3) * 2), 10, 25 );
				if ( Random.Shared.Next( 100 ) < playerChance )
				{
					target.hp = 1;
					manager.HasDefiedDeathThisMatch = true; // Consumed

					// 🔵 PLAYER HIGHLIGHT: Emerald light bloom alerts your kernel recovery
					GameManager.AddButtonFeedbackLog( $"🚨 PRESTIGE DEFIANCE MATRIX INJECTED! ({playerChance}% Success) Restored emergency player files! Clamped physical health grid at 1 HP!", isSuccess: true );
					return;
				}
			}

			// --- BRANCH 2: AI OVERLORD DEFIANCE CHECK ---
			if ( !isPlayer && (target.hp - damage) <= 0 && prestige >= 3 && !manager.HasAIDefiedDeathThisMatch )
			{
				// AI Scales with its massive edge: 15% base at P3, climbing up to a 50% max cap at P10
				int aiChance = System.Math.Clamp( 15 + ((prestige - 3) * 5), 15, 50 );
				if ( Random.Shared.Next( 100 ) < aiChance )
				{
					target.hp = 1;
					manager.HasAIDefiedDeathThisMatch = true; // Consumed

					// 💥 FLASH CRIT ANNOTATION: Strobe flashes a malicious red emergency core restart alert!
					GameManager.AddCriticalLog( $"🚨 WARNING: AI MAINFRAME BACKUP INJECTED! ({aiChance}% Success) Overlord executed emergency hardware reboot! AI hull locked at 1 HP!", isPlayer: false );
					return;
				}
			}

			target.hp -= damage;
			Log.Info( $"[TRACE] HP reduced by {damage} | New HP: {target.hp}" );

			if ( isPlayer ) GameManager.AddPlayerLog( $"{target.name} takes {damage} HP damage!" );
			else GameManager.AddAILog( $"{target.name} takes {damage} HP damage!" );
		}

		if ( target.hp < 0 ) target.hp = 0;
	}

	public static void healPlayer( ref Player p, Weapon w )
	{
		var manager = Sandbox.Game.ActiveScene.Get<GameManager>();
		int prestige = manager != null ? manager.CurrentPrestige : 0;

		// Calculate prestige scaling: Base 10 + 3 extra HP per prestige level + weapon bonus
		int scaledHealAmount = 10 + (prestige * 3) + w.healBonus;
		p.hp += scaledHealAmount;

		if ( manager != null && manager.LocalPlayer != null )
		{
			// Dynamic Cap: Clamp to your prestige-scaled maximum health
			if ( p.hp > manager.LocalPlayer.maxHp )
			{
				p.hp = manager.LocalPlayer.maxHp;
			}
		}
		else
		{
			if ( p.hp > 100 ) p.hp = 100;
		}

		Log.Info( $"[TRACE HEAL] Player healed for {scaledHealAmount}. Current HP: {p.hp}/{manager?.LocalPlayer?.maxHp ?? 100}" );
	}

	public static void healAI( ref Player ai, Weapon w )
	{
		var manager = Sandbox.Game.ActiveScene.Get<GameManager>();
		int prestige = manager != null ? manager.CurrentPrestige : 0;

		// Calculate prestige scaling: Base 10 + 4 extra HP per prestige level + weapon bonus
		int scaledHealAmount = 10 + (prestige * 4) + w.healBonus;
		ai.hp += scaledHealAmount;

		if ( manager != null && manager.AIEnemy != null )
		{
			// Dynamic Cap: Clamp to the AI's prestige-scaled maximum health
			if ( ai.hp > manager.AIEnemy.maxHp )
			{
				ai.hp = manager.AIEnemy.maxHp;
			}
		}
		else
		{
			if ( ai.hp > 100 ) ai.hp = 100;
		}

		Log.Info( $"[TRACE HEAL] AI healed for {scaledHealAmount}. Current HP: {ai.hp}/{manager?.AIEnemy?.maxHp ?? 100}" );
	}

	public static bool isCriticalHit()
	{
		return rand.Next( 100 ) < 5;
	}

	public static bool attemptBlock( float modifier = 1.0f )
	{
		return rand.Next( 100 ) < (int)(50 * modifier);
	}
}