244 lines
6.7 KiB
C#
244 lines
6.7 KiB
C#
using System.Collections.Generic;
|
|
using Game.Features.Interactions.Application;
|
|
using Game.Features.Interactions.Contracts;
|
|
using Game.Features.Interactions.Domain;
|
|
using Game.Features.Items.Contracts;
|
|
using Game.Features.Players.Contracts;
|
|
using Game.Godot;
|
|
using Game.Godot.Exceptions;
|
|
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 IInteractionHandler _interactions;
|
|
private PlayerId _playerId;
|
|
private readonly HashSet<Hand> _dropActions = [];
|
|
private readonly ResolvePickupAvailability _resolvePickupAvailability = new();
|
|
|
|
private ItemUseRequest CreateUseRequest(Hand hand, ItemActionPhase phase)
|
|
{
|
|
InteractionTargetId? targetId = InteractionRaycaster
|
|
.GetTargetOrNull<IInteractionTarget>()
|
|
?.InteractionTargetId;
|
|
|
|
return new ItemUseRequest(_playerId, hand, phase, targetId);
|
|
}
|
|
|
|
private void ProcessHandActionStarted(Hand hand)
|
|
{
|
|
if (Input.IsActionPressed(InputAction.DropModifier.ActionName()))
|
|
{
|
|
_items.TryUse(new ItemUseRequest(_playerId, hand, ItemActionPhase.Cancelled));
|
|
_items.TryDrop(hand, CalculateDropPosition());
|
|
_dropActions.Add(hand);
|
|
return;
|
|
}
|
|
|
|
if (!_items.IsHandFree(hand))
|
|
{
|
|
_items.TryUse(CreateUseRequest(hand, ItemActionPhase.Started));
|
|
return;
|
|
}
|
|
|
|
IPickupTarget target = InteractionRaycaster.GetTargetOrNull<IPickupTarget>();
|
|
if (target is not null)
|
|
{
|
|
_items.TryPickup(target.ItemInfo.Id, hand);
|
|
}
|
|
}
|
|
|
|
private void ProcessHandActionCompleted(Hand hand)
|
|
{
|
|
if (_dropActions.Remove(hand))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_items.TryUse(new ItemUseRequest(_playerId, hand, ItemActionPhase.Completed));
|
|
}
|
|
|
|
private Vector3 CalculateDropPosition()
|
|
{
|
|
DropRaycaster.ForceRaycastUpdate();
|
|
|
|
bool isBlocked = DropRaycaster.IsColliding();
|
|
DropRayHit hit = new(
|
|
Camera.GlobalPosition,
|
|
-Camera.GlobalBasis.Z.Normalized(),
|
|
DropRaycaster.TargetPosition.Length(),
|
|
isBlocked,
|
|
isBlocked ? DropRaycaster.GetCollisionPoint() : default
|
|
);
|
|
|
|
return DropPosition.CalculateFrom(hit);
|
|
}
|
|
|
|
private void UpdatePrompt()
|
|
{
|
|
IPickupTarget pickupTarget = InteractionRaycaster.GetTargetOrNull<IPickupTarget>();
|
|
if (pickupTarget is not null)
|
|
{
|
|
ShowPickupPrompt(pickupTarget.ItemInfo);
|
|
return;
|
|
}
|
|
|
|
IInteractionTarget interactionTarget = InteractionRaycaster.GetTargetOrNull<IInteractionTarget>();
|
|
if (interactionTarget?.CanInteract == true)
|
|
{
|
|
PromptController.ShowPrompt($"[E] {interactionTarget.Prompt.Text}");
|
|
return;
|
|
}
|
|
|
|
PromptController.Clear();
|
|
}
|
|
|
|
private void ShowPickupPrompt(ItemInfo itemInfo)
|
|
{
|
|
PickupAvailability availability = _resolvePickupAvailability.Resolve(
|
|
itemInfo.HandRequirement,
|
|
_items.IsHandFree(Hand.Left),
|
|
_items.IsHandFree(Hand.Right)
|
|
);
|
|
|
|
if (availability.RequiresBoth)
|
|
{
|
|
PromptController.ShowPrompt(
|
|
availability.LeftHand
|
|
? $"[LPM lub PPM] Podnieś {itemInfo.DisplayName} — obie ręce"
|
|
: "Potrzebujesz obu wolnych rąk"
|
|
);
|
|
return;
|
|
}
|
|
|
|
List<string> actions = [];
|
|
|
|
if (availability.LeftHand)
|
|
{
|
|
actions.Add($"[LPM] Podnieś {itemInfo.DisplayName} — lewa ręka");
|
|
}
|
|
|
|
if (availability.RightHand)
|
|
{
|
|
actions.Add($"[PPM] Podnieś {itemInfo.DisplayName} — prawa ręka");
|
|
}
|
|
|
|
PromptController.ShowPrompt(
|
|
actions.Count > 0 ? string.Join("\n", actions) : "Brak wolnej ręki"
|
|
);
|
|
}
|
|
|
|
public void Initialize(
|
|
PlayerId playerId,
|
|
IItemInteractionService items,
|
|
IInteractionHandler interactions
|
|
)
|
|
{
|
|
_playerId = playerId;
|
|
_items = items;
|
|
_interactions = interactions;
|
|
}
|
|
|
|
public Vector3 GetItemSpawnPosition() => CalculateDropPosition();
|
|
|
|
public override void _Ready()
|
|
{
|
|
this.AssertRequiredExports();
|
|
}
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
if (_items is null || _interactions is null)
|
|
{
|
|
throw new ControllerNotInitializedException(Name);
|
|
}
|
|
|
|
if (GamePause.IsPaused || !MouseCapture.IsCaptured)
|
|
{
|
|
PromptController.Clear();
|
|
return;
|
|
}
|
|
|
|
UpdatePrompt();
|
|
}
|
|
|
|
public override void _UnhandledInput(InputEvent @event)
|
|
{
|
|
if (_items is null || _interactions is null)
|
|
{
|
|
throw new ControllerNotInitializedException(Name);
|
|
}
|
|
|
|
if (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.GetTargetOrNull<IInteractionTarget>();
|
|
if (target?.CanInteract == true)
|
|
{
|
|
_interactions.TryInteract(
|
|
new InteractionRequest(_playerId, target.InteractionTargetId)
|
|
);
|
|
}
|
|
|
|
GetViewport().SetInputAsHandled();
|
|
}
|
|
}
|
|
}
|