249 lines
9.4 KiB
C#
249 lines
9.4 KiB
C#
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<int, IEntity> Entities { get; set; }
|
|
private static readonly ConcurrentDictionary<int, IEntity> _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<IHumanoidAnimationFactory> _humanoidAnimationFactories;
|
|
private readonly IReadOnlyCollection<IHumanoidTransitionAnimationFactory> _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<IHumanoidAnimationFactory>().ToArray();
|
|
_humanoidTransitionAnimationFactories = _entitiesService.QueryComponents<IHumanoidTransitionAnimationFactory>().ToArray();
|
|
}
|
|
|
|
public async UniTask<IEntity> CreateAsync(string addressablePath, object obj)
|
|
{
|
|
var entity = new Entity();
|
|
|
|
|
|
if (obj is not GameObject model || !model)
|
|
{
|
|
var op = Object.InstantiateAsync(await ModService.LoadAsset<GameObject>(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<IHealthComponent,HealthComponent > ();
|
|
}
|
|
|
|
}
|
|
|
|
var idComponent = new IdComponent() { Id = model.GetInstanceID(), Name = "Npc" };
|
|
|
|
if (model && model.TryGetComponent<IDialogueActor>(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<IEntitiesService>());
|
|
|
|
entity.ServiceCollection.AddSingleton(cts);
|
|
entity.ServiceCollection.AddSingleton(model.GetComponent<Transform>());
|
|
entity.ServiceCollection.AddSingleton(model.GetComponent<Animator>());
|
|
entity.ServiceCollection.AddSingleton(model.GetComponent<AnimancerComponent>());
|
|
entity.ServiceCollection.AddSingleton(model.GetComponent<NavMeshAgent>());
|
|
|
|
entity.ServiceCollection.AddSingleton(model.GetComponent<IBlackboard>());
|
|
|
|
entity.ServiceCollection.AddSingleton(_healthService);
|
|
entity.ServiceCollection.AddSingleton(_serviceProvider.GetRequiredService<IKnockedService>());
|
|
|
|
entity.ServiceCollection.AddSingleton<NpcAnimancerController>();
|
|
|
|
entity.ServiceCollection.AddSingleton(_serviceProvider.GetRequiredService<IDialogueService>());
|
|
|
|
entity.ServiceCollection.AddSingleton<NpcCharacterController>();
|
|
entity.ServiceCollection.AddSingleton<ICharacterController>(x =>
|
|
x.GetRequiredService<NpcCharacterController>());
|
|
|
|
entity.ServiceCollection.AddSingleton<ICharacterStateIdle, NpcCharacterStateIdle>();
|
|
entity.ServiceCollection.AddSingleton<ICharacterStateWalk, NpcCharacterStateWalk>();
|
|
|
|
entity.ServiceCollection.AddSingleton<ICharacterStateAnimation, NpcCharacterStateAnimation>();
|
|
|
|
entity.ServiceCollection.AddSingleton<AIBlackBoard>();
|
|
|
|
entity.ServiceCollection.AddSingleton<DynamicInteractable>();
|
|
entity.ServiceCollection.AddSingleton<IWorldInteractable>(x => x.GetRequiredService<DynamicInteractable>());
|
|
|
|
if (model.TryGetComponent<ITag>(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<Renderer>().ToDictionary(x => x.name, x=>x);
|
|
|
|
var ragdoll =await _poolService.Spawn<GameObject>("player_ragdoll");
|
|
ragdoll.transform.SetPositionAndRotation(model.transform.position,
|
|
model.transform.rotation);
|
|
|
|
var ragdollRenderer = ragdoll.GetComponentsInChildren<Renderer>().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<NpcCharacterController>();
|
|
serviceProvider.GetRequiredService<ICharacterController>();
|
|
|
|
using var npcAnimancer = serviceProvider.GetRequiredService<NpcAnimancerController>();
|
|
|
|
characterController.TransitionState<ICharacterStateIdle>();
|
|
|
|
var healthComponent = serviceProvider.GetRequiredService<IHealthComponent>();
|
|
|
|
healthComponent.OnHealthChanged += OnHealthChanged;
|
|
|
|
await ct.WaitUntilCanceled();
|
|
|
|
_entitiesService.UnRegister(entity);
|
|
|
|
_entities.TryRemove(entity.Id, out _);
|
|
}
|
|
}
|
|
|
|
|
|
public event Func<string, IEntity,UniTask> OnEntityCreate;
|
|
public event Func<string, IEntity,UniTask> OnEntityCreated;
|
|
|
|
public void Dispose()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|