using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using BITKit; using BITKit.Animations; using BITKit.StateMachine; using System.Linq; using System.Security.Permissions; using AYellowpaper.SerializedCollections; using BITFALL; using BITFALL.Entities.Equipment; using BITFALL.Player.Equip; using BITKit.Entities.Melee; using BITKit.Entities.VFX; using Cinemachine; using Cysharp.Threading.Tasks; using Unity.Mathematics; namespace BITKit.Entities { public abstract class BITEquipBase : StateBasedMonoBehaviour, IEquipBase where T : IState { [Header(Constant.Header.Settings)] [SerializeField] protected AssetableItem item; [Header(Constant.Header.Property)] [SerializeField] public SerializedDictionary animationProperties; [Header(Constant.Header.Components)] [SerializeField] public UnityAnimator animator; [SerializeField] protected EntityVFXPlayer vfxPlayer; [SerializeField] protected EntityAnimator entityAnimator; [SerializeField] private Renderer[] renderers; [SerializeField] protected Transform cameraTransform; [Header(Constant.Header.Services)] [SerializeReference,SubclassSelector] protected IMeleeService meleeService; public Entities.IEntity Entity { get; set; } public Entity UnityEntity=>Entity as Entity; public IBasicItem Item { get; set; } public IDictionary AnimationProperties => animationProperties.ToDictionary(x=>x.Key,x=>x.Value.Value); public readonly InputActionGroup inputActionGroup = new() { allowGlobalActivation = true }; protected readonly ValidHandle AllowRendering = new(); public virtual string AddressablePath => item.AddressablePath; protected virtual Vector3 meleeForce => Transform.forward; public bool IsEntered { get; set; } private Quaternion _initialCameraRotation; [Inject] protected IEntityOverride _entityOverride; public virtual void Entry() { if (animator) animator.enabled = true; AllowRendering.AddElement(this); inputActionGroup.allowInput.AddElement(this); if (entityAnimator) entityAnimator.enabled = true; if (vfxPlayer) vfxPlayer.enabled = true; if (animator) { var animName = animator.animator.GetCurrentAnimatorStateInfo(0).shortNameHash; animator.animator.Play(animName, -1, 0); } } public virtual UniTask EntryAsync() { return UniTask.CompletedTask; } public virtual void Entered() { } public virtual void Exit() { if (vfxPlayer) vfxPlayer.enabled = false; inputActionGroup.allowInput.RemoveElement(this); } public virtual UniTask ExitAsync() { AllowRendering.RemoveElement(this); if (animator) animator.enabled = false; return UniTask.CompletedTask; } public virtual async void Exited() { if (entityAnimator) entityAnimator.enabled = false; if (cameraTransform) { await UniTask.NextFrame(destroyCancellationToken); cameraTransform.localPosition = default; cameraTransform.localRotation = _initialCameraRotation; } } public virtual void OnAwake() { AllowRendering.AddListener(x => renderers.ForEach(y => { y.enabled = x; })); if (animator) animator.enabled = false; AllowRendering.Invoke(); if (cameraTransform) _initialCameraRotation = cameraTransform.localRotation; Initialize(); inputActionGroup.allowInput.Invoke(); if (vfxPlayer) vfxPlayer.enabled = false; _entityOverride.OnOverride += OnOverride; } private void OnOverride(bool obj) { inputActionGroup.allowInput.SetDisableElements("Override",obj); AllowRendering.SetDisableElements("Override",obj); } public virtual void OnDestroy() { inputActionGroup.Dispose(); } public virtual void OnStart() { } public virtual void OnUpdate(float deltaTime) { } public virtual bool IsSupportItem(IBasicItem item) =>item is not null && item.AddressablePath == AddressablePath; public virtual void PlayAudio(string eventName) { } public virtual void EquipEvent(string eventName){} public virtual void AnimationEvent(string eventName) { if (IsEntered is false) return; if (item is not AssetableEquip equip) return; switch (eventName) { case "Melee": case "Attack": meleeService.Melee(new MeleeCommand { PlayerId = Entity.Id, Position = Transform.position, Force = meleeForce * equip.MeleeForce, Range = equip.MeleeRange, Damage = equip.MeleeDamage, Forward = UnityEntity.transform.forward }); break; case "HeavyAttack": meleeService.Melee(new MeleeCommand { PlayerId = Entity.Id, Position = Transform.position, Force = meleeForce * equip.HeavyMeleeForce, Range = equip.HeavyMeleeRange, Damage = equip.HeavyMeleeDamage, Forward = UnityEntity.transform.forward }); break; } } } [CustomType(typeof(IEquipService))] [CustomType(typeof(IEntityEquipment))] public class EntityEquipment : EntityBehavior,IEquipService,IEntityEquipment { public IOptional Zoom { get; } = new Optional(){Value = 1}; public float Stable { get; set; } public bool AllowAttack { get; set; } public bool AllowScope { get; set; } [SerializeField] private CinemachineVirtualCamera virtualCamera; [SerializeField] private Optional overrideIndex; [SerializeField] private Optional optionalScope; public IBasicItem CurrentItem { get; private set; } public event Action OnEquip; public event Action OnUnEquip; public event Action OnEquipAddressable; public event Action OnUnEquipAddressable; private readonly EntryGroup equips = new(); protected IEquipBase entryComplete; [Inject(true)] private IHealth _health; public IValidHandle AllowEquip { get; } =new ValidHandle(); public override void OnAwake() { base.OnAwake(); equips.list = GetComponentsInChildren(true).ToList(); equips.OnEntry += OnEntry; equips.OnExit += OnExit; AllowEquip.AddListener(OnAllowEquip); if (_health is not null) { _health.OnSetAlive += x => { AllowEquip.SetElements(this,x); if (x is false) { CurrentItem = null; EntryEquip(-1); } }; } foreach (var x in equips.list) { x.Entity = UnityEntity; x.OnAwake(); } } private void OnAllowEquip(bool allow) { if (allow) { EntryEquip(CurrentItem); } else { EntryEquip(-1); } } public override void OnStart() { base.OnStart(); foreach (var x in equips.list) { x.OnStart(); } if (overrideIndex.Allow && (_health?.IsAlive ?? true)) { EntryEquip(overrideIndex.Value); } } private void OnExit(IEquipBase obj) { OnUnEquipAddressable?.Invoke(obj.AddressablePath); OnUnEquip?.Invoke(obj.Item); //Debug.Log($"已退出:{obj.Item.Name}"); } private void OnEntry(IEquipBase obj) { OnEquipAddressable?.Invoke(obj.AddressablePath); obj.Item = CurrentItem; OnEquip?.Invoke(obj.Item); //Debug.Log($"已进入:{obj.Item.Name}"); } public override void OnUpdate(float deltaTime) { if (equips.TryGetEntried(out entryComplete)) { entryComplete.OnUpdate(deltaTime); } if (virtualCamera is not null) { virtualCamera.m_Lens.FieldOfView = Mathf.Lerp( virtualCamera.m_Lens.FieldOfView, Zoom.Allow ? Mathf.Rad2Deg * CalcZoomFOV(Mathf.Deg2Rad * 60f, Zoom.Value) : PlayerConfig.Singleton.Fov, 16*deltaTime ); float CalcZoomFOV(float baseFOV, float zoom) { return 2.0f * Mathf.Atan(Mathf.Tan(baseFOV / 2.0f) / zoom); } } if (optionalScope.Allow) { optionalScope.Value.SetActive(AllowScope); } } public bool IsSupportItem(IBasicItem item)=> equips.list.Any(x => x.IsSupportItem(item)); public void EntryEquip(int index)=> equips.Entry(index); public void EntryEquip(Func factory)=>equips.Entry(x=>factory.Invoke(x.AddressablePath)); public void EntryEquip(IBasicItem item) { CurrentItem = item; equips.Entry(x=>x.IsSupportItem(item)); if (equips.list.TryGetAny(x => x.IsSupportItem(item), out var nextEquip)) { nextEquip.Item = item; } } } }