254 lines
8.7 KiB
C#
254 lines
8.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using Core;
|
|
using Input;
|
|
using Interactions;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
namespace Items
|
|
{
|
|
/// <summary>
|
|
/// Saveable data for ControllerSwitchItem state
|
|
/// </summary>
|
|
[Serializable]
|
|
public class ControllerSwitchItemSaveData
|
|
{
|
|
public bool hasBeenUsed;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Base interactable item that switches control from one character controller to another.
|
|
/// Level-agnostic - handles only controller switching logic.
|
|
/// Derive from this class for level-specific behavior (camera switching, teleportation, etc.)
|
|
/// </summary>
|
|
public class ControllerSwitchItem : SaveableInteractable
|
|
{
|
|
[Header("Controller Switch Settings")]
|
|
[Tooltip("Name of the controller to switch to (must match registration name in InputManager)")]
|
|
[SerializeField] protected string targetControllerName;
|
|
|
|
[Header("Visual Feedback")]
|
|
[Tooltip("Visual representation to hide after use (optional)")]
|
|
[SerializeField] protected GameObject visualRepresentation;
|
|
|
|
public UnityEvent OnCharacterSwitch;
|
|
|
|
// State
|
|
protected bool _hasBeenUsed;
|
|
protected bool _isSwitching;
|
|
|
|
public override string SaveId => $"{gameObject.scene.name}/ControllerSwitchItem/{gameObject.name}";
|
|
|
|
internal override void OnManagedAwake()
|
|
{
|
|
base.OnManagedAwake();
|
|
|
|
if (string.IsNullOrEmpty(targetControllerName))
|
|
{
|
|
Debug.LogError($"[ControllerSwitchItem] {gameObject.name} has no target controller name specified!");
|
|
}
|
|
}
|
|
|
|
internal override void OnManagedStart()
|
|
{
|
|
base.OnManagedStart();
|
|
|
|
// Apply state after restoration
|
|
if (_hasBeenUsed && isOneTime)
|
|
{
|
|
DisableVisual();
|
|
}
|
|
}
|
|
|
|
protected override bool CanBeClicked()
|
|
{
|
|
// Cannot be clicked if already used (one-time) or if currently switching
|
|
if (_isSwitching)
|
|
return false;
|
|
|
|
if (isOneTime && _hasBeenUsed)
|
|
return false;
|
|
|
|
// Check if target controller is registered
|
|
if (!InputManager.Instance.IsControllerRegistered(targetControllerName))
|
|
{
|
|
Debug.LogWarning($"[ControllerSwitchItem] Target controller '{targetControllerName}' is not registered with InputManager.");
|
|
return false;
|
|
}
|
|
|
|
return base.CanBeClicked();
|
|
}
|
|
|
|
protected override bool DoInteraction()
|
|
{
|
|
if (_isSwitching)
|
|
return false;
|
|
|
|
Logging.Debug("[ControllerSwitchItem] Starting controller switch sequence");
|
|
|
|
StartCoroutine(SwitchControllerSequence());
|
|
|
|
return true;
|
|
}
|
|
|
|
protected virtual IEnumerator SwitchControllerSequence()
|
|
{
|
|
_isSwitching = true;
|
|
|
|
// Step 1: Get controllers
|
|
var currentController = InputManager.Instance.GetActiveController();
|
|
var targetController = InputManager.Instance.GetController(targetControllerName);
|
|
|
|
if (currentController == null || targetController == null)
|
|
{
|
|
Debug.LogError($"[ControllerSwitchItem] Failed to get controllers! Current: {currentController}, Target: {targetController} (name: {targetControllerName})");
|
|
_isSwitching = false;
|
|
yield break;
|
|
}
|
|
|
|
GameObject currentGameObject = (currentController as MonoBehaviour)?.gameObject;
|
|
GameObject targetGameObject = (targetController as MonoBehaviour)?.gameObject;
|
|
|
|
Logging.Debug($"[ControllerSwitchItem] Switching from {currentGameObject?.name} to {targetGameObject?.name}");
|
|
|
|
// Step 2: Deactivate current controller
|
|
DeactivateCurrentController(currentController, currentGameObject);
|
|
|
|
// Step 3: Activate target controller
|
|
ActivateTargetController(targetController, targetGameObject);
|
|
|
|
// Step 4: Switch InputManager to target controller
|
|
bool switchSuccess = InputManager.Instance.SwitchToController(targetControllerName);
|
|
|
|
if (switchSuccess)
|
|
{
|
|
Logging.Debug($"[ControllerSwitchItem] Successfully switched input to controller: {targetControllerName}");
|
|
OnCharacterSwitch.Invoke();
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"[ControllerSwitchItem] Failed to switch to controller: {targetControllerName}");
|
|
}
|
|
|
|
// Step 5: Mark as used if one-time use
|
|
if (isOneTime)
|
|
{
|
|
_hasBeenUsed = true;
|
|
DisableVisual();
|
|
}
|
|
|
|
_isSwitching = false;
|
|
}
|
|
|
|
protected virtual void DeactivateCurrentController(ITouchInputConsumer currentController, GameObject currentGameObject)
|
|
{
|
|
// If current is a player controller, deactivate it
|
|
if (currentController is BasePlayerMovementController currentPlayerController)
|
|
{
|
|
currentPlayerController.DeactivateController();
|
|
}
|
|
|
|
// If switching FROM follower mode, deactivate follower
|
|
if (currentGameObject != null)
|
|
{
|
|
var currentFollower = currentGameObject.GetComponent<FollowerController>();
|
|
if (currentFollower != null && currentFollower.IsFollowerActive)
|
|
{
|
|
currentFollower.DeactivateFollower();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void ActivateTargetController(ITouchInputConsumer targetController, GameObject targetGameObject)
|
|
{
|
|
// Check if target GameObject has FollowerController component
|
|
FollowerController targetFollower = null;
|
|
if (targetGameObject != null)
|
|
{
|
|
targetFollower = targetGameObject.GetComponent<FollowerController>();
|
|
}
|
|
|
|
// If switching TO a GameObject with FollowerController, we need special handling
|
|
if (targetFollower != null)
|
|
{
|
|
// Switching TO Pulver player control (Pulver has both FollowerController and PulverController)
|
|
// Deactivate follower mode, activate player control
|
|
targetFollower.DeactivateFollower();
|
|
|
|
if (targetController is BasePlayerMovementController targetPlayerController)
|
|
{
|
|
targetPlayerController.ActivateController();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Switching TO Trafalgar (no FollowerController on Trafalgar)
|
|
// If there's a Pulver in the scene, activate its follower mode
|
|
var pulverFollower = FindFirstObjectByType<FollowerController>();
|
|
if (pulverFollower != null)
|
|
{
|
|
pulverFollower.ActivateFollower();
|
|
}
|
|
|
|
// Activate the target player controller
|
|
if (targetController is BasePlayerMovementController targetPlayerController)
|
|
{
|
|
targetPlayerController.ActivateController();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void DisableVisual()
|
|
{
|
|
if (visualRepresentation != null)
|
|
{
|
|
visualRepresentation.SetActive(false);
|
|
}
|
|
}
|
|
|
|
#region Save/Load
|
|
|
|
protected override object GetSerializableState()
|
|
{
|
|
return new ControllerSwitchItemSaveData
|
|
{
|
|
hasBeenUsed = _hasBeenUsed
|
|
};
|
|
}
|
|
|
|
protected override void ApplySerializableState(string serializedData)
|
|
{
|
|
try
|
|
{
|
|
var data = JsonUtility.FromJson<ControllerSwitchItemSaveData>(serializedData);
|
|
_hasBeenUsed = data.hasBeenUsed;
|
|
Logging.Debug($"[ControllerSwitchItem] Restored state: hasBeenUsed={_hasBeenUsed}");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[ControllerSwitchItem] Failed to deserialize save data: {e.Message}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnValidate()
|
|
{
|
|
// Visual feedback in editor
|
|
if (string.IsNullOrEmpty(targetControllerName))
|
|
{
|
|
name = "ControllerSwitchItem (UNCONFIGURED)";
|
|
}
|
|
else
|
|
{
|
|
name = $"ControllerSwitchItem_To_{targetControllerName}";
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|