maze_switching (#82)
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: #82
This commit is contained in:
@@ -34,7 +34,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 +69,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 +114,52 @@ 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();
|
||||
bool movementSucceeded = await MoveCharactersAsync(playerRef);
|
||||
|
||||
// If movement was cancelled, stop the interaction flow
|
||||
if (!movementSucceeded)
|
||||
{
|
||||
Logging.Debug($"[Interactable] Interaction cancelled due to movement failure on {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 +169,7 @@ namespace Interactions
|
||||
{
|
||||
DebugUIMessage.Show(errorMessage, Color.yellow);
|
||||
}
|
||||
FinishInteraction(false);
|
||||
FinishInteraction(false, playerRef);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -151,7 +177,7 @@ namespace Interactions
|
||||
bool success = DoInteraction();
|
||||
|
||||
// 10. Finish up
|
||||
FinishInteraction(success);
|
||||
FinishInteraction(success, playerRef);
|
||||
}
|
||||
|
||||
#region Virtual Lifecycle Methods
|
||||
@@ -260,151 +286,46 @@ namespace Interactions
|
||||
#region Character Movement Orchestration
|
||||
|
||||
/// <summary>
|
||||
/// Orchestrates character movement based on characterToInteract setting.
|
||||
/// Delegates movement to the interacting character's controller.
|
||||
/// Each controller implements its own movement behavior based on this interactable's settings.
|
||||
/// </summary>
|
||||
private async Task MoveCharactersAsync()
|
||||
/// <returns>True if movement succeeded, false if cancelled or failed</returns>
|
||||
private async Task<bool> 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);
|
||||
return;
|
||||
await DispatchEventAsync(InteractionEventType.InteractionInterrupted, playerRef);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If characterToInteract is None, skip movement
|
||||
if (characterToInteract == CharacterToInteract.None)
|
||||
{
|
||||
return; // Continue to arrival
|
||||
return true; // Continue to arrival
|
||||
}
|
||||
|
||||
// Move player and optionally follower based on characterToInteract setting
|
||||
if (characterToInteract == CharacterToInteract.Trafalgar)
|
||||
// Delegate to controller - let it decide how to handle the interaction
|
||||
bool arrived = await _interactingCharacter.MoveToInteractableAsync(this);
|
||||
|
||||
if (!arrived)
|
||||
{
|
||||
await MovePlayerAsync();
|
||||
}
|
||||
else if (characterToInteract == CharacterToInteract.Pulver || characterToInteract == CharacterToInteract.Both)
|
||||
{
|
||||
await MovePlayerAsync(); // Move player to range first
|
||||
await MoveFollowerAsync(); // Then move follower to interaction point
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the player to the interaction point or custom target.
|
||||
/// </summary>
|
||||
private async Task MovePlayerAsync()
|
||||
{
|
||||
Vector3 stopPoint = transform.position; // Default to interactable position
|
||||
bool customTargetFound = false;
|
||||
|
||||
// Check for a CharacterMoveToTarget component for Trafalgar or Both
|
||||
CharacterMoveToTarget[] moveTargets = GetComponentsInChildren<CharacterMoveToTarget>();
|
||||
foreach (var target in moveTargets)
|
||||
{
|
||||
if (target.characterType == CharacterToInteract.Trafalgar || target.characterType == CharacterToInteract.Both)
|
||||
{
|
||||
stopPoint = target.GetTargetPosition();
|
||||
customTargetFound = true;
|
||||
break;
|
||||
}
|
||||
Logging.Debug($"[Interactable] Movement cancelled for {gameObject.name}");
|
||||
await HandleInteractionCancelledAsync(playerRef);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no custom target, use default distance
|
||||
if (!customTargetFound)
|
||||
{
|
||||
Vector3 interactablePos = transform.position;
|
||||
Vector3 playerPos = playerRef.transform.position;
|
||||
float stopDistance = characterToInteract == CharacterToInteract.Pulver
|
||||
? GameManager.Instance.PlayerStopDistance
|
||||
: GameManager.Instance.PlayerStopDistanceDirectInteraction;
|
||||
Vector3 toPlayer = (playerPos - interactablePos).normalized;
|
||||
stopPoint = interactablePos + toPlayer * stopDistance;
|
||||
}
|
||||
|
||||
// Wait for player to arrive
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
void OnPlayerArrivedLocal()
|
||||
{
|
||||
if (playerRef != null)
|
||||
{
|
||||
playerRef.OnArrivedAtTarget -= OnPlayerArrivedLocal;
|
||||
playerRef.OnMoveToCancelled -= OnPlayerMoveCancelledLocal;
|
||||
}
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (FollowerController == null)
|
||||
return;
|
||||
|
||||
// Check for a CharacterMoveToTarget component for Pulver or Both
|
||||
Vector3 targetPosition = transform.position;
|
||||
CharacterMoveToTarget[] moveTargets = GetComponentsInChildren<CharacterMoveToTarget>();
|
||||
foreach (var target in moveTargets)
|
||||
{
|
||||
if (target.characterType == CharacterToInteract.Pulver || target.characterType == CharacterToInteract.Both)
|
||||
{
|
||||
targetPosition = target.GetTargetPosition();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for follower to arrive
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
void OnFollowerArrivedLocal()
|
||||
{
|
||||
if (FollowerController != null)
|
||||
{
|
||||
FollowerController.OnPickupArrived -= OnFollowerArrivedLocal;
|
||||
}
|
||||
|
||||
// Tell follower to return to player
|
||||
if (FollowerController != null && playerRef != null)
|
||||
{
|
||||
FollowerController.ReturnToPlayer(playerRef.transform);
|
||||
}
|
||||
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
|
||||
FollowerController.OnPickupArrived += OnFollowerArrivedLocal;
|
||||
FollowerController.GoToPoint(targetPosition);
|
||||
|
||||
await tcs.Task;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <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 +335,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 +358,7 @@ namespace Interactions
|
||||
}
|
||||
|
||||
// Reset state
|
||||
playerRef = null;
|
||||
_interactingCharacter = null;
|
||||
FollowerController = null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user