Files
AppleHillsProduction/Assets/Scripts/Input/PlayerTouchController.cs
2025-12-15 11:59:40 +01:00

198 lines
7.5 KiB
C#

using UnityEngine;
using AppleHills.Core.Settings;
using Core;
using Core.Settings;
namespace Input
{
/// <summary>
/// Saveable data for PlayerTouchController state
/// </summary>
[System.Serializable]
public class PlayerSaveData
{
public Vector3 worldPosition;
public Quaternion worldRotation;
}
/// <summary>
/// Handles player movement in response to tap and hold input events.
/// Supports both direct and pathfinding movement modes.
/// Extends BasePlayerMovementController with save/load functionality.
/// Interaction capability (MoveToAndNotify) is provided by base class.
/// </summary>
public class PlayerTouchController : BasePlayerMovementController
{
// Save system configuration
public override bool AutoRegisterForSave => true;
// Scene-specific SaveId - each level has its own player state
public override string SaveId => $"{gameObject.scene.name}/PlayerController";
protected override void LoadSettings()
{
var configs = GameManager.GetSettingsObject<IPlayerMovementConfigs>();
_movementSettings = configs.DefaultPlayerMovement;
}
internal override void OnManagedStart()
{
base.OnManagedStart();
// Register with InputManager as default consumer
if (InputManager.Instance != null)
{
InputManager.Instance.RegisterController("trafalgar", this, setAsDefaultConsumer: true);
Logging.Debug($"[PlayerTouchController] Registered controller '{gameObject.name}' as default consumer");
}
}
#region IInteractingCharacter Override
/// <summary>
/// PlayerTouchController-specific interaction movement.
/// Handles main character movement + follower dispatch based on interactable.characterToInteract setting.
/// </summary>
public override async System.Threading.Tasks.Task<bool> MoveToInteractableAsync(Interactions.InteractableBase interactable)
{
var characterToInteract = interactable.characterToInteract;
// If None, skip movement
if (characterToInteract == Interactions.CharacterToInteract.None)
{
return true;
}
// Determine stop distance based on interaction type
float stopDistance;
if (characterToInteract == Interactions.CharacterToInteract.Trafalgar)
{
// Move ONLY main character directly to item (close distance)
stopDistance = Core.GameManager.Instance.PlayerStopDistanceDirectInteraction;
}
else // Pulver or Both
{
// Move main character to radius (far distance)
stopDistance = Core.GameManager.Instance.PlayerStopDistance;
}
// Calculate stop position for main character
Vector3 stopPoint = interactable.transform.position;
bool customTargetFound = false;
// Check for custom CharacterMoveToTarget for main character
var moveTargets = interactable.GetComponentsInChildren<Interactions.CharacterMoveToTarget>();
foreach (var target in moveTargets)
{
if (target.characterType == Interactions.CharacterToInteract.Trafalgar ||
target.characterType == Interactions.CharacterToInteract.Both)
{
stopPoint = target.GetTargetPosition();
customTargetFound = true;
break;
}
}
// If no custom target, calculate based on distance
if (!customTargetFound)
{
stopPoint = Utils.MovementUtilities.CalculateStopPosition(
interactable.transform.position,
transform.position,
stopDistance
);
}
// Move main character
bool mainCharacterArrived = await Utils.MovementUtilities.MoveToPositionAsync(this, stopPoint);
if (!mainCharacterArrived)
{
return false; // Movement cancelled
}
// Handle follower dispatch based on interaction type
if (characterToInteract == Interactions.CharacterToInteract.Pulver ||
characterToInteract == Interactions.CharacterToInteract.Both)
{
// Find follower and dispatch to interactable
var followerController = FindFirstObjectByType<FollowerController>();
if (followerController != null)
{
// Determine follower target position
Vector3 followerTarget = interactable.transform.position;
// Check for custom target for Pulver
foreach (var target in moveTargets)
{
if (target.characterType == Interactions.CharacterToInteract.Pulver ||
target.characterType == Interactions.CharacterToInteract.Both)
{
followerTarget = target.GetTargetPosition();
break;
}
}
// Wait for follower to arrive
var tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
void OnFollowerArrived()
{
followerController.OnPickupArrived -= OnFollowerArrived;
followerController.ReturnToPlayer(transform);
tcs.TrySetResult(true);
}
followerController.OnPickupArrived += OnFollowerArrived;
followerController.GoToPoint(followerTarget);
await tcs.Task;
}
}
return true; // Success
}
#endregion
#region Save/Load Lifecycle Hooks
internal override string OnSceneSaveRequested()
{
var saveData = new PlayerSaveData
{
worldPosition = transform.position,
worldRotation = transform.rotation
};
return JsonUtility.ToJson(saveData);
}
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{
Logging.Debug("[PlayerTouchController] No saved state to restore");
return;
}
try
{
var saveData = JsonUtility.FromJson<PlayerSaveData>(serializedData);
if (saveData != null)
{
transform.position = saveData.worldPosition;
transform.rotation = saveData.worldRotation;
Logging.Debug($"[PlayerTouchController] Restored position: {saveData.worldPosition}");
}
}
catch (System.Exception ex)
{
Logging.Warning($"[PlayerTouchController] Failed to restore state: {ex.Message}");
}
}
#endregion
}
}