using UnityEngine; using AppleHills.Core.Settings; using Core; using Core.Settings; namespace Input { /// /// Saveable data for PlayerTouchController state /// [System.Serializable] public class PlayerSaveData { public Vector3 worldPosition; public Quaternion worldRotation; } /// /// 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. /// 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(); _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 /// /// PlayerTouchController-specific interaction movement. /// Handles main character movement + follower dispatch based on interactable.characterToInteract setting. /// public override async System.Threading.Tasks.Task 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(); 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(); 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(); 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(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 } }