using System.Collections.Generic; using Game.Features.Interactions.Contracts; using Game.Features.Items.Contracts; using Game.Features.Players.Contracts; using Game.Godot; using Game.Godot.Extensions; using Game.Godot.Mappings; using Godot; namespace Game.Features.Interactions.Godot; public partial class PlayerInteractionController : Node, IItemSpawnPoint { [Export] public Camera3D Camera; [Export] public CharacterBody3D Body; [Export] public InteractionRaycaster InteractionRaycaster; [Export] public RayCast3D DropRaycaster; [Export] public InteractionPromptController PromptController; [Export] public Node3D LeftHandAnchor; [Export] public Node3D RightHandAnchor; private IItemInteractionService _items; private IInteractionService _interactions; private PlayerId _playerId; private readonly HashSet _dropActions = []; private readonly Dictionary _activeUseTargets = []; public override void _Ready() { this.AssertRequiredExports(); } public void Initialize( PlayerId playerId, IItemInteractionService items, IInteractionService interactions ) { _playerId = playerId; _items = items; _interactions = interactions; } public override void _Process(double delta) { if (_items is null || _interactions is null || GamePause.IsPaused || !MouseCapture.IsCaptured) { PromptController.Clear(); return; } UpdatePrompt(); } public override void _UnhandledInput(InputEvent @event) { if (_items is null || _interactions is null || GamePause.IsPaused || !MouseCapture.IsCaptured) { return; } if (@event.IsActionPressed(InputAction.UseLeftHand.ActionName())) { ProcessHandActionStarted(Hand.Left); GetViewport().SetInputAsHandled(); return; } if (@event.IsActionReleased(InputAction.UseLeftHand.ActionName())) { ProcessHandActionCompleted(Hand.Left); GetViewport().SetInputAsHandled(); return; } if (@event.IsActionPressed(InputAction.UseRightHand.ActionName())) { ProcessHandActionStarted(Hand.Right); GetViewport().SetInputAsHandled(); return; } if (@event.IsActionReleased(InputAction.UseRightHand.ActionName())) { ProcessHandActionCompleted(Hand.Right); GetViewport().SetInputAsHandled(); return; } if (@event.IsActionPressed(InputAction.Interact.ActionName())) { IInteractionTarget target = InteractionRaycaster.GetTarget(); if (target?.CanInteract == true) { _interactions.TryInteract(new InteractionRequest(_playerId, target.Id)); } GetViewport().SetInputAsHandled(); } } public Vector3 GetItemSpawnPosition() => CalculateDropPosition(); private void ProcessHandActionStarted(Hand hand) { if (Input.IsActionPressed(InputAction.DropModifier.ActionName())) { if (_activeUseTargets.Remove(hand, out InteractionTargetId? activeTargetId)) { _items.TryUse(new ItemUseRequest( _playerId, hand, ItemActionPhase.Cancelled, activeTargetId )); } _items.TryDrop(hand, CalculateDropPosition()); _dropActions.Add(hand); return; } if (!_items.IsHandFree(hand)) { ItemUseRequest request = CreateUseRequest(hand, ItemActionPhase.Started); ItemActionResult result = _items.TryUse(request); if (result.Success) { _activeUseTargets[hand] = request.TargetId; } return; } IPickupTarget target = InteractionRaycaster.GetTarget(); if (target is not null) { _items.TryPickup(target.ItemInfo.Id, hand); } } private void ProcessHandActionCompleted(Hand hand) { if (_dropActions.Remove(hand)) { return; } if (_activeUseTargets.Remove(hand, out InteractionTargetId? targetId)) { _items.TryUse(new ItemUseRequest(_playerId, hand, ItemActionPhase.Completed, targetId)); } } private ItemUseRequest CreateUseRequest(Hand hand, ItemActionPhase phase) { InteractionTargetId? targetId = InteractionRaycaster.GetTarget()?.Id; return new ItemUseRequest(_playerId, hand, phase, targetId); } private Vector3 CalculateDropPosition() { DropRaycaster.ForceRaycastUpdate(); Vector3 origin = Camera.GlobalPosition; Vector3 direction = -Camera.GlobalBasis.Z.Normalized(); float distance = DropRaycaster.TargetPosition.Length(); if (DropRaycaster.IsColliding()) { distance = Mathf.Max(0.25f, origin.DistanceTo(DropRaycaster.GetCollisionPoint()) - 0.2f); } return origin + direction * distance; } private void UpdatePrompt() { IPickupTarget pickupTarget = InteractionRaycaster.GetTarget(); if (pickupTarget is not null) { ShowPickupPrompt(pickupTarget.ItemInfo); return; } IInteractionTarget interactionTarget = InteractionRaycaster.GetTarget(); if (interactionTarget?.CanInteract == true) { PromptController.ShowPrompt($"[E] {interactionTarget.Prompt.Text}"); return; } PromptController.Clear(); } private void ShowPickupPrompt(ItemInfo itemInfo) { if (itemInfo.HandRequirement == HandRequirement.Both) { bool bothHandsFree = _items.IsHandFree(Hand.Left) && _items.IsHandFree(Hand.Right); PromptController.ShowPrompt(bothHandsFree ? $"[LPM lub PPM] Podnieś {itemInfo.DisplayName} — obie ręce" : "Potrzebujesz obu wolnych rąk"); return; } List actions = []; if (_items.IsHandFree(Hand.Left)) { actions.Add($"[LPM] Podnieś {itemInfo.DisplayName} — lewa ręka"); } if (_items.IsHandFree(Hand.Right)) { actions.Add($"[PPM] Podnieś {itemInfo.DisplayName} — prawa ręka"); } PromptController.ShowPrompt(actions.Count > 0 ? string.Join("\n", actions) : "Brak wolnej ręki"); } }