using System; using System.Linq; using BITFALL.Hotkey; using BITKit.PlayerCamera; using BITKit.Selection; using BITKit.Sensors; using BITKit.UX; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Interactions; namespace BITKit.Entities.Player { [CustomType(typeof(ISelector))] public class EntityInteractive : EntityPlayerBehavior,ISelector,IHotkeyProvider { [Header(Constant.Header.Settings)] [SerializeReference, SubclassSelector] private ISensor sensor; [Header(Constant.Header.Gameobjects)] [SerializeField] private Transform sensorTransform; [Header(Constant.Header.Input)] [SerializeField] private InputActionReference interactiveAction; [Header(Constant.Header.InternalVariables)] private ISelectable selected; private IntervalUpdate cd = new(0.08f); [Inject] private IHealth _health; [Inject(true)] private IEntityMovement _movement; [Inject(true)] IPlayerCameraService _cameraService; [Inject(true)] private IUXPopup _popup; [Inject(true)] private IHotkeyCollection _hotkeyCollection; [Inject(true)] private InputActionGroup _inputActionGroup; private bool _isBusy; public override void OnStart() { _health.OnSetAlive += OnSetAlive; _inputActionGroup.RegisterCallback(interactiveAction, Interactive); } private void OnSetAlive(bool obj) { TryDeSelected(); } public override async void OnUpdate(float deltaTime) { if (_cameraService is not null && _movement is not null) { if (_cameraService.IsCameraActivated) { sensorTransform.SetPositionAndRotation(_cameraService.CameraPosition, _cameraService.CameraRotation); } else { sensorTransform.position = _movement.Position + _movement.Rotation * _movement.ViewCenter; sensorTransform.rotation = _movement.ViewRotation; } } if (_isBusy) return; _isBusy = true; await sensor.Execute(deltaTime); try { var nextDetected = sensor.Get().FirstOrDefault(); if (nextDetected) { if (nextDetected.TryGetComponentAny(out var _detected)) { if (_detected == selected) { } else { TryDeSelected(); Detected(_detected); } } else { TryDeSelected(); } } else { TryDeSelected(); } } catch(MissingReferenceException e) {} catch (Exception e) { BIT4Log.LogException(e); } if (_hotkeyCollection is not null) { if (selected is MonoBehaviour mono && mono) { _hotkeyCollection.Register(this); } else { _hotkeyCollection.UnRegister(this); } } _isBusy = false; } private void TryDeSelected() { if (selected is null) return; selected.SetSelectionState(SelectionState.None); OnInactive?.Invoke(selected); selected = null; } private void Detected(ISelectable detected) { selected = detected; detected.SetSelectionState(SelectionState.Hover); OnSelected?.Invoke(selected); } public void Interactive(InputAction.CallbackContext context) { if (context.interaction is not PressInteraction || !context.performed ) return; if (cd.AllowUpdate is false) return; InteractiveInternal(); } private void InteractiveInternal() { var _selected = selected; if (_selected is not MonoBehaviour monoBehaviour || !monoBehaviour) return; switch (monoBehaviour.TryGetComponent(out var action),monoBehaviour.TryGetComponent(out var condition)) { case (true,true): if (condition.Allow) { action.Execute(); Activated(); }else { _popup?.Popup(condition.Reason); } break; case (true,false): action.Execute(); Activated(); break; case (false,true): if (condition.Allow) { Activated(); } else { _popup?.Popup(condition.Reason); } break; default: Activated(); break; } return; void Activated() { _selected.SetSelectionState(SelectionState.Active); OnActive?.Invoke(_selected); } } public bool TryGetCurrentSelectable(out ISelectable selectable) { selectable = selected; return selected?.Transform; } public event Action OnNone; public event Action OnHover; public event Action OnActive; public event Action OnInactive; public event Action OnFocus; public event Action OnSelected; public event Action OnEnabled; public event Action OnChecked; public event Action OnRoot; public string Name => "互动"; public string Description => selected switch { not null when selected.Transform.TryGetComponent(out var description)=> description.Name, not null=>"互动", _ => "没有选中物体", }; public object Data { get; set; }= null; public bool Enabled => true; public Action OnPerform => InteractiveInternal; } }