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 CollisionManager collisionManager; [SerializeField] private StatusDisplay statusDisplay; [SerializeField] private CollisionManager 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; private Maze maze; private Intermission2Pole intermission2Pole; private Animator mazeSpriteAnimator; private GameObject[] attractScreenElements; private GameObject[] intermissionScreenElements; private PacManGameState gameState; private int score; private int level; private int highScore; 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, collisionManager, this); pacMan.Initialize(playerInput, maze.pacManStart, this, collisionManager); bonusFruit.Initialize(); collisionManager.Initialize(this, bonusFruit, ghostManager.Ghosts); statusDisplay.Initialize(); playerInput.Initialize(this); soundManager.Initialize(); intermission2Pole.Initialize(this, ghostManager.Ghosts[0]); SubscribeToEvent(NetworkEventType.StartGameButtonPressed); SubscribeToEvent(NetworkEventType.FullSync); 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 RestartLevel(bool afterLifeLost = false) { //Debug.Log($"{gameObject} (Re)started level!"); mazeSpriteAnimator.SetBool("Blinking", false); ghostManager.RestartLevel(afterLifeLost); pacMan.Reset(); bonusFruit.Despawn(); soundManager.Reset(); collisionManager.Reset(); } private void PrepareForCutscene() { HideEverything(); RestartLevel(); SetFrozen(true); } internal void GotPellet(Pellet pellet, bool isPowerPellet, int pelletsCollectedCount, int pelletsRemainingCount) { AddScore(isPowerPellet ? 50 : 10); ghostManager.PelletConsumed(pelletsRemainingCount); soundManager.PlayPelletSound(); soundManager.UpdatePelletCount(pelletsRemainingCount); if (pelletsRemainingCount <= 0) { StartTimeSequence(PacManTimeSequence.BoardClear); return; } if (isPowerPellet) { if (gameState == PacManGameState.AttractMode) { TimeSequenceSkipToNextStep(); return; } ghostManager.SetPowerPellet(true); pacMan.SetPowerPellet(true); soundManager.SetGhostBlue(true); } if (pelletsCollectedCount == 70 || pelletsCollectedCount == 170) { bonusFruit.Spawn(); } } public void GotPowerPellet(Pellet pellet) { //Debug.Log($"{gameObject} GotPowerPellet"); } 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); } 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) { collisionManager.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); collisionManager.SetFrozen(frozen); } public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType) { if (eventType != NetworkEventType.FullSync) { return; } data.Append(currentlyInTimeSequence, ref index); if (currentlyInTimeSequence) { data.AppendAsByte((int)currentTimeSequence, ref index); data.Append(timeSequenceSecondsPassed, ref index); } data.AppendAsByte(level, ref index); data.Append(score, ref index); data.AppendAsByte(extraLives, ref index); bonusFruit.CollectSyncedData(data, ref index, eventType); collisionManager.CollectSyncedData(data, ref index, eventType); ghostManager.CollectSyncedData(data, ref index, eventType); pacMan.CollectSyncedData(data, ref index, eventType); } public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType) { if (eventType == NetworkEventType.StartGameButtonPressed) { StartGameButtonPressed(); return true; } if (eventType != NetworkEventType.FullSync) { return true; } var currentlyInTimeSequence = data.ReadBool(ref index); if (currentlyInTimeSequence) { var currentTimeSequence = (PacManTimeSequence)data.ReadByte(ref index); var timeSequenceSecondsPassed = data.ReadFloat(ref index); TimeSequenceSyncWithRemote(currentTimeSequence, timeSequenceSecondsPassed); } else { TimeSequenceTryEndCurrent(); } SetLevel(data.ReadByte(ref index)); SetScore(data.ReadInt(ref index)); SetExtraLives(data.ReadByte(ref index)); bonusFruit.WriteSyncedData(data, ref index, eventType); collisionManager.WriteSyncedData(data, ref index, eventType); ghostManager.WriteSyncedData(data, ref index, eventType); pacMan.WriteSyncedData(data, ref index, eventType); return true; } public PacManGameState GameState => gameState; public int Level => level; } }