Pulver trash maze sequence

This commit is contained in:
Michal Pikulski
2025-12-19 15:26:13 +01:00
parent 15c9ba0127
commit f0905f92d3
36 changed files with 2372 additions and 842 deletions

View File

@@ -0,0 +1,92 @@
using System.Collections;
using Core;
using Interactions;
using Minigames.TrashMaze.Core;
using Minigames.TrashMaze.Data;
using UnityEngine;
namespace Minigames.TrashMaze.Objects
{
/// <summary>
/// Maze exit interactable that teleports Pulver out of the maze.
/// Uses camera blend with midway teleportation to create seamless transition.
/// Does NOT switch controllers - Pulver remains player-controlled.
/// </summary>
public class MazeExit : SaveableInteractable
{
[Header("Maze Exit Settings")]
[Tooltip("Transform where Pulver should be teleported to (maze exit position)")]
[SerializeField] private Transform teleportTarget;
[Tooltip("Camera state to blend to (typically PulverGameplay)")]
[SerializeField] private TrashMazeCameraState targetCameraState = TrashMazeCameraState.PulverGameplay;
public override string SaveId => $"{gameObject.scene.name}/MazeExit/{gameObject.name}";
protected override object GetSerializableState()
{
// MazeExit doesn't need to save state - it's a simple trigger
return null;
}
protected override void ApplySerializableState(string state)
{
// MazeExit doesn't need to restore state
}
protected override bool DoInteraction()
{
Logging.Debug("[MazeExit] Starting maze exit sequence");
StartCoroutine(ExitMazeSequence());
return true;
}
private IEnumerator ExitMazeSequence()
{
// Step 1: Find Pulver (should be the active player-controlled character)
var pulverController = FindFirstObjectByType<PulverController>();
if (pulverController == null)
{
Debug.LogError("[MazeExit] PulverController not found in scene!");
yield break;
}
GameObject pulverGameObject = pulverController.gameObject;
Logging.Debug($"[MazeExit] Found Pulver at {pulverGameObject.transform.position}");
// Step 2: Start camera blend to target state (PulverGameplay)
TeleportationHelper.StartCameraBlend(targetCameraState);
// Step 3: Wait for halfway through blend, teleport Pulver, and set tracking target
yield return TeleportationHelper.TeleportMidBlendAndSetTracking(pulverGameObject, teleportTarget, targetCameraState);
// Step 4: Wait for camera blend to complete
yield return TeleportationHelper.WaitForCameraBlend();
// Step 5: Stop Pulver movement to prevent it from continuing with cached input
if (pulverController != null)
{
pulverController.InterruptMoveTo();
Logging.Debug("[MazeExit] Stopped Pulver movement after teleportation");
}
Logging.Debug("[MazeExit] Maze exit sequence completed");
}
#if UNITY_EDITOR
private void OnValidate()
{
name = "MazeExit";
// Default to PulverGameplay camera
if (targetCameraState == TrashMazeCameraState.Gameplay)
{
targetCameraState = TrashMazeCameraState.PulverGameplay;
}
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4543937f547b49ce8e506aac52442f73
timeCreated: 1766151078

View File

@@ -0,0 +1,158 @@
using System.Collections;
using Core;
using Minigames.TrashMaze.Core;
using Minigames.TrashMaze.Data;
using Unity.Cinemachine;
using UnityEngine;
namespace Minigames.TrashMaze.Objects
{
/// <summary>
/// Static helper class for trash maze teleportation logic.
/// Provides reusable methods for:
/// - Starting camera blend to a target state
/// - Teleporting a character midway through blend
/// - Repositioning camera to teleported character
/// - Setting up camera tracking after blend completes
/// </summary>
public static class TeleportationHelper
{
/// <summary>
/// Get the camera for the target camera state
/// </summary>
private static CinemachineCamera GetTargetCamera(TrashMazeCameraState cameraState)
{
if (TrashMazeCameraController.Instance == null)
return null;
return cameraState switch
{
TrashMazeCameraState.Gameplay => TrashMazeCameraController.Instance.GetGameplayCamera(),
TrashMazeCameraState.Maze => TrashMazeCameraController.Instance.GetMazeCamera(),
TrashMazeCameraState.PulverGameplay => TrashMazeCameraController.Instance.GetPulverGameplayCamera(),
_ => null
};
}
/// <summary>
/// Start the camera blend to the target state
/// </summary>
public static void StartCameraBlend(TrashMazeCameraState targetState)
{
if (TrashMazeCameraController.Instance != null)
{
Logging.Debug($"[TeleportationHelper] Starting camera blend to {targetState}");
switch (targetState)
{
case TrashMazeCameraState.Gameplay:
TrashMazeCameraController.Instance.SwitchToGameplay();
break;
case TrashMazeCameraState.Maze:
TrashMazeCameraController.Instance.SwitchToMaze();
break;
case TrashMazeCameraState.PulverGameplay:
TrashMazeCameraController.Instance.SwitchToPulverGameplay();
break;
}
}
else
{
Debug.LogError($"[TeleportationHelper] TrashMazeCameraController instance not found!");
}
}
/// <summary>
/// Coroutine that waits for halfway through camera blend, then teleports the character
/// and immediately sets it as the tracking target for the camera
/// </summary>
public static IEnumerator TeleportMidBlendAndSetTracking(GameObject character, Transform teleportTarget, TrashMazeCameraState targetCameraState)
{
CinemachineBrain brain = Camera.main?.GetComponent<CinemachineBrain>();
if (brain != null && brain.IsBlending)
{
// Get blend duration from brain
float blendDuration = brain.ActiveBlend != null ? brain.ActiveBlend.Duration : 1f;
float halfBlendTime = blendDuration / 2f;
Logging.Debug($"[TeleportationHelper] Waiting {halfBlendTime:F2}s (half of {blendDuration:F2}s blend) before teleport");
// Wait for halfway through the blend
yield return new WaitForSeconds(halfBlendTime);
}
else
{
// Fallback: wait a short moment if no blend is detected
yield return new WaitForSeconds(0.25f);
}
// Teleport character
if (character != null && teleportTarget != null)
{
character.transform.position = teleportTarget.position;
character.transform.rotation = teleportTarget.rotation;
Logging.Debug($"[TeleportationHelper] Teleported {character.name} to {teleportTarget.position}");
// Immediately set as tracking target - let the blend finish naturally
SetCameraTrackingTarget(character, targetCameraState);
}
else
{
if (character == null)
Debug.LogError($"[TeleportationHelper] Character GameObject is null!");
if (teleportTarget == null)
Debug.LogError($"[TeleportationHelper] Teleport target not assigned!");
}
}
/// <summary>
/// Wait for camera blend to complete
/// </summary>
public static IEnumerator WaitForCameraBlend()
{
CinemachineBrain brain = Camera.main?.GetComponent<CinemachineBrain>();
if (brain != null)
{
// Wait until blend is complete
while (brain.IsBlending)
{
yield return null;
}
Logging.Debug($"[TeleportationHelper] Camera blend completed");
}
else
{
// Fallback: wait a brief moment
yield return new WaitForSeconds(0.5f);
}
}
/// <summary>
/// Set a character as the tracking target for the target camera
/// </summary>
public static void SetCameraTrackingTarget(GameObject character, TrashMazeCameraState targetCameraState)
{
if (character == null)
{
Debug.LogError($"[TeleportationHelper] Cannot set tracking target - character is null");
return;
}
var targetCamera = GetTargetCamera(targetCameraState);
if (targetCamera != null)
{
targetCamera.Follow = character.transform;
targetCamera.LookAt = character.transform;
Logging.Debug($"[TeleportationHelper] Set {character.name} as tracking target for {targetCameraState} camera");
}
else
{
Debug.LogError($"[TeleportationHelper] Target camera for state {targetCameraState} not found!");
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1cf42e4c3a1e3c345aaef352bf84c762

View File

@@ -0,0 +1,109 @@
using System.Collections;
using Core;
using Input;
using Items;
using Minigames.TrashMaze.Core;
using Minigames.TrashMaze.Data;
using Unity.Cinemachine;
using UnityEngine;
namespace Minigames.TrashMaze.Objects
{
/// <summary>
/// Trash Maze specific controller switch that transitions TO Pulver (maze entrance).
/// Handles camera blend with midway teleportation to create the illusion of entering the maze.
/// </summary>
public class TrashMazeSwitchToPulver : ControllerSwitchItem
{
[Header("Trash Maze - To Pulver Settings")]
[Tooltip("Transform where Pulver should be teleported to (maze entrance)")]
[SerializeField] private Transform teleportTarget;
[Tooltip("Camera state to blend to (Maze camera)")]
[SerializeField] private TrashMazeCameraState targetCameraState = TrashMazeCameraState.Maze;
protected override IEnumerator SwitchControllerSequence()
{
_isSwitching = true;
// Step 1: Get controllers
var currentController = InputManager.Instance.GetActiveController();
var targetController = InputManager.Instance.GetController("pulver");
if (currentController == null || targetController == null)
{
Debug.LogError($"[TrashMazeSwitchToPulver] Failed to get controllers!");
_isSwitching = false;
yield break;
}
GameObject currentGameObject = (currentController as MonoBehaviour)?.gameObject;
GameObject targetGameObject = (targetController as MonoBehaviour)?.gameObject;
Logging.Debug($"[TrashMazeSwitchToPulver] Switching from {currentGameObject?.name} to Pulver");
// Step 2: Deactivate current controller (Trafalgar)
DeactivateCurrentController(currentController, currentGameObject);
// Step 3: Deactivate Pulver's follower controller (will be teleported)
if (targetGameObject != null)
{
var pulverFollower = targetGameObject.GetComponent<FollowerController>();
if (pulverFollower != null)
{
pulverFollower.DeactivateFollower();
}
}
// Step 4: Start camera blend to maze
TeleportationHelper.StartCameraBlend(targetCameraState);
// Step 5: Wait for halfway through the blend, teleport Pulver, and set tracking target
yield return TeleportationHelper.TeleportMidBlendAndSetTracking(targetGameObject, teleportTarget, targetCameraState);
// Step 6: Wait for camera blend to complete
yield return TeleportationHelper.WaitForCameraBlend();
// Step 7: Activate Pulver controller
if (targetController is BasePlayerMovementController targetPlayerController)
{
targetPlayerController.ActivateController();
}
// Step 8: Switch InputManager to Pulver controller
bool switchSuccess = InputManager.Instance.SwitchToController("pulver");
if (switchSuccess)
{
Logging.Debug($"[TrashMazeSwitchToPulver] Successfully switched to Pulver controller");
OnCharacterSwitch.Invoke();
}
else
{
Debug.LogError($"[TrashMazeSwitchToPulver] Failed to switch to Pulver controller");
}
// Step 9: Mark as used if one-time use
if (isOneTime)
{
DisableVisual();
}
_isSwitching = false;
}
#if UNITY_EDITOR
private void OnValidate()
{
name = "TrashMazeSwitch_ToPulver";
// Default to Maze camera
if (targetCameraState != TrashMazeCameraState.Maze)
{
targetCameraState = TrashMazeCameraState.Maze;
}
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fe2d6200d0d54638adb61befd932228f
timeCreated: 1766149622

View File

@@ -0,0 +1,180 @@
using System.Collections;
using Core;
using Input;
using Items;
using Minigames.TrashMaze.Core;
using Minigames.TrashMaze.Data;
using Unity.Cinemachine;
using UnityEngine;
namespace Minigames.TrashMaze.Objects
{
/// <summary>
/// Trash Maze specific controller switch that transitions FROM Pulver back TO Trafalgar (maze exit).
/// Handles camera blend back to gameplay view and re-enables follower mode.
/// </summary>
public class TrashMazeSwitchToTrafalgar : ControllerSwitchItem
{
[Header("Trash Maze - To Trafalgar Settings")]
[Tooltip("Camera state to blend to (Gameplay camera)")]
[SerializeField] private TrashMazeCameraState targetCameraState = TrashMazeCameraState.Gameplay;
protected override IEnumerator SwitchControllerSequence()
{
_isSwitching = true;
// Step 1: Get controllers
var currentController = InputManager.Instance.GetActiveController();
var targetController = InputManager.Instance.GetController("trafalgar");
if (currentController == null || targetController == null)
{
Debug.LogError($"[TrashMazeSwitchToTrafalgar] Failed to get controllers!");
_isSwitching = false;
yield break;
}
GameObject currentGameObject = (currentController as MonoBehaviour)?.gameObject;
GameObject targetGameObject = (targetController as MonoBehaviour)?.gameObject;
Logging.Debug($"[TrashMazeSwitchToTrafalgar] Switching from Pulver to {targetGameObject?.name}");
// Step 2: Deactivate current controller (Pulver)
DeactivateCurrentController(currentController, currentGameObject);
// Explicitly deactivate PulverController to ensure it stops receiving input
if (currentGameObject != null)
{
var pulverController = currentGameObject.GetComponent<PulverController>();
if (pulverController != null)
{
pulverController.DeactivateController();
Logging.Debug("[TrashMazeSwitchToTrafalgar] Explicitly deactivated PulverController");
}
}
// Step 3: Start camera blend back to gameplay
StartCameraBlend();
// Step 4: Wait for camera blend to complete
yield return WaitForCameraBlend();
// Step 5: Unset Pulver as tracking target and set Trafalgar for gameplay camera
SetTrafalgarAsTrackingTarget(targetGameObject);
// Step 6: Activate Pulver's follower mode (so it follows Trafalgar)
if (currentGameObject != null)
{
var pulverFollower = currentGameObject.GetComponent<FollowerController>();
if (pulverFollower != null)
{
pulverFollower.ActivateFollower();
}
}
// Step 7: Activate Trafalgar controller
if (targetController is BasePlayerMovementController targetPlayerController)
{
targetPlayerController.ActivateController();
}
// Step 8: Switch InputManager to Trafalgar controller
bool switchSuccess = InputManager.Instance.SwitchToController("trafalgar");
if (switchSuccess)
{
Logging.Debug($"[TrashMazeSwitchToTrafalgar] Successfully switched to Trafalgar controller");
OnCharacterSwitch.Invoke();
}
else
{
Debug.LogError($"[TrashMazeSwitchToTrafalgar] Failed to switch to Trafalgar controller");
}
// Step 8: Mark as used if one-time use
if (isOneTime)
{
DisableVisual();
}
_isSwitching = false;
}
private void StartCameraBlend()
{
if (TrashMazeCameraController.Instance != null)
{
Logging.Debug($"[TrashMazeSwitchToTrafalgar] Starting camera blend to {targetCameraState}");
if (targetCameraState == TrashMazeCameraState.Gameplay)
{
TrashMazeCameraController.Instance.SwitchToGameplay();
}
else
{
Logging.Warning($"[TrashMazeSwitchToTrafalgar] Unexpected camera state: {targetCameraState}");
}
}
else
{
Debug.LogError("[TrashMazeSwitchToTrafalgar] TrashMazeCameraController instance not found!");
}
}
private IEnumerator WaitForCameraBlend()
{
CinemachineBrain brain = Camera.main?.GetComponent<CinemachineBrain>();
if (brain != null)
{
// Wait until blend is complete
while (brain.IsBlending)
{
yield return null;
}
Logging.Debug("[TrashMazeSwitchToTrafalgar] Camera blend completed");
}
else
{
// Fallback: wait a brief moment
yield return new WaitForSeconds(0.5f);
}
}
private void SetTrafalgarAsTrackingTarget(GameObject trafalgarGameObject)
{
if (TrashMazeCameraController.Instance != null)
{
// Clear maze camera tracking target
var mazeCamera = TrashMazeCameraController.Instance.GetMazeCamera();
if (mazeCamera != null)
{
mazeCamera.Follow = null;
mazeCamera.LookAt = null;
Logging.Debug($"[TrashMazeSwitchToTrafalgar] Cleared Pulver as tracking target from maze camera");
}
// Set Trafalgar as tracking target for gameplay camera
if (trafalgarGameObject != null)
{
var gameplayCamera = TrashMazeCameraController.Instance.GetGameplayCamera();
if (gameplayCamera != null)
{
gameplayCamera.Follow = trafalgarGameObject.transform;
gameplayCamera.LookAt = trafalgarGameObject.transform;
Logging.Debug($"[TrashMazeSwitchToTrafalgar] Set Trafalgar as tracking target for gameplay camera");
}
}
}
}
#if UNITY_EDITOR
private void OnValidate()
{
name = "TrashMazeSwitch_ToTrafalgar";
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60a323cce5144fe9bae5dd3b313315a1
timeCreated: 1766149639