using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading; using BITFALL.Entities; using BITFALL.Entities.Armor; using BITFALL.Entities.Equipment; using BITFALL.Guns; using BITFALL.Player.Equip; using BITFALL.Player.Movement; using UnityEngine; using UnityEngine.UIElements; using UnityEngine.InputSystem; using BITKit; using BITKit.UX; using BITKit.Entities; using BITKit.Entities.Player; using BITKit.SceneManagement; using BITKit.Selection; using BITKit.Steamwork; using BITKit.Tween; using Cysharp.Threading.Tasks; using UnityEditor; using UnityEngine.InputSystem.Interactions; using Image = UnityEngine.UI.Image; namespace BITFALL.UX { public class UXHud : UIToolKitPanel { [Header(Constant.Header.Providers)] [SerializeReference, SubclassSelector] private ISceneService sceneService; [SerializeReference, SubclassSelector] private IPlayerService playerService; [SerializeReference, SubclassSelector] private ISteamService steamService; [Header(Constant.Header.Components)] [SerializeField] private UXImage crosshairImage; [SerializeField] private UXImage crosshairParentImage; [SerializeField] private UXLabel playerNameLabel; [SerializeField] private UXImage playerAvatarImage; [SerializeField] private UXElement playerInfo; [SerializeField] private UXBar healthBar; [SerializeField] private UXBar lerpHealthBar; [SerializeField] private UXBar staminaBar; [SerializeField] private UXBar armorBar; [Header(Constant.Header.Input)] [SerializeField] private InputActionReference inventoryAction; [SerializeField] private InputActionReference returnAction; [SerializeField] private InputActionReference inspectAction; [Header(Constant.Header.Input)] [SerializeField] private InputActionReference interactiveAction; [Inject] private IHealth _health; [Inject] private IEntityMovement _entityMovement; [Inject] private IPlayerMovement _playerMovement; [Inject] private IEquipService _equipService; [Inject] private IEntityEquipment _equipment; [Inject] private ISelector _selector; [Inject] private IArmor _armor; [Inject] private IKnockdown _knockdown; [UXBindPath("count-label")] private Label countLabel; [UXBindPath("injury-container")] private VisualElement injuryContainer; [UXBindPath("bloody-container")] private VisualElement bloodyContainer; [UXBindPath("knocked-fill")] private VisualElement knockedFill; [UXBindPath("health-fill")] private VisualElement healthFill; private VisualElement[] swayElements; private float _currentHealthLerp; protected CancellationTokenSource bloodyScreenCancellationTokenSource; private string _interactiveKey="F"; protected override void Awake() { base.Awake(); inputActionGroup.RegisterCallback(returnAction, OnReturn); inputActionGroup.RegisterCallback(inventoryAction, OnInventory); inputActionGroup.RegisterCallback(inspectAction, OnInspect); sceneService.OnSceneLoaded += x=>Entry(); playerService.OnPlayerInitialized += OnPlayerInitializedLocalPlayer; BITKit.UX.UXUtils.Inject(this); bloodyContainer.SetOpacity(0); injuryContainer.SetOpacity(0); swayElements = document.rootVisualElement.Query(className: "swap").ToList().ToArray(); } private void OnInspect(InputAction.CallbackContext obj) { switch (obj,_equipment) { case ({interaction:HoldInteraction,performed:true}, {CurrentItem: not null}): UXInventoryInspector.Entry(_equipment.CurrentItem); break; } } private void OnDestroy() { playerService.OnPlayerInitialized -= OnPlayerInitializedLocalPlayer; } public void OnSelect(ISelectable selectable) { } public void OnInactive(ISelectable selectable) { //seleableLabel.SetActive(false); UXOnScreenPrompts.Release(_interactiveKey); } private async void OnPlayerInitializedLocalPlayer(IUnityEntity unityEntity) { unityEntity.Inject(this); _health.OnDamageRelease += OnDamageRelease; _health.OnSetAlive += OnSetAlive; _health.OnSetHealthPoint += OnSetHP; _selector.OnInactive += OnInactive; _selector.OnSelected += OnSelect; _armor.OnArmorChanged += x => armorBar.Set(x); _armor.OnEquipArmor += x => { armorBar.Set(_armor.Armor); armorBar.SetActive(true); }; _knockdown.OnKnockdown+=OnKnockdown; _knockdown.OnRevive+=OnRevive; _armor.OnUnEquipArmor += x => armorBar.SetActive(false); if (steamService.IsInitialized) { playerNameLabel.Set(steamService.Name); var avatar = await steamService.GetAvatarAsync(cancellationToken); playerAvatarImage.SetTexture(avatar); } _equipService = unityEntity.Get(); armorBar.SetActive(_armor.TryGetCurrentArmor(out _)); OnSetHP(_health.HealthPoint); OnSetAlive(_health.IsAlive); OnInactive(null); OnRevive(); _interactiveKey = interactiveAction.GetKeyMap(); } private void OnRevive() { knockedFill.SetActive(false); healthFill.SetActive(true); } private void OnKnockdown() { knockedFill.SetActive(true); healthFill.SetActive(false); } private void OnDamageRelease(DamageMessage message) { bloodyScreenCancellationTokenSource?.Cancel(); bloodyScreenCancellationTokenSource = new CancellationTokenSource(); destroyCancellationToken.Register(bloodyScreenCancellationTokenSource.Cancel); var tweenSequence = BITween.CreateSequence(); tweenSequence.Append( BITween.MoveToForward( bloodyContainer.SetOpacity, 0, 1, 0.25f, bloodyScreenCancellationTokenSource.Token) ); tweenSequence.Append( BITween.MoveToForward( bloodyContainer.SetOpacity, 1, 0, 0.5f, bloodyScreenCancellationTokenSource.Token) ); tweenSequence.Play(bloodyScreenCancellationTokenSource.Token).Forget(); } private void OnInventory(InputAction.CallbackContext context) { if(playerService.LocalPlayer is null) return; if(_health.IsAlive is false) return; if(context is {interaction:PressInteraction,performed:true}) UXService.Entry(); } private void Update() { if (_health is not null) { _currentHealthLerp = Mathf.MoveTowards(_currentHealthLerp, _health.HealthPoint * 0.01f, Time.deltaTime * 5).Fix(); _currentHealthLerp = Mathf.Clamp(_currentHealthLerp, 0, 1); lerpHealthBar.SetDirect(_currentHealthLerp, _health.HealthPoint.ToString()); staminaBar.Set(_playerMovement.Stamina * 0.01f); staminaBar.visualElement.parent.SetActive(_playerMovement.Stamina is not 100); crosshairImage.visualElement.SetActive(_equipService.AllowAttack); crosshairImage.visualElement.transform.scale = Vector3.one * Mathf.Lerp(1.25f, 1, _equipService.Stable); if (_health.HealthPoint < 50) { injuryContainer.SetOpacity( 0.8f + Mathf.Sin(Time.time * 10) * 0.2f ); } if (_selector.TryGetCurrentSelectable(out var selectable)) { UXOnScreenPrompts.Get(_interactiveKey, selectable.Transform.TryGetComponent(out var description) ? description.Name : "互动"); // switch (selectable,selectable.Transform.GetComponentInChildren()) // { // case (not null, { } condition): // if (condition.Allow) // { // UXOnScreenPrompts.Get(_interactiveKey, // selectable.Transform.TryGetComponent(out var description) // ? description.Name // : "互动"); // } // else // { // UXOnScreenPrompts.Get(_interactiveKey, // selectable.Transform.TryGetComponent(out var description) // ? description.Name // : $"互动\t{condition.Reason}"); // } // break; // case (not null, null): // { // UXOnScreenPrompts.Get(_interactiveKey, // selectable.Transform.TryGetComponent(out var description) // ? description.Name // : "互动"); // } // break; // } } } if (_knockdown is {IsKnockdown:true}) { knockedFill.style.width = new StyleLength(Length.Percent(_knockdown.KnockedHealth)); } countLabel.SetVisible(false); if (_equipment is not null) { if (_equipment.CurrentItem is not null) { switch (_equipment.CurrentItem) { case var x when x.TryGetProperty(out var clip): countLabel.text = clip.ToString(); countLabel.SetVisible(true); break; case var x when x.TryGetProperty(out var count): countLabel.text = count.Count.ToString(); countLabel.SetVisible(true); break; } } } } private void LateUpdate() { if (playerService.LocalPlayer is null) return; if(_equipService is null) return; var currentCrosshairOpacity = crosshairParentImage.visualElement.style.opacity.value; crosshairParentImage.visualElement.style.opacity = _equipService.AllowScope?0: Mathf.Lerp(currentCrosshairOpacity, _equipService.Zoom.Allow ? 0 : 1, Time.deltaTime * 5); { var crossHairElement = crosshairImage.visualElement; var pos = crossHairElement.GetPosition(); var targetPos = crossHairElement.GetScreenPosition(_entityMovement.FocusPoint); crossHairElement.SetPosition( Vector2.Lerp(pos, targetPos, 8 * Time.deltaTime) ); } { var movementVelocity = _entityMovement.LocomotionBasedVelocity * 16; var velocity = -_entityMovement.AngularVelocity * 10 -new Vector3(-movementVelocity.z,movementVelocity.x); var newPos = new Vector3() { x = velocity.y, y = velocity.x, }; foreach (var x in swayElements) { var currentPos = x.transform.position; newPos = Vector3.Lerp(currentPos, newPos, Time.deltaTime * 10); x.transform.position = newPos; } } } private static void OnReturn(InputAction.CallbackContext context) { if (context.JustPressed()) UXService.Entry(); } private void OnSetAlive(bool alive) { if (alive is false) { Entry(); } crosshairImage.SetActive(alive); crosshairParentImage.SetActive(alive); playerInfo.SetActive(alive); OnRevive(); } private void OnSetHP(int hp) { healthBar.Set(Mathf.Clamp(hp,0,100) * 0.01f); injuryContainer.SetOpacity( hp< 60 ? Mathf.InverseLerp(40,60,hp) : 0 ); } } }