Files
PacManUdon/Assets/Scripts/GameController.cs
2025-12-13 20:30:54 +01:00

1486 lines
52 KiB
C#

#define RECORDING_DEMO
namespace Marro.PacManUdon
{
using System;
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.SDK3.Components;
using VRC.Udon.Common.Interfaces;
using VRC.SDK3.Data;
public class GameController : UdonSharpBehaviour
{
[Header("Static game components")]
[SerializeField] private Maze[] mazes;
[SerializeField] private PacMan pacMan;
[SerializeField] private GhostManager ghostManager;
[SerializeField] private BonusFruit bonusFruit;
[SerializeField] private PelletManager pelletManager;
[SerializeField] public StatusDisplay statusDisplay; // This one is public so other scripts can write to the debug display
[SerializeField] private PelletManager attractScreen;
[SerializeField] private GameObject pressStartButtonScreen;
[SerializeField] private PlayerInput playerInput;
[SerializeField] private Animator demo;
[SerializeField] private SoundManager soundManager;
[SerializeField] private GameObject recorder;
[Header("Game settings")]
[SerializeField] private int startingExtraLives = 3;
[SerializeField] private int scoreToExtraLife = 10000;
[Tooltip("Override amount of pellets needed to clear stage, set to -1 to disable.")]
[SerializeField] private int pelletCountOverride = -1;
private Maze maze;
private VRCObjectPool pelletPool;
private Animator mazeSpriteAnimator;
private int pelletCountTotal;
private int pelletCountRemaining;
private GameObject[] attractScreenElements;
[UdonSynced, FieldChangeCallback(nameof(GameState))] private PacManGameState gameState;
[UdonSynced, FieldChangeCallback(nameof(Score))] private int score;
[UdonSynced, FieldChangeCallback(nameof(Level))] private int level;
[UdonSynced, FieldChangeCallback(nameof(HighScore))] private int highScore;
[UdonSynced, FieldChangeCallback(nameof(ExtraLives))] private int extraLives;
public void Start()
{
maze = mazes[0];
pelletPool = maze.pelletContainer.GetComponent<VRCObjectPool>();
mazeSpriteAnimator = maze.mazeSprite.GetComponent<Animator>();
attractScreenElements = new GameObject[attractScreen.transform.childCount];
for (int i = 0; i < attractScreenElements.Length; i++)
{
attractScreenElements[i] = attractScreen.transform.GetChild(i).gameObject;
}
ghostManager.Initialize(maze.ghostTargets, pacMan, this);
pacMan.Initialize(playerInput, pelletPool, this);
bonusFruit.Initialize();
pelletManager.Initialize(pelletPool);
statusDisplay.Initialize();
playerInput.Initialize(this);
soundManager.Initialize();
SetScore(0);
SetHighScore(0);
SetLevel(0);
StartAttractMode();
}
public void FixedUpdate()
{
TimeSequenceUpdate(Time.deltaTime);
}
public void JoystickGrabbed()
{
if (gameState == PacManGameState.AttractMode || gameState == PacManGameState.AttractModeDemo)
StartTimeSequence(PacManTimeSequence.WaitForStart);
}
public void JoystickReleased()
{
if (gameState == PacManGameState.WaitForStart)
StartTimeSequence(PacManTimeSequence.WaitForStartTimeout);
}
public void StartGameButtonPressed()
{
Debug.Log($"{gameObject} Start Game Button was pressed!");
TakeOwnership();
StartTimeSequence(PacManTimeSequence.StartNewGame);
}
public void SkipLevelButtonPressed()
{
if (Networking.IsOwner(gameObject))
{
Debug.Log($"{gameObject} Skip level button pressed!");
StartTimeSequence(PacManTimeSequence.BoardClear);
TimeSequenceSkipToNextStep();
}
}
public void StartDemoButtonPressed()
{
if (Networking.IsOwner(gameObject))
{
Debug.Log($"{gameObject} Start demo button pressed!");
StartTimeSequence(PacManTimeSequence.AttractScreenDemo);
}
}
private void StartAttractMode()
{
#if RECORDING_DEMO
// recorder.gameObject.SetActive(true);
StartTimeSequence(PacManTimeSequence.AttractScreenIntroduction);
#else
SetGameState(PacManGameState.AttractMode);
HideEverything();
demo.gameObject.SetActive(true);
#endif
}
private void InitializeNewGame()
{
Debug.Log($"{gameObject} Started new game!");
SetScore(0);
SetExtraLives(startingExtraLives);
SetLevel(1);
}
private void InitializeLevel()
{
Debug.Log($"{gameObject} New level started!");
if (Networking.IsOwner(gameObject))
{
pelletCountTotal = pelletPool.Pool.Length;
pelletCountRemaining = pelletCountTotal;
ghostManager.SetPelletsRemaining(pelletCountRemaining);
ghostManager.NewLevel();
pelletManager.RestoreAllPellets();
if (pelletCountOverride > 0)
{
pelletCountRemaining = pelletCountOverride;
}
}
mazeSpriteAnimator.SetBool("Blinking", false);
}
private void RestartLevel()
{
Debug.Log($"{gameObject} (Re)started level!");
// SetInGameComponentVisibility(true);
ghostManager.Reset();
pacMan.Reset();
bonusFruit.Despawn();
soundManager.Reset();
pelletManager.SetPowerPelletsBlink(false);
}
public void GotPellet(bool addScore = true)
{
pelletCountRemaining--;
if (addScore) AddScore(10);
ghostManager.PelletConsumed();
soundManager.PlayPelletSound();
soundManager.UpdatePelletCount(pelletCountRemaining);
int pelletsConsumed = pelletCountTotal - pelletCountRemaining;
if (pelletCountRemaining <= 0)
{
StartTimeSequence(PacManTimeSequence.BoardClear);
}
else if (pelletsConsumed == 70 || pelletsConsumed == 170)
{
bonusFruit.Spawn();
}
}
public void GotPowerPellet()
{
if (gameState == PacManGameState.AttractMode)
{
TimeSequenceSkipToNextStep();
return;
}
GotPellet(addScore: false);
AddScore(50);
ghostManager.SetPowerPellet(true);
pacMan.SetPowerPellet(true);
soundManager.SetGhostBlue(true);
}
public void EndPowerPellet()
{
ghostManager.SetPowerPellet(false);
pacMan.SetPowerPellet(false);
soundManager.SetGhostBlue(false);
}
public void GotFruit()
{
AddScore(bonusFruit.Collected());
soundManager.PlayFruitSound();
}
public void GhostCaught(int scoreBonus)
{
if (gameState == PacManGameState.AttractMode)
{
TimeSequenceSkipToNextStep();
return;
}
AddScore(scoreBonus);
StartTimeSequence(PacManTimeSequence.GhostCaught);
pacMan.HideUntilUnfrozen();
}
public void PacManCaught()
{
StartTimeSequence(PacManTimeSequence.PacManCaught);
}
public void NoGhostsScared()
{
soundManager.SetGhostBlue(false);
}
public void NoGhostsRetreating()
{
soundManager.SetGhostRetreat(false);
}
void BoardClearAnimation()
{
ghostManager.gameObject.SetActive(false);
mazeSpriteAnimator.SetBool("Blinking", true);
}
private void HideEverything()
{
SetMazeActive(false);
SetPelletsActive(false);
SetMazeVisible(false);
SetGhostsActive(false);
SetPacManActive(false);
SetPressStartButtonScreenVisible(false);
statusDisplay.SetGameOverTextVisible(false);
statusDisplay.SetExtraLivesDisplayVisible(false);
statusDisplay.SetLevelDisplayVisible(false);
statusDisplay.SetPlayer1TextVisible(false);
statusDisplay.SetReadyTextVisible(false);
demo.gameObject.SetActive(false);
}
void SetMazeActive(bool active)
{
maze.gameObject.SetActive(active);
}
void SetPelletsActive(bool active)
{
pelletPool.gameObject.SetActive(active);
}
void SetMazeVisible(bool visible)
{
mazeSpriteAnimator.SetBool("Hidden", !visible);
}
void SetGhostsActive(bool active)
{
ghostManager.SetActive(active);
}
void SetPacManActive(bool active)
{
pacMan.SetActive(active);
}
void SetPressStartButtonScreenVisible(bool visible)
{
pressStartButtonScreen.SetActive(visible);
}
void SetGameState(PacManGameState newGameState)
{
// Debug.Log($"{gameObject} State transitioning from {gameState} to {newGameState}");
gameState = newGameState;
if (Networking.IsOwner(gameObject))
{
RequestSerialization();
}
}
private void IncrementLevel()
{
SetLevel(level + 1);
}
private void SetLevel(int level)
{
this.level = level;
pacMan.SetLevel(level);
ghostManager.SetLevel(level);
statusDisplay.SetLevel(level);
bonusFruit.SetFruitType(PacManConstants.GetFruitTypeForLevel(level));
}
void AddScore(int score)
{
if (gameState == PacManGameState.AttractMode || gameState == PacManGameState.AttractModeDemo)
{
return;
}
if (this.score < scoreToExtraLife && this.score + score >= scoreToExtraLife)
{
BonusLifeReached();
}
SetScore(this.score + score);
RequestSerialization();
}
void SetScore(int score)
{
this.score = score;
statusDisplay.Set1UPScore(score);
if (score > highScore)
{
highScore = score;
statusDisplay.SetHighScore(score);
}
}
void SetHighScore(int highScore)
{
this.highScore = highScore;
statusDisplay.SetHighScore(score);
}
public void DecrementLives()
{
if (!Networking.IsOwner(gameObject))
{
return;
}
// Debug.Log($"{gameObject} Decremented lives from {extraLives} to {extraLives - 1}");
SetExtraLives(extraLives - 1);
}
void IncrementLives()
{
if (!Networking.IsOwner(gameObject))
{
return;
}
// Debug.Log($"{gameObject} Incremented lives from {extraLives} to {extraLives + 1}");
SetExtraLives(extraLives + 1);
}
void SetExtraLives(int extraLives)
{
// Debug.Log($"{gameObject} Set lives from {this.extraLives} to {extraLives}");
this.extraLives = extraLives;
statusDisplay.SetExtraLives(extraLives);
}
void BonusLifeReached()
{
IncrementLives();
soundManager.PlayExtraLifeSound();
}
public void SetFrozen(bool frozen, bool ghostIgnoreIfCaught = false, bool ghostKeepAnimating = false)
{
// Debug.Log($"{gameObject} Set Frozen: {frozen}");
pacMan.SetFrozen(frozen);
bonusFruit.SetFrozen(frozen);
ghostManager.SetFrozen(frozen, ignoreIfCaught: ghostIgnoreIfCaught);
if (!frozen)
{
pelletManager.SetPowerPelletsBlink(true);
}
}
void TakeOwnership()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
Networking.SetOwner(Networking.LocalPlayer, pacMan.gameObject);
Networking.SetOwner(Networking.LocalPlayer, pelletPool.gameObject);
ghostManager.SetOwner(Networking.LocalPlayer);
}
public int ExtraLives
{
set
{
SetExtraLives(value);
}
get => extraLives;
}
public PacManGameState GameState
{
set
{
SetGameState(value);
}
get => gameState;
}
public bool GhostsScared
{
set
{
}
get => GhostsScared;
}
public int Score
{
set
{
SetScore(value);
}
get => score;
}
public int HighScore
{
set
{
SetHighScore(value);
}
get => score;
}
public int Level
{
set
{
SetLevel(value);
}
get => level;
}
#region TIME SEQUENCE BEHAVIOUR
// This was supposed to be a separate class, right until the moment I realized Udon doesn't support instantiating classes...
bool currentlyInTimeSequence;
PacManTimeSequence currentTimeSequence;
bool hasTimeSequenceQueued;
private DataList timeSequenceQueue;
[UdonSynced] float timeSequenceSecondsPassed;
int timeSequenceProgress;
float[] timeSequenceKeyframeTimes;
private void StartTimeSequence(PacManTimeSequence timeSequence)
{
if (timeSequenceQueue == null)
{
timeSequenceQueue = new DataList();
}
if (currentlyInTimeSequence == true)
{
int timeSequenceInt = (int)timeSequence; // Doing the conversion in the line below crashes the script. I love working in Udon
timeSequenceQueue.Add(timeSequenceInt);
hasTimeSequenceQueued = true;
return;
}
Debug.Log($"StartTimeSequence: {timeSequence}");
currentlyInTimeSequence = true;
currentTimeSequence = timeSequence;
timeSequenceProgress = 0;
timeSequenceSecondsPassed = 0;
timeSequenceKeyframeTimes = GetTimeSequenceKeyframeTimes(timeSequence);
TimeSequenceProgressToTime(timeSequenceSecondsPassed);
}
private void InsertTimeSequence(PacManTimeSequence timeSequence)
{
StartTimeSequence(timeSequence);
}
private void TimeSequenceUpdate(float deltaSeconds)
{
if (!currentlyInTimeSequence && hasTimeSequenceQueued)
{
timeSequenceQueue.TryGetValue(0, out DataToken nextTimeSequence);
StartTimeSequence((PacManTimeSequence)nextTimeSequence.Int);
timeSequenceQueue.RemoveAt(0);
hasTimeSequenceQueued = timeSequenceQueue.Count > 0 ? true : false;
return;
}
if (currentlyInTimeSequence)
{
if (hasTimeSequenceQueued)
{
while (currentlyInTimeSequence)
{
TimeSequenceSkipToNextStep();
}
}
else
{
TimeSequenceProgressToTime(timeSequenceSecondsPassed + deltaSeconds);
}
}
}
private void TimeSequenceSkipToNextStep()
{
// Debug.Log($"{gameObject} TimeSequenceSkipToNextStep");
if (timeSequenceProgress < timeSequenceKeyframeTimes.Length)
{
TimeSequenceProgressToTime(timeSequenceKeyframeTimes[timeSequenceProgress]);
}
else
{
Debug.LogWarning($"{gameObject} Tried skipping to next time sequence step when already on last step!");
currentlyInTimeSequence = false;
}
}
private void TimeSequenceProgressToTime(float seconds)
{
timeSequenceSecondsPassed = seconds;
while (timeSequenceSecondsPassed >= timeSequenceKeyframeTimes[timeSequenceProgress])
{
TimeSequenceExecuteStep(timeSequenceProgress);
timeSequenceProgress += 1;
if (timeSequenceProgress >= timeSequenceKeyframeTimes.Length)
{
currentlyInTimeSequence = false;
break;
}
}
}
private void TimeSequenceExecuteStep(int sequenceProgress)
{
// Debug.Log($"{gameObject} Triggered time sequence step for sequence {currentTimeSequence} with progress {sequenceProgress}");
switch (currentTimeSequence)
{
default:
Debug.LogError($"{gameObject} No time sequence keyframes known for sequence {currentTimeSequence}");
break;
case PacManTimeSequence.AttractScreenIntroduction:
TimeSequenceStepAttractScreenIntroduction(sequenceProgress);
break;
case PacManTimeSequence.AttractScreenDemo:
TimeSequenceStepAttractScreenDemo(sequenceProgress);
break;
case PacManTimeSequence.WaitForStart:
TimeSequenceStepWaitForStart(sequenceProgress);
break;
case PacManTimeSequence.WaitForStartTimeout:
TimeSequenceStepWaitForStartTimeout(sequenceProgress);
break;
case PacManTimeSequence.StartNewGame:
TimeSequenceStepStartNewGame(sequenceProgress);
break;
case PacManTimeSequence.BoardClear:
TimeSequenceStepBoardClear(sequenceProgress);
break;
case PacManTimeSequence.StartNewLevel:
TimeSequenceStepStartNewLevel(sequenceProgress);
break;
case PacManTimeSequence.GhostCaught:
TimeSequenceStepGhostCaught(sequenceProgress);
break;
case PacManTimeSequence.PacManCaught:
TimeSequenceStepPacManCaught(sequenceProgress);
break;
case PacManTimeSequence.RestartLevel:
TimeSequenceStepRestartLevel(sequenceProgress);
break;
case PacManTimeSequence.GameOver:
TimeSequenceStepGameOver(sequenceProgress);
break;
}
}
private float[] GetTimeSequenceKeyframeTimes(PacManTimeSequence timeSequence)
{
switch (timeSequence)
{
default:
Debug.LogError($"{gameObject} No time sequence keyframe times known for sequence {timeSequence}");
return new float[0];
case PacManTimeSequence.AttractScreenIntroduction:
return DeltaToAbsolute(new float[] { 0, 0.032f, 1f, 1f, .5f, .5f, 1f, .5f, .5f, 1f, .5f, .5f, 1f, .5f, 1f, 1f, 1f,
5f, 0.2f, 2f, 0.91667f, 2f, 0.91667f, 2f, 0.91667f, 2f, 0.91667f });
case PacManTimeSequence.AttractScreenDemo:
return DeltaToAbsolute(new float[] { 0, 0.016f, 0.05f, 0.16f, 0.33f, 1.85f, 54f });
case PacManTimeSequence.WaitForStart:
return DeltaToAbsolute(new float[] { 0, 0.016f });
case PacManTimeSequence.WaitForStartTimeout:
return DeltaToAbsolute(new float[] { 0, 5f });
case PacManTimeSequence.StartNewGame:
return DeltaToAbsolute(new float[] { 0, 0.016f, 2.2f, 0.032f, 0.032f, 1.92f, 0.032f });
case PacManTimeSequence.BoardClear:
return DeltaToAbsolute(new float[] { 0, 2f, 0.016f, 1.6f - 0.016f, 0.016f, 0.032f, 0.3f });
case PacManTimeSequence.StartNewLevel:
return DeltaToAbsolute(new float[] { 0, 0.064f, 0.032f, 1.85f, 0.016f });
case PacManTimeSequence.GhostCaught:
return DeltaToAbsolute(new float[] { 0, 0.91667f });
case PacManTimeSequence.PacManCaught:
return DeltaToAbsolute(new float[] { 0, 1, 0.35f, 2.40f, 2f });
case PacManTimeSequence.RestartLevel:
return DeltaToAbsolute(new float[] { 0, 0.016f, 0.064f, 0.032f, 1.85f, 0.016f });
case PacManTimeSequence.GameOver:
return DeltaToAbsolute(new float[] { 0, 1.95f });
}
}
private static float[] DeltaToAbsolute(float[] delta)
{
if (delta.Length < 1)
{
return new float[0];
}
float[] absolute = new float[delta.Length];
absolute[0] = delta[0];
for (int i = 1; i < delta.Length; i++)
{
absolute[i] = delta[i] + absolute[i - 1];
}
return absolute;
}
private void TimeSequenceStepAttractScreenIntroduction(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
SetGameState(PacManGameState.AttractMode);
// Initialize
soundManager.SuppressSound(true);
RestartLevel();
HideEverything();
SetFrozen(true);
attractScreen.gameObject.SetActive(true);
attractScreen.Initialize();
for (int i = 0; i <= 15; i++)
{
// Debug.Log($"{gameObject} TimeSequenceAttractScreen deactivating with iteration i");
attractScreenElements[i].SetActive(false);
}
attractScreen.SetPowerPelletsBlink(false);
break;
case 1:
// show "Character / Nickname"
attractScreenElements[0].SetActive(true);
break;
case 2:
// Show blinky sprite
attractScreenElements[1].SetActive(true);
break;
case 3:
// Show blinky character
attractScreenElements[2].SetActive(true);
break;
case 4:
// Show blinky nickname
attractScreenElements[3].SetActive(true);
break;
case 5:
// Show pinky sprite
attractScreenElements[4].SetActive(true);
break;
case 6:
// Show pinky character
attractScreenElements[5].SetActive(true);
break;
case 7:
// Show pinky nickname
attractScreenElements[6].SetActive(true);
break;
case 8:
// Show inky sprite
attractScreenElements[7].SetActive(true);
break;
case 9:
// Show inky character
attractScreenElements[8].SetActive(true);
break;
case 10:
// Show inky nickname
attractScreenElements[9].SetActive(true);
break;
case 11:
// Show clyde sprite
attractScreenElements[10].SetActive(true);
break;
case 12:
// Show clyde character
attractScreenElements[11].SetActive(true);
break;
case 13:
// Show clyde nickname
attractScreenElements[12].SetActive(true);
break;
case 14:
// Show pellet point values
attractScreenElements[13].SetActive(true);
break;
case 15:
// Show copyright message, setup pellet demonstration
attractScreenElements[14].SetActive(true);
attractScreenElements[15].SetActive(true);
pacMan.SetLevel(1);
pacMan.Reset();
pacMan.SetKinematic(true);
pacMan.SetActive(true);
pacMan.SetPosition(attractScreenElements[16].transform.localPosition);
pacMan.SetDirection(Vector2.left);
ghostManager.SetLevel(2);
ghostManager.Reset();
ghostManager.SetKinematic(true);
ghostManager.SetActive(true);
Ghost[] ghosts = ghostManager.Ghosts;
for (int i = 0; i < ghosts.Length; i++)
{
ghosts[i].SetPosition(attractScreenElements[17 + i].transform.localPosition);
ghosts[i].SetDirection(Vector2.left);
ghosts[i].SetState(PacManGhostState.Normal);
}
break;
case 16:
attractScreen.SetPowerPelletsBlink(true);
SetFrozen(false);
break;
case 17:
ghostManager.SetPowerPellet(true);
pacMan.SetPowerPellet(true);
attractScreenElements[15].SetActive(false);
break;
case 18:
// Turn PacMan around after eating power pellet
pacMan.SetDirection(Vector2.right);
pacMan.SetTargetDirection(Vector2.right);
break;
case 19:
ghostManager.Ghosts[0].Caught(200);
pacMan.SetActive(false);
SetFrozen(true);
break;
case 20:
ghostManager.Ghosts[0].ReturnHome();
ghostManager.Ghosts[0].SetActive(false);
pacMan.SetActive(true);
SetFrozen(false);
break;
case 21:
ghostManager.Ghosts[1].Caught(400);
pacMan.SetActive(false);
SetFrozen(true);
break;
case 22:
ghostManager.Ghosts[1].ReturnHome();
ghostManager.Ghosts[1].SetActive(false);
pacMan.SetActive(true);
SetFrozen(false);
break;
case 23:
ghostManager.Ghosts[2].Caught(800);
pacMan.SetActive(false);
SetFrozen(true);
break;
case 24:
ghostManager.Ghosts[2].ReturnHome();
ghostManager.Ghosts[2].SetActive(false);
pacMan.SetActive(true);
SetFrozen(false);
break;
case 25:
ghostManager.Ghosts[3].Caught(1600);
pacMan.SetActive(false);
SetFrozen(true);
break;
case 26:
ghostManager.Ghosts[3].ReturnHome();
ghostManager.Ghosts[3].SetActive(false);
// Hide elements, start demo
attractScreen.gameObject.SetActive(false);
if (!hasTimeSequenceQueued)
{
StartTimeSequence(PacManTimeSequence.AttractScreenDemo);
}
break;
}
}
private void TimeSequenceStepAttractScreenDemo(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
SetGameState(PacManGameState.AttractModeDemo);
HideEverything();
SetFrozen(true);
break;
case 1:
InitializeLevel();
SetMazeActive(true);
SetMazeVisible(true);
SetLevel(1);
break;
case 2:
// Reset ghosts
RestartLevel();
ghostManager.Ghosts[0].SetPredefinedPath(new Vector2[]{ // Blinky
Vector2.down,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.zero,
Vector2.up,
Vector2.right,
Vector2.down,
Vector2.zero,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.zero,
Vector2.left,
Vector2.down,
Vector2.zero,
Vector2.left,
Vector2.down,
Vector2.right,
// Goes through tunnel
Vector2.zero,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.zero,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.up,
Vector2.left,
// Gets eaten
Vector2.zero,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
});
ghostManager.Ghosts[1].SetPredefinedPath(new Vector2[]{ // Pinky
Vector2.down,
Vector2.left,
Vector2.up,
Vector2.zero,
Vector2.zero,
Vector2.left,
Vector2.down,
Vector2.right,
// Pellet starts
Vector2.up,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.up,
Vector2.right,
// Pellet ends
Vector2.zero,
Vector2.down,
// Pellet starts
Vector2.left,
Vector2.down,
Vector2.zero,
Vector2.left,
// Caught
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.right,
// Home
Vector2.down,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.down,
Vector2.zero,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.zero,
Vector2.down,
Vector2.left,
// Power pellet active
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.up
});
ghostManager.Ghosts[2].SetPredefinedPath(new Vector2[]{ // Inky
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.zero,
Vector2.zero,
Vector2.down,
Vector2.right,
// Pellet starts
Vector2.up,
Vector2.right,
Vector2.up,
// Caught
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.zero,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.zero,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.up,
Vector2.left
});
ghostManager.Ghosts[3].SetPredefinedPath(new Vector2[]{ // Clyde
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.zero,
Vector2.left,
Vector2.zero,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.up,
Vector2.right
});
pacMan.SetPredefinedPath(new Vector2[]{
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.right,
Vector2.zero,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.zero,
Vector2.up,
Vector2.zero,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.up,
Vector2.zero,
Vector2.zero,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.zero,
Vector2.up,
Vector2.left,
Vector2.down,
Vector2.zero,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.down,
Vector2.zero,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.zero,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.down,
Vector2.right,
Vector2.up,
Vector2.right,
Vector2.zero,
Vector2.down,
Vector2.left,
Vector2.down,
Vector2.right,
Vector2.down,
Vector2.left,
Vector2.zero,
Vector2.zero,
Vector2.up,
Vector2.right,
Vector2.up,
Vector2.left,
Vector2.up,
Vector2.right,
});
break;
case 3:
SetPelletsActive(true);
pelletManager.RestoreAllPellets();
statusDisplay.SetGameOverTextVisible(true);
break;
case 4:
// Show pacman, show ghosts
SetPacManActive(true);
SetGhostsActive(true);
break;
case 5:
// Unfreeze
SetFrozen(false);
break;
// case 6:
// if (!hasTimeSequenceQueued)
// {
// StartTimeSequence(PacManTimeSequence.AttractScreenWaitForStart);
// }
// break;
}
}
private void TimeSequenceStepWaitForStart(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
SetGameState(PacManGameState.WaitForStart);
HideEverything();
soundManager.SuppressSound(false);
soundManager.PlayCoinSound();
break;
case 1:
SetPressStartButtonScreenVisible(true);
if (playerInput.active == false && hasTimeSequenceQueued == false)
{
StartTimeSequence(PacManTimeSequence.WaitForStartTimeout);
}
break;
}
}
private void TimeSequenceStepWaitForStartTimeout(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
break;
case 1:
if (playerInput.active == false && hasTimeSequenceQueued == false)
{
StartAttractMode();
}
break;
}
}
private void TimeSequenceStepStartNewGame(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Prepare new game, hide everything except score bar
gameState = PacManGameState.InGame;
HideEverything();
SetMazeActive(true);
InitializeNewGame();
InitializeLevel();
RestartLevel();
SetFrozen(true);
soundManager.SuppressSound(false);
soundManager.PlayGameStartSound();
break;
case 1:
// Show maze, lives indicator, level indicator, player 1 and ready text
// SOMEWHERE IN HERE UNITY (EDITOR) APPEARS TO HAVE A SMALL RANDOM CHANCE OF CRASHING !!
Debug.Log("Log dump in case of crash");
Debug.Log("Setting pellets visible");
SetPelletsActive(true);
Debug.Log("Setting maze visible");
SetMazeVisible(true);
Debug.Log("Setting extra lives display visible");
statusDisplay.SetExtraLivesDisplayVisible(true);
Debug.Log("Setting level display visible");
statusDisplay.SetLevelDisplayVisible(true);
Debug.Log("Setting player 1 text visible");
statusDisplay.SetPlayer1TextVisible(true);
Debug.Log("Setting ready text visible");
statusDisplay.SetReadyTextVisible(true);
Debug.Log("Starting 1UP blink");
statusDisplay.SetLabel1UPTextBlinking(true);
break;
case 2:
// Subtract a life
DecrementLives();
break;
case 3:
// Remove Player 1 text
statusDisplay.SetPlayer1TextVisible(false);
break;
case 4:
// Show ghosts and pacman
SetGhostsActive(true);
SetPacManActive(true);
break;
case 5:
// Remove ready text
statusDisplay.SetReadyTextVisible(false);
break;
case 6:
// Start game, end sequence
soundManager.StartGhostSound();
SetFrozen(false);
break;
}
}
private void TimeSequenceStepBoardClear(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Freeze
SetFrozen(true);
soundManager.StopAllSound();
break;
case 1:
// Start board blinking, hide pellets in case of rack test
BoardClearAnimation();
SetPelletsActive(false);
break;
case 2:
// Hide ghosts
SetGhostsActive(false);
break;
case 3:
// Hide maze, lives indicator, level indicator
SetMazeVisible(false);
statusDisplay.SetExtraLivesDisplayVisible(false);
statusDisplay.SetLevelDisplayVisible(false);
break;
case 4:
// Hide score bar
statusDisplay.SetScoreDisplayVisible(false);
break;
case 5:
// Hide pacman, show level indicator with old level
SetPacManActive(false);
statusDisplay.SetLevelDisplayVisible(true);
break;
case 6:
// Call handler for what should happen next
InsertTimeSequence(PacManTimeSequence.StartNewLevel);
break;
}
}
private void TimeSequenceStepStartNewLevel(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Reset, show maze and score display
InitializeLevel();
RestartLevel();
SetMazeVisible(true);
statusDisplay.SetScoreDisplayVisible(true);
soundManager.SuppressSound(false);
break;
case 1:
// Increment level, show ready, show pellets, show lives indicators
IncrementLevel();
statusDisplay.SetExtraLivesDisplayVisible(true);
statusDisplay.SetReadyTextVisible(true);
SetPelletsActive(true);
break;
case 2:
// Show pacman, show ghosts
SetPacManActive(true);
SetGhostsActive(true);
break;
case 3:
// Hide ready
statusDisplay.SetReadyTextVisible(false);
break;
case 4:
// Unfreeze
SetFrozen(false);
soundManager.StartGhostSound();
break;
}
}
private void TimeSequenceStepGhostCaught(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Freeze and hide pacman, but let ghosts already in the caught animation continue
SetFrozen(true, ghostIgnoreIfCaught: true);
SetPacManActive(false);
soundManager.PlayGhostEatSound();
break;
case 1:
// Unfreeze and reveal pacman
SetPacManActive(true);
SetFrozen(false);
ghostManager.GhostCaughtContinue();
soundManager.SetGhostRetreat(true);
break;
}
}
private void TimeSequenceStepPacManCaught(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Freeze (except for the ghost animations)
SetFrozen(true, ghostKeepAnimating: true);
soundManager.StopAllSound();
break;
case 1:
// Hide ghosts, start pacman death animation
SetGhostsActive(false);
pacMan.SetDead(true);
break;
case 2:
// Start playing death sound
soundManager.PlayDeathSound();
break;
case 3:
// Hide pacman, start next state
SetPacManActive(false);
if (gameState == PacManGameState.AttractModeDemo)
{
break;
}
if (extraLives > 0)
{
InsertTimeSequence(PacManTimeSequence.RestartLevel);
break;
}
InsertTimeSequence(PacManTimeSequence.GameOver);
break;
case 4:
if (gameState == PacManGameState.AttractModeDemo)
{
#if RECORDING_DEMO
// recorder.gameObject.SetActive(false);
#endif
InsertTimeSequence(PacManTimeSequence.AttractScreenIntroduction);
}
break;
}
}
private void TimeSequenceStepRestartLevel(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Hide playfield and pellets
SetMazeVisible(false);
SetPelletsActive(false);
break;
case 1:
// Make maze visible
RestartLevel();
SetMazeVisible(true);
break;
case 2:
// Take life, show ready, show pellets
DecrementLives();
statusDisplay.SetReadyTextVisible(true);
SetPelletsActive(true);
break;
case 3:
// Show pacman, show ghosts
SetPacManActive(true);
SetGhostsActive(true);
break;
case 4:
// Hide ready
statusDisplay.SetReadyTextVisible(false);
break;
case 5:
// Unfreeze
SetFrozen(false);
soundManager.SuppressSound(false);
soundManager.StartGhostSound();
soundManager.UpdatePelletCount(pelletCountRemaining);
break;
}
}
private void TimeSequenceStepGameOver(int sequenceProgress)
{
switch (sequenceProgress)
{
case 0:
// Show game over text, freeze power pellet blink
statusDisplay.SetGameOverTextVisible(true);
pelletManager.FreezePowerPelletsBlink(true);
break;
case 1:
// Stop text blinking, transition to attract screen
statusDisplay.SetLabel1UPTextBlinking(false);
StartAttractMode();
break;
}
}
public int TimeSequenceProgress
{
get => timeSequenceProgress;
}
public float TimeSequenceSecondsPassed
{
get => timeSequenceSecondsPassed;
set => TimeSequenceProgressToTime(value);
}
#endregion
}
}