swb_player/PlayerBase.Health.cs

using Sandbox.Sboku.Arena;
using SWB.Base;
using SWB.Shared;
using System;
using System.Collections.Generic;

namespace SWB.Player;

public partial class PlayerBase
{
	[Property] public int MaxHealth { get; set; } = 100;
	[Property] public float DamageThrottle { get; set; } = 0.3f;
	[Property] public int MaxDamageClamp { get; set; } = 15;
	[Sync] public int Health { get; set; } = 100;
	[Sync] public int Kills { get; set; }
	[Sync] public int Deaths { get; set; }
	[Sync] public bool GodMode { get; set; }
    [Sync] public bool IsDead { get; set; }
    
    public bool IsAlive => !IsDead;
	
	public Dictionary<string, DamageEntry> dmgTable = new();
	public sealed class DamageEntry
    {
        public Shared.DamageInfo Info { get; }
        public int Damage { get; set; }
        public float Since { get; set; }

        public DamageEntry(Shared.DamageInfo info)
        {
            Info = info;
            Damage = 0;
            Since = 0f;
        }
    }

	private void HandleDamage()
	{
		var remove = new List<string>();

		foreach (var entry in dmgTable)
		{
			entry.Value.Since += Time.Delta;
			if (entry.Value.Since > DamageThrottle)
			{
				InflictDamage(entry.Value.Info, entry.Value.Damage);
				remove.Add(entry.Key);
			}
		}

		remove.ForEach(x => dmgTable.Remove(x));
    }

    private void InflictDamage(Shared.DamageInfo info, int dmg)
	{
        Health -= dmg;
        // Flinch
        var weapon = WeaponRegistry.Instance.Get(info.Inflictor);
        if (weapon is not null)
            DoHitFlinch(weapon.Primary.HitFlinch);

        if (Health <= 0)
            OnDeath(info);
    }

	[Rpc.Broadcast]
	public virtual void TakeDamage( Shared.DamageInfo info )
	{
        Log.Info($"[PlayerBase.Health] TakeDamage called: dmg={info.Damage} attacker={info.AttackerId}");
        if ( !IsValid || !Networking.IsHost || !IsAlive || GodMode )
			return;

		if ( Array.Exists( info.Tags, tag => tag == "head" ) )
			info.Damage *= 2;

		float dmgMultiplier = 1;
		float armorMultiplier = 1;
		var attacker = Scene.Directory.FindByGuid(info.AttackerId);

        
		if (attacker != null && attacker.IsValid)
		{
            var upgradeHolder = attacker.GetComponent<UpgradeHolder>();
			if (upgradeHolder != null)
			{
				armorMultiplier = upgradeHolder.ArmorMultiplier;
				dmgMultiplier = upgradeHolder.DamageMultiplier * upgradeHolder.ArmorMultiplier;
			}
		}

		if (!dmgTable.ContainsKey(info.Inflictor))
		{
			dmgTable.Add(info.Inflictor, new(info));
		}
		dmgTable[info.Inflictor].Damage = (int)MathF.Round(Math.Clamp(
            dmgTable[info.Inflictor].Damage + info.Damage,
            1,
            MathF.Round(MaxDamageClamp * armorMultiplier * dmgMultiplier)));

		//Log.Info("[TakeDamage]:" + info.Inflictor + " causes " + dmgTable[info.Inflictor].Damage + " damage to " + info.Tags);
    }

    [Rpc.Broadcast]
    public virtual void TakeDamage(int damage)
    {
        Log.Info($"TakeDamage called: dmg={damage} ");
        if (!IsValid || IsProxy || !IsAlive || GodMode)
            return;

        Health -= damage;
    }

}