using System; using System.Collections.Concurrent; using System.Collections.Generic; using BITKit; using BITKit.Entities; using BITKit.WorldNode; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Net.Project.B.Health { public interface IHealthComponent { public int HealthPoint { get;internal set; } public int MaxHealthPoint { get; } public event Action OnHealthChanged; } [Serializable] public class HealthComponent:IHealthComponent,IWorldNode { public int maxHealthPoint = 100; public int healthPoint; int IHealthComponent.HealthPoint { get => healthPoint; set { var prevHp = healthPoint; if(prevHp==value)return; healthPoint = value; OnHealthChanged?.Invoke(prevHp,healthPoint); } } public int MaxHealthPoint => maxHealthPoint; public event Action OnHealthChanged; } /// /// 生命值服务 /// public interface IHealthService { /// /// 已缓存的生命值 /// public IReadOnlyDictionary Healths { get; } /// /// 在改变生命值前,ID,当前生命值,新的生命值,来源 /// 增加的数值 /// public event Func OnHealthPlus; /// /// 在改变生命值后 /// /// /// /// 当健康值发生变化时触发的事件。 /// public event Action OnHealthChanged; /// /// 调整生命值 /// /// /// +- not set /// public UniTask AddHealth(int id,int value,object arg); } public class HealthService:IHealthService,IDisposable { private static HealthService _singleton; private readonly IEntitiesService _entitiesService; private static readonly ConcurrentDictionary HealthComponents = new(); private readonly ILogger _logger; public HealthService(IEntitiesService entitiesService, ILogger logger) { _entitiesService = entitiesService; _logger = logger; _singleton = this; _entitiesService.OnAdd += OnAdd; } private void OnAdd(IEntity obj) { if (obj.ServiceProvider.QueryComponents(out IHealthComponent healthComponent) is false) return; HealthComponents[obj.Id] = healthComponent; Healths[obj.Id] = healthComponent.HealthPoint = healthComponent.MaxHealthPoint <= 0 ? 100 : healthComponent.MaxHealthPoint; } [BITCommand] public static void SetHealth(int id, int newHealth) { Healths.GetOrAdd(id,100); Healths.Set(id,newHealth); OnHealthChanged?.Invoke(id,newHealth,newHealth,null); } [BITCommand] public static void AddHealth(int id, int newHealth) { Healths.GetOrAdd(id,100); _singleton.AddHealth(id, newHealth, null).Forget(); } IReadOnlyDictionary IHealthService.Healths => Healths; private static event Func OnHealthChange; private static event Action OnHealthChanged; private static readonly ConcurrentDictionary Healths = new(); event Func IHealthService.OnHealthPlus { add => OnHealthChange += value; remove => OnHealthChange -= value; } event Action IHealthService.OnHealthChanged { add => OnHealthChanged += value; remove => OnHealthChanged -= value; } public UniTask AddHealth(int id, int value,object arg) { if (Healths.TryGetValue(id,out var current) is false) return UniTask.FromResult(-1); foreach (var func in OnHealthChange.CastAsFunc()) { value =func.Invoke(id,current, value, arg); } var newHp = Math.Clamp(current + value, -1, HealthComponents[id].MaxHealthPoint); Healths.Set(id,HealthComponents[id].HealthPoint = newHp); try { OnHealthChanged?.Invoke(id, current, newHp, arg); } catch (Exception e) { _logger.LogCritical(e, e.Message); } return UniTask.FromResult(newHp); } public void Dispose() { _entitiesService.OnAdd -= OnAdd; } } }