using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using Animancer; using BITKit; using BITKit.Entities; using BITKit.Mod; using BITKit.Pool; using BITKit.StateMachine; using Cysharp.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Net.Project.B.AI; using Net.Project.B.Dialogue; using Net.Project.B.Health; using Net.Project.B.Interaction; using NodeCanvas.DialogueTrees; using NodeCanvas.Framework; using Project.B.Animation; using Project.B.CharacterController; using UnityEngine; using UnityEngine.AI; using Object = UnityEngine.Object; namespace Project.B.Entities { public class NpcFactory:INpcFactory { public IReadOnlyDictionary Entities { get; set; } private static readonly ConcurrentDictionary _entities=new(); private readonly IServiceProvider _serviceProvider; private readonly IServiceCollection _serviceCollection; private readonly IPoolService _poolService; private readonly IHealthService _healthService; private readonly IEntitiesService _entitiesService; private readonly IReadOnlyCollection _humanoidAnimationFactories; private readonly IReadOnlyCollection _humanoidTransitionAnimationFactories; public NpcFactory(IServiceProvider serviceProvider, IServiceCollection serviceCollection, IEntitiesService entitiesService, IHealthService healthService, IPoolService poolService) { _serviceProvider = serviceProvider; _serviceCollection = serviceCollection; _entitiesService = entitiesService; _healthService = healthService; _poolService = poolService; _humanoidAnimationFactories = _entitiesService.QueryComponents().ToArray(); _humanoidTransitionAnimationFactories = _entitiesService.QueryComponents().ToArray(); } public async UniTask CreateAsync(string addressablePath, object obj) { var entity = new Entity(); if (obj is not GameObject model || !model) { var op = Object.InstantiateAsync(await ModService.LoadAsset(string.IsNullOrEmpty(addressablePath) ? "npc_base" : addressablePath)); await op; model = op.Result[0]; } if (_entities.TryGetValue(model.GetInstanceID(), out var currentEntity)) { return currentEntity; } if (model && _entitiesService.Entities.TryGetValue(model.GetInstanceID(), out currentEntity) ) { _entitiesService.UnRegister(currentEntity); await UniTask.NextFrame(); if (currentEntity is IDisposable disposable) { disposable.Dispose(); } await UniTask.NextFrame(); entity.Id = model.GetInstanceID(); entity.ServiceCollection.Add(currentEntity.ServiceCollection); if (currentEntity.ServiceProvider.QueryComponents(out IHealthComponent _) is false) { entity.ServiceCollection.AddSingleton (); } } var idComponent = new IdComponent() { Id = model.GetInstanceID(), Name = "Npc" }; if (model && model.TryGetComponent(out var dialogueActor)) { idComponent.Name = dialogueActor.name; } entity.ServiceCollection.AddSingleton(idComponent); var ct = model.GetCancellationTokenOnDestroy(); var cts = new CancellationTokenSource(); ct.Register(entity.Dispose); ct.Register(cts.Cancel); entity.CancellationToken = ct; entity.ServiceCollection.Add(_serviceCollection); entity.ServiceCollection.AddSingleton(_serviceProvider.GetRequiredService()); entity.ServiceCollection.AddSingleton(cts); entity.ServiceCollection.AddSingleton(model.GetComponent()); entity.ServiceCollection.AddSingleton(model.GetComponent()); entity.ServiceCollection.AddSingleton(model.GetComponent()); entity.ServiceCollection.AddSingleton(model.GetComponent()); entity.ServiceCollection.AddSingleton(model.GetComponent()); entity.ServiceCollection.AddSingleton(_healthService); entity.ServiceCollection.AddSingleton(_serviceProvider.GetRequiredService()); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(_serviceProvider.GetRequiredService()); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(x => x.GetRequiredService()); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(); entity.ServiceCollection.AddSingleton(x => x.GetRequiredService()); if (model.TryGetComponent(out var tag)) { var tags = tag.Tags; foreach (var animationFactory in _humanoidAnimationFactories) { if(animationFactory.Tags.Count is 0)continue; if (tags.Intersect(animationFactory.Tags).Any()) { entity.ServiceCollection.AddSingleton(animationFactory); break; } } foreach (var animationFactory in _humanoidTransitionAnimationFactories) { if(animationFactory.Tags.Count is 0)continue; if (tags.Intersect(animationFactory.Tags).Any()) { entity.ServiceCollection.AddSingleton(animationFactory); break; } } } await OnEntityCreate.UniTaskFunc(addressablePath, entity); var serviceProvider = entity.ServiceProvider; await OnEntityCreated.UniTaskFunc(addressablePath, entity); _entities[entity.Id] = entity; ct.Register(WaitEntityDispose); _entitiesService.Register(entity); WaitEntityDispose(); return entity; async void OnHealthChanged(int arg2, int arg3) { if(ct.IsCancellationRequested)return; if (arg3 < 0) { var myRenderer = model.GetComponentsInChildren().ToDictionary(x => x.name, x=>x); var ragdoll =await _poolService.Spawn("player_ragdoll"); ragdoll.transform.SetPositionAndRotation(model.transform.position, model.transform.rotation); var ragdollRenderer = ragdoll.GetComponentsInChildren().ToDictionary(x => x.name, x=>x); foreach (var (name,myMeshRenderer) in myRenderer) { if (ragdollRenderer.TryGetValue(name, out var ragdollMeshRenderer)) { ragdollMeshRenderer.material = myMeshRenderer.material; } } model.SetActive(false); } else { model.SetActive(true); } } async void WaitEntityDispose() { using var characterController = serviceProvider.GetRequiredService(); serviceProvider.GetRequiredService(); using var npcAnimancer = serviceProvider.GetRequiredService(); characterController.TransitionState(); var healthComponent = serviceProvider.GetRequiredService(); healthComponent.OnHealthChanged += OnHealthChanged; await ct.WaitUntilCanceled(); _entitiesService.UnRegister(entity); _entities.TryRemove(entity.Id, out _); } } public event Func OnEntityCreate; public event Func OnEntityCreated; public void Dispose() { } } }