509 lines
15 KiB
C#
509 lines
15 KiB
C#
#define RECORDING_DEMO
|
|
|
|
namespace Marro.PacManUdon
|
|
{
|
|
using System;
|
|
using UdonSharp;
|
|
using UnityEngine;
|
|
using VRC.SDK3.Components;
|
|
using VRC.SDKBase;
|
|
|
|
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] public StatusDisplay statusDisplay; // This one is public so other scripts can write to the debug display
|
|
[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 networkManager;
|
|
|
|
[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 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];
|
|
pelletPool = maze.pelletContainer.GetComponent<VRCObjectPool>();
|
|
mazeSpriteAnimator = maze.mazeSprite.GetComponent<Animator>();
|
|
intermission2Pole = intermissionScreenElements[4].GetComponent<Intermission2Pole>();
|
|
|
|
ghostManager.Initialize(maze.ghostTargets, pacMan, this);
|
|
pacMan.Initialize(playerInput, pelletPool, this);
|
|
bonusFruit.Initialize();
|
|
pelletManager.Initialize(pelletPool);
|
|
statusDisplay.Initialize();
|
|
playerInput.Initialize(this);
|
|
soundManager.Initialize();
|
|
intermission2Pole.Initialize(this, ghostManager.Ghosts[0]);
|
|
networkManager.Initialize();
|
|
|
|
HideEverything();
|
|
|
|
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 ResetButtonPressed()
|
|
{
|
|
Debug.Log($"{gameObject} Reset button was pressed!");
|
|
Start();
|
|
}
|
|
|
|
public void StartGameButtonPressed()
|
|
{
|
|
Debug.Log($"{gameObject} Start Game Button was pressed!");
|
|
TakeOwnership();
|
|
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 = 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);
|
|
}
|
|
|
|
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()
|
|
{
|
|
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);
|
|
}
|
|
|
|
public void 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)
|
|
{
|
|
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 SetIntermissionScreenVisible(bool visible)
|
|
{
|
|
intermissionScreen.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 override void AppendSyncedData(byte[][] data, ref int offset, NetworkEventType eventType, uint eventTime)
|
|
{
|
|
data[offset++] = new byte[] { Int32ToByte((int)gameState) };
|
|
data[offset++] = BitConverter.GetBytes(currentlyInTimeSequence);
|
|
data[offset++] = new byte[] { Int32ToByte((int)currentTimeSequence) };
|
|
data[offset++] = BitConverter.GetBytes(timeSequenceProgress);
|
|
}
|
|
|
|
public override bool SetSyncedData(byte[] data, ref int offset, NetworkEventType eventType, uint eventTime)
|
|
{
|
|
SetGameState((PacManGameState)data[offset++]);
|
|
|
|
var currentlyInTimeSequence = BitConverter.ToBoolean(data, offset++);
|
|
var currentTimeSequence = (PacManTimeSequence)data[offset++];
|
|
var timeSequenceProgress = BitConverter.ToSingle(data, offset);
|
|
offset += 4;
|
|
TimeSequenceSyncWithRemote(currentlyInTimeSequence, currentTimeSequence, timeSequenceProgress);
|
|
|
|
return true;
|
|
}
|
|
|
|
public override void SyncedToNewTime(uint oldTime, uint newTime)
|
|
{
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public static byte Int32ToByte(int value) =>
|
|
(byte)value;
|
|
//byte.Parse(value.ToString()); // This is the only way I could find to cast an int to byte in Udon, a regular cast crashes in runtime...
|
|
}
|
|
} |