using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Security.Permissions; using System.Threading; using BITFALL.Entities; using BITFALL.Entities.Armor; using BITFALL.Entities.Equipment; using BITFALL.Entities.Inventory; using BITFALL.Guns; using BITFALL.Items.Armor; using BITFALL.Player.Equip; using BITFALL.Player.Inventory; 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.Events; using BITKit.SceneManagement; using BITKit.Selection; using BITKit.Steamwork; using BITKit.Tween; using Cysharp.Threading.Tasks; using UnityEditor; using UnityEngine.Events; 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; [Header(Constant.Header.Input)] [SerializeField] private InputActionReference inventoryAction; [SerializeField] private InputActionReference returnAction; [SerializeField] private InputActionReference inspectAction; [SerializeField] private InputActionReference radialMenuAction; [Header(Constant.Header.Input)] [SerializeField] private InputActionReference interactiveAction; [Header(Constant.Header.Prefabs)] [SerializeField] private VisualTreeAsset armorTemplate; [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; [Inject] private IPlayerInventory _playerInventory; [Inject] private IEntityInventory _entityInventory; [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; [UXBindPath("money-label")] private Label moneyLabel; [UXBindPath("armor-container")] private VisualElement armorContainer; private VisualElement[] swayElements; private float _currentHealthLerp; protected CancellationTokenSource bloodyScreenCancellationTokenSource; private string _interactiveKey="F"; private int ammoCount; protected override void Start() { base.Start(); sceneService.OnSceneLoaded += x=>Entry(); playerService.OnPlayerInitialized += OnPlayerInitializedLocalPlayer; playerService.OnPlayerDisposed += OnPlayerDisposed; BITKit.UX.UXUtils.Inject(this); bloodyContainer.SetOpacity(0); injuryContainer.SetOpacity(0); swayElements = document.rootVisualElement.Query(className: "swap").ToList().ToArray(); document.rootVisualElement.SetActive(false); } private void OnPlayerDisposed(Entity obj) { if (document) document.rootVisualElement.SetActive(false); } protected override void OnPanelEntry() { base.OnPanelEntry(); inputActionGroup.RegisterCallback(returnAction, OnReturn); inputActionGroup.RegisterCallback(inventoryAction, OnInventory); inputActionGroup.RegisterCallback(inspectAction, OnInspect); inputActionGroup.RegisterCallback(radialMenuAction, OnRadialMenu); } protected override void OnPanelExit() { base.OnPanelExit(); inputActionGroup.UnRegisterCallback(returnAction, OnReturn); inputActionGroup.UnRegisterCallback(inventoryAction, OnInventory); inputActionGroup.UnRegisterCallback(inspectAction, OnInspect); inputActionGroup.UnRegisterCallback(radialMenuAction, OnRadialMenu); } private void OnInspect(InputAction.CallbackContext obj) { switch (obj,_equipment) { case ({interaction:HoldInteraction,performed:true}, {CurrentItem: not null}): UXInventoryInspector.Entry(_equipment.CurrentItem); break; } } private static void OnRadialMenu(InputAction.CallbackContext obj) { // Debug.Log(obj); switch (obj) { case {interaction:HoldInteraction,performed:true}: UXService.Entry(); break; case {interaction:HoldInteraction,canceled:true}: 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) { document.rootVisualElement.SetActive(true); unityEntity.Inject(this); _health.OnDamageRelease += OnDamageRelease; _health.OnSetAlive += OnSetAlive; _health.OnSetHealthPoint += OnSetHP; _selector.OnInactive += OnInactive; _selector.OnSelected += OnSelect; _armor.OnPlatesChanged += OnPlatesChanged; _knockdown.OnKnockdown+=OnKnockdown; _knockdown.OnRevive+=OnRevive; _entityInventory.OnAdd += _ => UpdateAmmo(); _entityInventory.OnRemove += _ => UpdateAmmo(); _entityInventory.OnSet += _ => UpdateAmmo(); _equipment.OnEquip += _ => UpdateAmmo(); if (steamService.IsInitialized) { playerNameLabel.Set(steamService.Name); var avatar = await steamService.GetAvatarAsync(cancellationToken); playerAvatarImage.SetTexture(avatar); } _equipService = unityEntity.Get(); OnSetHP(_health.HealthPoint); OnSetAlive(_health.IsAlive); OnInactive(null); OnPlatesChanged(_armor.Plates); OnRevive(); _interactiveKey = interactiveAction.GetKeyMap(); } private void OnPlatesChanged(IBasicItem[] obj) { armorContainer.Clear(); for (var i = 0; i < _armor.PlateCapacity; i++) { var element = new UXContainer(armorContainer.Create(armorTemplate)); if (!obj.IndexInRange(i)) { element.visualElement.style.width = 50; continue; } //#1285C6 //element.visualElement[0].style.backgroundColor = new StyleColor(new Color(0x12/255f,0x85/255f,0xC6/255f)); var x = obj[i]; element.visualElement[0].style.backgroundColor = AssetableItem.GetQualityColor(x.Quality); element.visualElement.style.width = x.GetOrCreateProperty().CurrentPoint*0x2; } } private void OnRevive() { knockedFill.SetActive(false); healthFill.SetActive(true); } private void OnKnockdown() { knockedFill.SetActive(true); healthFill.SetActive(false); } private void UpdateAmmo() { if (_equipment.CurrentItem is null) return; if (_equipment.CurrentItem.TryGetProperty(out var clip) is false) return; ammoCount = _entityInventory.GetItems() .Sum(x => { if (x.TryGetProperty(out var nextClip) is false) return 0; return nextClip.AddressablePath != clip.AddressablePath ? 0 : nextClip.Remaining; }); } 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) 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 : "互动"); } } if (_knockdown is {IsKnockdown:true}) { knockedFill.style.width = new StyleLength(Length.Percent(_knockdown.KnockedHealth)); } countLabel.SetVisible(false); if (_playerInventory is not null) { moneyLabel.text = _playerInventory.Money.ToString(); } if (_equipment?.CurrentItem != null) { switch (_equipment.CurrentItem) { case var x when x.TryGetProperty(out var clip): countLabel.text = $"{clip.Remaining}/{ammoCount}"; 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 is {interaction:PressInteraction,performed:true}) 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 ); } } }