using UdonSharp; using UnityEngine; namespace Marro.PacManUdon { public partial class GameManager : SyncedObject { [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] private StatusDisplay statusDisplay; [SerializeField] private PelletManager attractScreen; [SerializeField] private GameObject intermissionScreen; [SerializeField] private GameObject pressStartButtonScreen; [SerializeField] private PlayerInput playerInput; [SerializeField] private Animator demo; [SerializeField] private SoundManager soundManager; [SerializeField] private NetworkManager networkManagerSetup; [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 Intermission2Pole intermission2Pole; private Animator mazeSpriteAnimator; private int pelletCountTotal; private int pelletCountRemaining; private GameObject[] attractScreenElements; private GameObject[] intermissionScreenElements; 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() { attractScreenElements = new GameObject[attractScreen.transform.childCount]; for (int i = 0; i < attractScreenElements.Length; i++) { attractScreenElements[i] = attractScreen.transform.GetChild(i).gameObject; } intermissionScreenElements = new GameObject[intermissionScreen.transform.childCount]; for (int i = 0; i < intermissionScreenElements.Length; i++) { intermissionScreenElements[i] = intermissionScreen.transform.GetChild(i).gameObject; } maze = mazes[0]; mazeSpriteAnimator = maze.mazeSprite.GetComponent(); intermission2Pole = intermissionScreenElements[4].GetComponent(); networkManager.Initialize(); ghostManager.Initialize(maze.ghostStarts, maze.ghostTargets, pacMan, this); pacMan.Initialize(playerInput, maze.pacManStart, this); bonusFruit.Initialize(); pelletManager.Initialize(); statusDisplay.Initialize(); playerInput.Initialize(this); soundManager.Initialize(); intermission2Pole.Initialize(this, ghostManager.Ghosts[0]); HideEverything(); SetScore(0); SetHighScore(0); SetLevel(0); StartAttractMode(); } public override void SyncedUpdate() { TimeSequenceUpdate(networkManager.SyncedDeltaTime); } 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 ResetButtonPressed() { Debug.Log($"{gameObject} Reset button was pressed!"); Start(); } public void StartGameButtonPressed() { Debug.Log($"{gameObject} Start Game Button was pressed!"); if (networkManager.IsOwner) { networkManager.SendEventNow(NetworkEventType.StartGameButtonPressed); } StartTimeSequence(PacManTimeSequence.StartNewGame); } 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!"); pelletCountTotal = pelletManager.PelletCount; 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); } private void PrepareForCutscene() { HideEverything(); RestartLevel(); SetFrozen(true); } 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() { Debug.Log($"{gameObject} 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) { Debug.Log($"{gameObject} GhostCaught"); if (gameState == PacManGameState.AttractMode) { TimeSequenceSkipToNextStep(); return; } AddScore(scoreBonus); StartTimeSequence(PacManTimeSequence.GhostCaught); pacMan.HideUntilUnfrozen(); } public void PacManCaught() { return; StartTimeSequence(PacManTimeSequence.PacManCaught); } public void NoGhostsScared() { soundManager.SetGhostBlue(false); } public void NoGhostsRetreating() { soundManager.SetGhostRetreat(false); } public void Intermission2PoleUpdate() { Debug.Log($"{gameObject} Intermission2PoleUpdate"); TimeSequenceSkipToNextStep(); } void BoardClearAnimation() { ghostManager.gameObject.SetActive(false); mazeSpriteAnimator.SetBool("Blinking", true); } private void HideEverything() { SetPelletsActive(false); SetMazeVisible(false); SetGhostsActive(false); SetPacManActive(false); SetPressStartButtonScreenVisible(false); SetIntermissionScreenVisible(false); statusDisplay.SetGameOverTextVisible(false); statusDisplay.SetExtraLivesDisplayVisible(false); statusDisplay.SetLevelDisplayVisible(false); statusDisplay.SetPlayer1TextVisible(false); statusDisplay.SetReadyTextVisible(false); demo.gameObject.SetActive(false); } void SetPelletsActive(bool active) { pelletManager.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 SetIntermissionScreenVisible(bool visible) { intermissionScreen.SetActive(visible); } void SetGameState(PacManGameState newGameState) { // Debug.Log($"{gameObject} State transitioning from {gameState} to {newGameState}"); gameState = newGameState; } 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); } 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() { // Debug.Log($"{gameObject} Decremented lives from {extraLives} to {extraLives - 1}"); SetExtraLives(extraLives - 1); } void IncrementLives() { // 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); } } public override void CollectSyncedData(byte[] data, ref int offset, NetworkEventType eventType) { //data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)gameState) }; //data[offset++] = BitConverter.GetBytes(currentlyInTimeSequence); //data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)currentTimeSequence) }; //data[offset++] = BitConverter.GetBytes(timeSequenceSecondsPassed); } public override bool WriteSyncedData(byte[] data, ref int offset, NetworkEventType eventType) { if (eventType == NetworkEventType.StartGameButtonPressed) { StartGameButtonPressed(); } //SetGameState((PacManGameState)data[offset++]); //var currentlyInTimeSequence = BitConverter.ToBoolean(data, offset++); //var currentTimeSequence = (PacManTimeSequence)data[offset++]; //var timeSequenceSecondsPassed = BitConverter.ToSingle(data, offset); //offset += 4; //TimeSequenceSyncWithRemote(currentlyInTimeSequence, currentTimeSequence, timeSequenceSecondsPassed); return true; } 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; } } }