A bit wonky but working switching

This commit is contained in:
Michal Pikulski
2025-12-15 00:57:50 +01:00
parent 8995dd1949
commit b14fdfbe68
12 changed files with 282 additions and 153 deletions

View File

@@ -6,6 +6,7 @@ using UnityEngine.Events;
using System.Threading.Tasks;
using Core;
using Core.Lifecycle;
using Utils;
namespace Interactions
{
@@ -34,7 +35,7 @@ namespace Interactions
public UnityEvent characterArrived;
public UnityEvent<bool> interactionComplete;
private PlayerTouchController playerRef;
private IInteractingCharacter _interactingCharacter;
protected FollowerController FollowerController;
private bool isActive = true;
@@ -69,7 +70,7 @@ namespace Interactions
/// <summary>
/// Dispatch an interaction event to all registered actions and await their completion
/// </summary>
private async Task DispatchEventAsync(InteractionEventType eventType)
private async Task DispatchEventAsync(InteractionEventType eventType, PlayerTouchController playerRef = null)
{
// Collect all tasks from actions that want to respond
List<Task<bool>> tasks = new List<Task<bool>>();
@@ -114,26 +115,45 @@ namespace Interactions
/// </summary>
private async Task StartInteractionFlowAsync()
{
// 2. Find characters
playerRef = FindFirstObjectByType<PlayerTouchController>();
// 2. Find characters - get the ACTIVE controller from InputManager
BasePlayerMovementController playerController = null;
if (InputManager.Instance != null)
{
// Get the controller that currently has input control
var activeController = InputManager.Instance.GetActiveController();
playerController = activeController as BasePlayerMovementController;
}
// Fallback: if InputManager doesn't have an active controller, try to find PlayerTouchController specifically
if (playerController == null)
{
playerController = FindFirstObjectByType<PlayerTouchController>();
Logging.Warning("[Interactable] No active controller from InputManager, falling back to FindFirstObjectByType<PlayerTouchController>");
}
_interactingCharacter = playerController;
FollowerController = FindFirstObjectByType<FollowerController>();
// For legacy event compatibility, try to get PlayerTouchController reference
var playerRef = playerController as PlayerTouchController;
// 3. Virtual hook: Setup
OnInteractionStarted();
// 4. Fire events
interactionStarted?.Invoke(playerRef, FollowerController);
await DispatchEventAsync(InteractionEventType.InteractionStarted);
await DispatchEventAsync(InteractionEventType.InteractionStarted, playerRef);
// 5. Orchestrate character movement
await MoveCharactersAsync();
await MoveCharactersAsync(playerRef);
// 6. Virtual hook: Arrival reaction
OnInteractingCharacterArrived();
// 7. Fire arrival events
characterArrived?.Invoke();
await DispatchEventAsync(InteractionEventType.InteractingCharacterArrived);
await DispatchEventAsync(InteractionEventType.InteractingCharacterArrived, playerRef);
// 8. Validation (base + child)
var (canProceed, errorMessage) = ValidateInteraction();
@@ -143,7 +163,7 @@ namespace Interactions
{
DebugUIMessage.Show(errorMessage, Color.yellow);
}
FinishInteraction(false);
FinishInteraction(false, playerRef);
return;
}
@@ -151,7 +171,7 @@ namespace Interactions
bool success = DoInteraction();
// 10. Finish up
FinishInteraction(success);
FinishInteraction(success, playerRef);
}
#region Virtual Lifecycle Methods
@@ -262,13 +282,13 @@ namespace Interactions
/// <summary>
/// Orchestrates character movement based on characterToInteract setting.
/// </summary>
private async Task MoveCharactersAsync()
private async Task MoveCharactersAsync(PlayerTouchController playerRef = null)
{
if (playerRef == null)
if (_interactingCharacter == null)
{
Logging.Debug($"[Interactable] Player character could not be found. Aborting interaction.");
Logging.Debug($"[Interactable] No interacting character found. Aborting interaction.");
interactionInterrupted.Invoke();
await DispatchEventAsync(InteractionEventType.InteractionInterrupted);
await DispatchEventAsync(InteractionEventType.InteractionInterrupted, playerRef);
return;
}
@@ -278,31 +298,42 @@ namespace Interactions
return; // Continue to arrival
}
// Move player and optionally follower based on characterToInteract setting
// Move the appropriate character based on characterToInteract setting
if (characterToInteract == CharacterToInteract.Trafalgar)
{
await MovePlayerAsync();
await MoveCharacterAsync(_interactingCharacter, CharacterToInteract.Trafalgar, playerRef);
}
else if (characterToInteract == CharacterToInteract.Pulver || characterToInteract == CharacterToInteract.Both)
else if (characterToInteract == CharacterToInteract.Pulver)
{
await MovePlayerAsync(); // Move player to range first
await MoveFollowerAsync(); // Then move follower to interaction point
await MoveCharacterAsync(_interactingCharacter, CharacterToInteract.Pulver, playerRef);
}
else if (characterToInteract == CharacterToInteract.Both)
{
await MoveCharacterAsync(_interactingCharacter, CharacterToInteract.Trafalgar, playerRef); // Move first character to range
await MoveFollowerAsync(playerRef); // Then move follower to interaction point
}
}
/// <summary>
/// Moves the player to the interaction point or custom target.
/// Moves a character controller to the interaction point or custom target.
/// Works with any controller implementing IInteractingCharacter.
/// </summary>
private async Task MovePlayerAsync()
private async Task MoveCharacterAsync(IInteractingCharacter character, CharacterToInteract targetCharacterType, PlayerTouchController playerRef = null)
{
if (character == null)
{
Logging.Warning("[Interactable] Cannot move null character");
return;
}
Vector3 stopPoint = transform.position; // Default to interactable position
bool customTargetFound = false;
// Check for a CharacterMoveToTarget component for Trafalgar or Both
// Check for a CharacterMoveToTarget component
CharacterMoveToTarget[] moveTargets = GetComponentsInChildren<CharacterMoveToTarget>();
foreach (var target in moveTargets)
{
if (target.characterType == CharacterToInteract.Trafalgar || target.characterType == CharacterToInteract.Both)
if (target.characterType == targetCharacterType || target.characterType == CharacterToInteract.Both)
{
stopPoint = target.GetTargetPosition();
customTargetFound = true;
@@ -314,49 +345,26 @@ namespace Interactions
if (!customTargetFound)
{
Vector3 interactablePos = transform.position;
Vector3 playerPos = playerRef.transform.position;
float stopDistance = characterToInteract == CharacterToInteract.Pulver
Vector3 characterPos = character.transform.position;
float stopDistance = targetCharacterType == CharacterToInteract.Pulver
? GameManager.Instance.PlayerStopDistance
: GameManager.Instance.PlayerStopDistanceDirectInteraction;
Vector3 toPlayer = (playerPos - interactablePos).normalized;
stopPoint = interactablePos + toPlayer * stopDistance;
stopPoint = MovementUtilities.CalculateStopPosition(interactablePos, characterPos, stopDistance);
}
// Wait for player to arrive
var tcs = new TaskCompletionSource<bool>();
// Use MovementUtilities to handle movement
bool arrived = await MovementUtilities.MoveToPositionAsync(character, stopPoint);
void OnPlayerArrivedLocal()
if (!arrived)
{
if (playerRef != null)
{
playerRef.OnArrivedAtTarget -= OnPlayerArrivedLocal;
playerRef.OnMoveToCancelled -= OnPlayerMoveCancelledLocal;
}
tcs.TrySetResult(true);
_ = HandleInteractionCancelledAsync(playerRef);
}
void OnPlayerMoveCancelledLocal()
{
if (playerRef != null)
{
playerRef.OnArrivedAtTarget -= OnPlayerArrivedLocal;
playerRef.OnMoveToCancelled -= OnPlayerMoveCancelledLocal;
}
_ = HandleInteractionCancelledAsync();
tcs.TrySetResult(false);
}
playerRef.OnArrivedAtTarget += OnPlayerArrivedLocal;
playerRef.OnMoveToCancelled += OnPlayerMoveCancelledLocal;
playerRef.MoveToAndNotify(stopPoint);
await tcs.Task;
}
/// <summary>
/// Moves the follower to the interaction point or custom target.
/// </summary>
private async Task MoveFollowerAsync()
private async Task MoveFollowerAsync(PlayerTouchController playerRef = null)
{
if (FollowerController == null)
return;
@@ -383,7 +391,7 @@ namespace Interactions
FollowerController.OnPickupArrived -= OnFollowerArrivedLocal;
}
// Tell follower to return to player
// Tell follower to return to player if we have a PlayerTouchController reference
if (FollowerController != null && playerRef != null)
{
FollowerController.ReturnToPlayer(playerRef.transform);
@@ -401,10 +409,10 @@ namespace Interactions
/// <summary>
/// Handles interaction being cancelled (player stopped moving).
/// </summary>
private async Task HandleInteractionCancelledAsync()
private async Task HandleInteractionCancelledAsync(PlayerTouchController playerRef = null)
{
interactionInterrupted?.Invoke();
await DispatchEventAsync(InteractionEventType.InteractionInterrupted);
await DispatchEventAsync(InteractionEventType.InteractionInterrupted, playerRef);
}
#endregion
@@ -414,14 +422,14 @@ namespace Interactions
/// <summary>
/// Finalizes the interaction after DoInteraction completes.
/// </summary>
private async void FinishInteraction(bool success)
private async void FinishInteraction(bool success, PlayerTouchController playerRef = null)
{
// Virtual hook: Cleanup
OnInteractionFinished(success);
// Fire completion events
interactionComplete?.Invoke(success);
await DispatchEventAsync(InteractionEventType.InteractionComplete);
await DispatchEventAsync(InteractionEventType.InteractionComplete, playerRef);
// Handle one-time / cooldown
if (success)
@@ -437,7 +445,7 @@ namespace Interactions
}
// Reset state
playerRef = null;
_interactingCharacter = null;
FollowerController = null;
}