using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using BITKit; using BITKit.Entities; using BITKit.UX.Hotkey; using Lightbug.CharacterControllerPro.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Net.Project.B.Health; using Net.Project.B.Mark; using NodeCanvas.Framework; using Project.B.CharacterController; using Project.B.Player; using UnityEngine; using UnityEngine.Animations; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Interactions; using Object = UnityEngine.Object; namespace Net.Project.B.Interaction { public class PlayerInteractionController:IDisposable,IHotkeyCollection { private Transform CameraTransform => _cameraTransform ? _cameraTransform : _cameraTransform = Camera.main!.transform; private Transform _cameraTransform; private readonly Transform _transform; private readonly IMarkService _markService; private readonly IEntitiesService _entitiesService; private readonly IKnockedService _knockedService; private readonly IEntity _entity; private readonly ILogger _logger; private readonly IWorldInteractionService _interactionService; private readonly InputActionGroup _inputActionGroup; private readonly IBlackboard _blackBoard; private readonly IMainTicker _ticker; private readonly IFixedTicker _fixedTicker; private readonly ICharacterController _characterController; private readonly CharacterActor _characterActor; private readonly LayerMask _interactionLayer; private IWorldInteractable _currentInteractable; private IWorldInteractable _triggerInteractable; private readonly ConcurrentDictionary _interactions = new(); private readonly ConcurrentDictionary _hotkeyProviders = new(); public IEnumerable Hotkeys => _hotkeyProviders.Values; private bool AllowInteraction => _inputActionGroup.allowInput.Allow && _characterController.CurrentState is not null or ICharacterSeating; private bool _markNextFrame; private readonly GameObject _myMark; private readonly IEntity _markEntity; public PlayerInteractionController(IBlackboard blackBoard, IMainTicker ticker, IWorldInteractionService interactionService, InputActionGroup inputActionGroup, IPlayerKeyMap playerKeyMap, ICharacterController characterController, CharacterActor characterActor, ILogger logger, IEntity entity, IKnockedService knockedService, IFixedTicker fixedTicker, IEntitiesService entitiesService, IMarkService markService, Transform transform) { _blackBoard = blackBoard; _ticker = ticker; _interactionService = interactionService; _inputActionGroup = inputActionGroup; _characterController = characterController; _characterActor = characterActor; _logger = logger; _entity = entity; _knockedService = knockedService; _fixedTicker = fixedTicker; _entitiesService = entitiesService; _markService = markService; _transform = transform; _fixedTicker.Add(OnTick); _interactionLayer = blackBoard.GetVariable("layer_interaction").GetValue(); inputActionGroup.RegisterCallback(playerKeyMap.InteractiveKey, OnInteraction); inputActionGroup.RegisterCallback(playerKeyMap.MarkKey, OnMark); _myMark = new GameObject() { hideFlags = HideFlags.HideAndDontSave }; _markEntity = new Entity(); _markEntity.ServiceCollection.AddSingleton(_myMark.transform); _markEntity.ServiceCollection.AddSingleton(_myMark); _markEntity.ServiceCollection.AddSingleton(); _markEntity.ServiceCollection.AddSingleton(); _markEntity.ServiceCollection.AddSingleton(); _entitiesService.Register(_markEntity); } private void OnMark(InputAction.CallbackContext obj) { if(BITAppForUnity.AllowCursor.Allow)return; if (obj.JustPressed()) { _markNextFrame = true; } } private void OnInteraction(InputAction.CallbackContext obj) { var interactable = _currentInteractable?? _triggerInteractable; if(interactable is null)return; switch (obj) { case { interaction: PressInteraction, performed: true }: { _interactionService.Interaction(_entity, interactable); } break; case { interaction: PressInteraction, canceled: true }: { _interactionService.Interaction(_entity, interactable,WorldInteractionProcess.Cancel); } break; } } private void OnTick(float obj) { try { if (_markService.InMarking.Contains(_markEntity.Id)) { if (Vector3.Distance(_transform.position, _myMark.transform.position) < 1) { _markService.CancelMark(_markEntity.Id); } } var releaseTrigger = true; if (AllowInteraction) { foreach (var trigger in _characterActor.Triggers) { if (!trigger.collider3D) { continue; } var id = trigger.collider3D.gameObject.GetInstanceID(); if (_entitiesService.TryGetEntity(id, out var triggerEntity) is false) continue; if (triggerEntity.ServiceProvider.QueryComponents( out IWorldInteractable worldInteractable) is false) continue; worldInteractable.WorldObject = trigger.gameObject; worldInteractable.Id = id; if (_currentInteractable == worldInteractable) { releaseTrigger = false; continue; } _triggerInteractable = worldInteractable; _interactionService.Interaction(_entity, _triggerInteractable, WorldInteractionProcess.Hover); return; } } if (releaseTrigger || AllowInteraction is false) { if (_triggerInteractable is not null) { _interactionService.Interaction(_entity, _triggerInteractable, WorldInteractionProcess.None); _triggerInteractable = null; } } if (AllowInteraction is false) { DisposeCurrentInteractable(); return; } if (Physics.Raycast(CameraTransform.position, CameraTransform.forward, out var hit, 512, _interactionLayer) is false) { DisposeCurrentInteractable(); return; } if (_markNextFrame && hit.transform) { var id = hit.transform.gameObject.GetInstanceID(); var colliderId = hit.collider.gameObject.GetInstanceID(); if (_entitiesService.Entities.ContainsKey(id)) { }else if (_entitiesService.Entities.ContainsKey(colliderId)) { id = colliderId; }else if (hit.collider.GetComponentInParent() is {} parentEntity) { id = parentEntity.Id; } else { _myMark.transform.position = hit.point; if (hit.rigidbody) { _myMark.transform.SetParentConstraint(hit.transform); } else if(_myMark.transform.TryGetComponent(out var parentConstraint)) { Object.Destroy(parentConstraint); _myMark.transform.position = hit.point; } id = _markEntity.Id; if (_markService.InMarking.Contains(id)) { id = -9999; } } if (id != _characterActor.gameObject.GetInstanceID()) { if (_markService.InMarking.Contains(id)) { _markService.CancelMark(id); } else { _markService.Mark(id); } } } if (_knockedService.Knocked.Contains(_entity.Id)) { DisposeCurrentInteractable(); return; } if (Vector3.Distance(_characterController.Center, hit.point) > 2) { DisposeCurrentInteractable(); return; } IWorldInteractable newInteractable = null; try { if (_entitiesService.TryGetEntity(hit.transform.gameObject.GetInstanceID(), out var hitEntity)) { Check(hitEntity); } if (_entitiesService.TryGetEntity(hit.collider.gameObject.GetInstanceID(), out hitEntity)) { Check(hitEntity); } if (hit.transform.GetComponentInParent() is { } e) { Check(e); } DisposeCurrentInteractable(); return; void Check(IEntity entity) { if (!entity.ServiceProvider.QueryComponents(out IWorldInteractable interactable, out GameObject gameObject)) return; if(interactable.Enabled is false)return; newInteractable = interactable; newInteractable.WorldObject = gameObject; newInteractable.Id = hit.transform.gameObject.GetInstanceID(); throw new OperationCanceledException(); } } catch (OperationCanceledException) { } if (_currentInteractable == newInteractable) { return; } DisposeCurrentInteractable(); _interactionService.Interaction(_entity, newInteractable, WorldInteractionProcess.Hover); _currentInteractable = newInteractable; } finally { _markNextFrame = false; } } public bool IsInteractable(Transform transform, out IWorldInteractable interactable) { interactable = null; return false; } public void Dispose() { _fixedTicker.Remove(OnTick); _entitiesService.UnRegister(_markEntity); } public void DisposeCurrentInteractable() { if (_currentInteractable is not null) { _hotkeyProviders.TryRemove(_currentInteractable.Id); _interactions.TryRemove(_currentInteractable.Id); _interactionService.Interaction(_entity, _currentInteractable, WorldInteractionProcess.None); _currentInteractable = null; } } public void Register(IHotkeyProvider hotkey) { throw new NotImplementedException(); } public void UnRegister(IHotkeyProvider hotkey) { throw new NotImplementedException(); } } }