374 lines
12 KiB
C#
374 lines
12 KiB
C#
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.Entities.Inventory;
|
|
using BITFALL.Guns.Modify;
|
|
using BITFALL.Player.Equip;
|
|
using BITFALL.Props;
|
|
using BITKit.Entities.Melee;
|
|
using BITKit.Entities.VFX;
|
|
using BITKit.UX;
|
|
using Cinemachine;
|
|
using Cysharp.Threading.Tasks;
|
|
using Unity.Mathematics;
|
|
|
|
namespace BITKit.Entities
|
|
{
|
|
|
|
public abstract class BITEquipBase<T> : StateBasedMonoBehaviour<T>, IEquipBase where T : IState
|
|
{
|
|
[Header(Constant.Header.Settings)]
|
|
[SerializeField] protected AssetableItem item;
|
|
|
|
[Header(Constant.Header.Property)]
|
|
[SerializeField] public SerializedDictionary<string,AnimationProperty> 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;
|
|
[SerializeField] protected Prop_Modify _modify;
|
|
[Header(Constant.Header.Services)]
|
|
[SerializeReference,SubclassSelector] protected IMeleeService meleeService;
|
|
[SerializeReference,SubclassSelector,Inject] private IUXPopup _popup;
|
|
|
|
public Entities.IEntity Entity { get; set; }
|
|
public Entity UnityEntity=>Entity as Entity;
|
|
|
|
public IBasicItem Item
|
|
{
|
|
get => _item;
|
|
set
|
|
{
|
|
OnApply?.Invoke(_item = value);
|
|
OnItemApplied(value);
|
|
}
|
|
}
|
|
private IBasicItem _item;
|
|
public IDictionary<string,float> 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;
|
|
|
|
protected event Action<IBasicItem> OnApply;
|
|
|
|
[Inject] protected IEntityOverride _entityOverride;
|
|
[Inject(true)] protected IEntityInventory _inventory;
|
|
[Inject(true)] protected IUXPopup _uxPopup;
|
|
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 (_modify)
|
|
_modify.AllowModify.SetDisableElements(this,!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;
|
|
|
|
OnApply += (x) =>
|
|
{
|
|
if (!_modify) return;
|
|
if (x is not null && x.TryGetProperty<GunModify>(out var modify))
|
|
{
|
|
_modify.Modify(modify);
|
|
}
|
|
else
|
|
{
|
|
_modify.ClearModify();
|
|
}
|
|
};
|
|
if (_modify)
|
|
_modify.ClearModify();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
protected virtual void OnItemApplied(IBasicItem item)
|
|
{
|
|
|
|
}
|
|
}
|
|
[CustomType(typeof(IEquipService))]
|
|
[CustomType(typeof(IEntityEquipment))]
|
|
public class EntityEquipment : EntityBehavior,IEquipService,IEntityEquipment
|
|
{
|
|
public IOptional<float> Zoom { get; } = new Optional<float>(){Value = 1};
|
|
public float Stable { get; set; }
|
|
public float Aim { get; set; }
|
|
public bool AllowAttack { get; set; }
|
|
public bool AllowScope { get; set; }
|
|
|
|
[SerializeField] private CinemachineVirtualCamera virtualCamera;
|
|
|
|
[SerializeField] private Optional<int> overrideIndex;
|
|
|
|
[SerializeField] private Optional<GameObject> optionalScope;
|
|
|
|
public IBasicItem CurrentItem { get; private set; }
|
|
|
|
public event Action<IBasicItem> OnEquip;
|
|
public event Action<IBasicItem> OnUnEquip;
|
|
public event Action<string> OnEquipAddressable;
|
|
public event Action<string> OnUnEquipAddressable;
|
|
|
|
private readonly EntryGroup<IEquipBase> 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<IEquipBase>(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)
|
|
{
|
|
var targetFov =
|
|
Mathf.Lerp(
|
|
virtualCamera.m_Lens.FieldOfView,
|
|
Zoom.Allow ?
|
|
Mathf.Rad2Deg * CalcZoomFOV(Mathf.Deg2Rad * 60f, Zoom.Value) : PlayerConfig.Singleton.Fov,
|
|
16*deltaTime
|
|
);
|
|
virtualCamera.m_Lens.FieldOfView = Mathf.Lerp(PlayerConfig.Singleton.Fov, targetFov, Aim);
|
|
|
|
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<string,bool> 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;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
} |