using System; using System.Collections.Generic; using System.Linq; using BITFALL.Cosmetic; using BITFALL.Entities.Equipment; using BITFALL.HotFix; using BITFALL.Player.Movement; using BITKit.PlayerCamera; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.Animations.Rigging; using UnityEngine.Rendering; namespace BITKit.Entities.Player.Character { public class PlayerCharacter : EntityPlayerBehavior { [DictionaryReferenceConfig(nameof(Player_Mesh_Type_FPV))] public const string Player_Mesh_Type_FPV = nameof(Player_Mesh_Type_FPV); [DictionaryReferenceConfig(nameof(Player_Mesh_Type_TPV))] public const string Player_Mesh_Type_TPV = nameof(Player_Mesh_Type_TPV); [DictionaryReferenceConfig(nameof(Player_Mesh_Type_FPVOverride))] public const string Player_Mesh_Type_FPVOverride = nameof(Player_Mesh_Type_FPVOverride); [DictionaryReferenceConfig(nameof(Player_Mesh_Type_TPV_Group))] public const string Player_Mesh_Type_TPV_Group = nameof(Player_Mesh_Type_TPV_Group); private readonly List fpvRenderer = new(); private readonly List tpvRenderer = new(); private readonly List fpvOverrideRenderers = new(); private readonly List tpvOverrideRenderers = new(); [Header(Constant.Header.Reference)] [SerializeReference, SubclassSelector] public IReference getDamage; [Inject(true)] private ICosmeticService cosmeticService; [Inject(true)] private IHealth _health; [Inject(true)] private IPlayerCameraService _cameraService; [Inject(true)] private IEntityOverride _entityOverride; [Inject(true)] private IEntityEquipment _equipment; [Inject(true)] private IEntityMovement _movement; private readonly ValidHandle allowFPVOverride = new(); private readonly IntervalUpdate _freezeInterval=new (0.01f); private bool _isDirty; private bool _lock; private void OnCosmeticsChange() { _lock = true; } public override void OnAwake() { if (cosmeticService is not null) { cosmeticService.OnCosmeticsChange += OnCosmeticsChange; cosmeticService.OnCosmeticsChanged += OnCosmeticsChanged; } if (_health is not null) { _health.OnSetAlive += SetDirty; _health.OnDamageRelease += OnDamageRelease; } if (_cameraService is not null) _cameraService.OnCameraActivated += SetDirty; if (_entityOverride is not null) _entityOverride.OnOverride += _ => { UpdateMeshState(); allowFPVOverride.SetElements(_entityOverride, _); }; if (_equipment is not null) { _equipment.OnEquipAddressable += OnEquipmentOnOnEquipAddressable; _equipment.OnUnEquipAddressable += OnEquipmentOnOnUnEquipAddressable; void OnEquipmentOnOnUnEquipAddressable(string _) { allowFPVOverride.AddElement(_equipment); allowFPVOverride.RemoveDisableElements(_equipment); } void OnEquipmentOnOnEquipAddressable(string _) { allowFPVOverride.RemoveElement(_equipment); allowFPVOverride.AddDisableElements(_equipment); } } if (_movement is not null) { _movement.OnStateChanged += OnNewState; OnNewState(null, _movement.CurrentState); void OnNewState(IEntityMovementState oldState, IEntityMovementState newState) { var allow = newState switch { IPlayerEdgeClimbState => true, IPlayerClimbState => true, IPlayerVaultState => true, IPlayerKnockdownState => true, IPlayerSlideState => true, IPlayerFixedState => true, IPlayerLinkState => true, IPlayerCrawlState=>true, _ => false, }; allowFPVOverride.SetElements(_movement,allow); allowFPVOverride.SetDisableElements(_movement,!allow); } } allowFPVOverride.AddListener(value => { foreach (var x in fpvOverrideRenderers) { try { x.enabled = value; } catch (Exception e) { BIT4Log.LogException(e); } } }); } public override void OnDestroyComponent() { base.OnDestroyComponent(); if (cosmeticService is null) return; cosmeticService.OnCosmeticsChange -= OnCosmeticsChange; cosmeticService.OnCosmeticsChanged -= OnCosmeticsChanged; } public override void OnUpdate(float deltaTime) { if (!_isDirty || _lock || !_freezeInterval.AllowUpdateWithoutReset) return; _isDirty = false; UpdateMeshState(); } private void OnDamageRelease(DamageMessage damageMessage) { if (getDamage is not null) UnityEntity.Invoke(Constant.Animation.Play, getDamage.Value); } private void UpdateMeshState() { allowFPVOverride.Invoke(); switch (_health, _cameraService) { case (null, null): SetFPV(false); allowFPVOverride.Invoke(false); break; case (null, not null): SetFPV(_cameraService.IsCameraActivated); break; case (not null, null): SetFPV(_health.IsAlive); break; case (not null, not null): SetFPV(_health.IsAlive && _cameraService.IsCameraActivated); break; } } private void SetFPV(bool isFpv) { var shadowMode = isFpv ? ShadowCastingMode.ShadowsOnly : ShadowCastingMode.On; foreach (var x in fpvRenderer) { x.enabled = isFpv; } foreach (var x in tpvRenderer) { x.shadowCastingMode = shadowMode; } foreach (var x in tpvOverrideRenderers) { x.shadowCastingMode = _entityOverride is not null ? _entityOverride.IsOvering ? ShadowCastingMode.On : shadowMode : shadowMode; } if (_health is not null && TryGetComponent(out var rigBuilder)) { rigBuilder.enabled = _health.IsAlive; } } [BIT] private void OnCosmeticsChanged() { _lock = true; BITAppForUnity.ThrowIfNotPlaying(); fpvRenderer.Clear(); tpvRenderer.Clear(); fpvOverrideRenderers.Clear(); tpvOverrideRenderers.Clear(); var _tpvOverrideRenderers = new List(); foreach (var _tag in GetComponentsInChildren(true)) { var tags = _tag.GetTags(); if (tags.Contains(Player_Mesh_Type_TPV_Group)) _tpvOverrideRenderers.Add(_tag.transform); if (_tag.TryGetComponent(out var _renderer) is false) continue; if (tags.Contains(Player_Mesh_Type_FPV)) fpvRenderer.Add(_renderer); if (tags.Contains(Player_Mesh_Type_TPV)) tpvRenderer.Add(_renderer); if (tags.Contains(Player_Mesh_Type_FPVOverride)) fpvOverrideRenderers.Add(_renderer); } var tpvRenderers = _tpvOverrideRenderers.SelectMany(x => x.GetComponentsInChildren(true)); tpvOverrideRenderers.Clear(); tpvOverrideRenderers.AddRange(tpvRenderers); UpdateMeshState(); allowFPVOverride.Invoke(); _lock = false; _isDirty = false; if (TryGetComponent(out var rigBuilder)) { rigBuilder.Clear(); foreach (var rig in GetComponentsInChildren()) { rigBuilder.layers.Add(new RigLayer(rig)); } rigBuilder.enabled = true; rigBuilder.Build(); GetComponent().Rebind(); } } private void SetDirty(bool any) => SetDirty(); private void SetDirty() { _freezeInterval.Reset(); _isDirty = true; } } }