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

485 lines
15 KiB
C#

namespace Marro.PacManUdon
{
using System;
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.SDK3.Data;
public class GhostManager : UdonSharpBehaviour
{
[NonSerialized] public GameController gameController;
private Ghost[] ghosts;
private Ghost blinky;
// Level constants
private float speedDefault;
private float speedScared;
private float speedReturn;
private float speedTunnel;
private float speedHome;
private float speedElroy1;
private float speedElroy2;
private int elroy1PelletCount;
private int elroy2PelletCount;
// Power Pellet logic
private bool powerPelletActive;
private float powerPelletDuration;
private float powerPelletCountdown;
private int powerPelletMultiplier;
private DataList ghostScaredQueue;
// Blink logic
private float blinkCycleRate = 0.233333333f;
private bool blinkingActivated;
private float blinkCountdown;
private bool blinkCurrentlyWhite;
// Scattering logic
private float scatterCounter;
private float[] scatterPattern;
private int scatterPatternIndex;
// Elroy logic
private int elroyLevel;
private int pelletsRemaining;
// Ghost house logic
private bool sharedPelletCounterActive;
private int sharedPelletCounter;
private int[] sharedPelletCounterReleaseValues = { 0, 7, 17, 32 };
private float pelletTimeout;
private float pelletTimeoutLimit;
private bool frozen;
// This should be called once when the game is initialized
public void Initialize(GameObject[] ghostTargets, PacMan pacMan, GameController gameController)
{
this.gameController = gameController;
ghosts = transform.GetComponentsInChildren<Ghost>(true);
blinky = ghosts[0];
for (int ghostIndex = 0; ghostIndex < ghosts.Length; ghostIndex++)
{
Vector2 homePosition = ghostTargets[0].transform.localPosition;
Vector2 idlePosition1 = ghostTargets[1 + ghostIndex * 3].transform.localPosition;
Vector2 idlePosition2 = ghostTargets[2 + ghostIndex * 3].transform.localPosition;
Vector2 cornerPosition = ghostTargets[3 + ghostIndex * 3].transform.localPosition;
ghosts[ghostIndex].Initialize(pacMan, blinky, homePosition, idlePosition1, idlePosition2, cornerPosition);
}
}
// This should be called every time the level is reset
public void Reset()
{
ghostScaredQueue = new DataList();
powerPelletActive = false;
scatterCounter = 0;
scatterPatternIndex = 0;
SetScattering(false, reverseDirection: false);
sharedPelletCounter = 0;
pelletTimeout = 0;
elroyLevel = 0;
foreach (Ghost ghost in ghosts)
{
ghost.Reset();
}
SetScattering(true, reverseDirection: false);
}
public void NewLevel()
{
SetSharedPelletCounterActive(false);
foreach (Ghost ghost in ghosts)
{
ghost.ResetHousePelletCounter();
}
}
public void LifeLost()
{
SetSharedPelletCounterActive(true);
}
public void FixedUpdate()
{
// gameStateManager.statusDisplay.SetDebugText(1, this.blinkCountdown.ToString());
if (frozen)
{
return;
}
UpdatePelletTimeout();
if (powerPelletActive)
{
UpdatePowerPellet();
}
else
{
UpdateScattering();
}
}
void UpdateScattering()
{
scatterCounter += Time.deltaTime;
if (scatterPatternIndex < scatterPattern.Length && scatterCounter >= scatterPattern[scatterPatternIndex])
{
// Debug.Log($"{gameObject} SetScattering: {scatterPatternIndex%1 == 0}, scatterCounter: {scatterCounter}, scatterPattern: {scatterPattern[scatterPatternIndex]}, scatterPatternIndex: {scatterPatternIndex}");
scatterPatternIndex++;
SetScattering(scatterPatternIndex % 2 == 0);
}
}
void UpdatePowerPellet()
{
powerPelletCountdown -= Time.deltaTime;
if (powerPelletCountdown <= 0)
{
gameController.EndPowerPellet(); // End power pellet
SetPowerPellet(false);
}
else if (!blinkingActivated && powerPelletCountdown <= 2.1166667)
{
SetGhostBlinking(true);
}
if (blinkingActivated)
{
blinkCountdown -= Time.deltaTime;
if (blinkCountdown <= 0)
{
blinkCountdown += blinkCycleRate;
SetGhostBlinkingState(!blinkCurrentlyWhite);
}
}
}
void UpdatePelletTimeout()
{
pelletTimeout += Time.deltaTime;
if (pelletTimeout >= pelletTimeoutLimit)
{
pelletTimeout -= pelletTimeoutLimit;
for (int ghostIndex = 0; ghostIndex < ghosts.Length; ghostIndex++)
{
Ghost ghost = ghosts[ghostIndex];
if (ghost.GetGhostState() == PacManGhostState.Home)
{
ghost.Release();
break;
}
}
}
}
public void GhostCaughtQueue(Ghost ghost)
{
if (gameController.GameState == PacManGameState.AttractMode)
{
gameController.GhostCaught(0);
return;
}
Debug.Log($"{gameObject} GhostCaughtQueue with ghost {ghost}");
ghostScaredQueue.Add(ghost);
GhostCaughtExecute(ghost);
}
public void GhostCaughtContinue()
{
Debug.Log($"{gameObject} GhostCaughtContinue with ghost queue length {ghostScaredQueue.Count}");
if (!ghostScaredQueue.TryGetValue(0, out DataToken currentGhost))
{
Debug.LogError("Called GhostCaughtContinue without a ghost in the queue!");
return;
}
((Ghost)currentGhost.Reference).ReturnHome();
ghostScaredQueue.RemoveAt(0);
if (ghostScaredQueue.TryGetValue(0, out DataToken nextGhost))
{
GhostCaughtExecute((Ghost)nextGhost.Reference);
}
}
void GhostCaughtExecute(Ghost ghost)
{
int scoreBonus = 200 * (int)Math.Pow(2, powerPelletMultiplier);
powerPelletMultiplier += 1;
ghost.Caught(scoreBonus);
gameController.GhostCaught(scoreBonus);
}
public void CapturedPacMan()
{
gameController.PacManCaught();
}
public void GhostReturned()
{
bool noGhostsScared = true;
bool noGhostsRetreating = true;
foreach (var ghost in ghosts)
{
var state = ghost.GetGhostState();
if (ghost.IsScared)
{
noGhostsScared = false;
}
if (state == PacManGhostState.CaughtScore || state == PacManGhostState.Returning || state == PacManGhostState.Entering)
{
noGhostsRetreating = false;
}
}
if (noGhostsScared)
{
gameController.NoGhostsScared();
}
if (noGhostsRetreating)
{
gameController.NoGhostsRetreating();
}
}
void SetGhostBlinking(bool blinking)
{
blinkingActivated = blinking;
if (blinking == true)
{
blinkCountdown = blinkCycleRate;
SetGhostBlinkingState(true);
}
}
void SetGhostBlinkingState(bool white)
{
blinkCurrentlyWhite = white;
foreach (Ghost ghost in ghosts)
{
ghost.SetWhite(white);
}
}
public void SetPowerPellet(bool powerPelletActive)
{
this.powerPelletActive = powerPelletActive;
if (powerPelletActive)
{
powerPelletCountdown = powerPelletDuration;
powerPelletMultiplier = 0;
SetGhostBlinking(false);
foreach (Ghost ghost in ghosts)
{
ghost.BecomeScared();
}
}
if (!powerPelletActive || powerPelletDuration == 0)
{
foreach (Ghost ghost in ghosts)
{
ghost.CalmDown();
SetGhostBlinking(false);
}
}
}
public void SetFrozen(bool frozen, bool ignoreIfCaught = false)
{
this.frozen = frozen;
foreach (Ghost ghost in ghosts)
{
ghost.SetFrozen(frozen, ignoreIfCaught: ignoreIfCaught);
}
}
public void SetLevel(int level)
{
speedDefault = PacManConstants.GetGhostDefaultSpeedForLevel(level);
speedScared = PacManConstants.GetGhostScaredSpeedForLevel(level);
speedReturn = 15f;
speedTunnel = PacManConstants.GetGhostTunnelSpeedForLevel(level);
speedHome = PacManConstants.GetGhostHomeSpeed();
speedElroy1 = PacManConstants.GetBlinkyElroy1SpeedForLevel(level);
speedElroy2 = PacManConstants.GetBlinkyElroy2SpeedForLevel(level);
elroy1PelletCount = PacManConstants.GetElroy1PelletsRemainingForLevel(level);
elroy2PelletCount = PacManConstants.GetElroy2PelletsRemainingForLevel(level);
powerPelletDuration = PacManConstants.GetScaredDurationForLevel(level);
scatterPattern = PacManConstants.GetScatterPatternForLevel(level);
pelletTimeoutLimit = PacManConstants.GetGhostHousePelletTimeoutLimitForLevel(level);
int[] privatePelletCounterReleaseValues = PacManConstants.GetGhostHousePrivatePelletCounterLimitForLevel(level);
for (int i = 0; i < Math.Min(sharedPelletCounterReleaseValues.Length, ghosts.Length); i++)
{
ghosts[i].SetHousePelletCounterLimit(privatePelletCounterReleaseValues[i]);
}
}
public void SetOwner(VRCPlayerApi player)
{
Networking.SetOwner(player, gameObject);
foreach (Ghost ghost in ghosts)
{
Networking.SetOwner(player, ghost.gameObject);
}
}
public float GetTargetSpeed(Ghost ghost, PacManGhostState ghostState, bool isScared, bool inTunnel)
{
if (ghostState == PacManGhostState.Returning || ghostState == PacManGhostState.Entering)
{
return speedReturn;
}
if (inTunnel)
{
return speedTunnel;
}
if (ghostState == PacManGhostState.Exiting || ghostState == PacManGhostState.Home)
{
return speedHome;
}
if (isScared)
{
return speedScared;
}
if (ghost == blinky)
{
if (elroyLevel == 1)
{
return speedElroy1;
}
if (elroyLevel == 2)
{
return speedElroy2;
}
}
return speedDefault;
}
void SetScattering(bool scattering, bool reverseDirection = true)
{
Debug.Log($"{gameObject} SetScattering: {scattering}");
foreach (Ghost ghost in ghosts)
{
if (ghost == blinky && pelletsRemaining <= elroy1PelletCount)
{
continue;
}
ghost.SetScattering(scattering, reverseDirection);
}
}
public void SetPelletsRemaining(int pelletsRemaining)
{
this.pelletsRemaining = pelletsRemaining;
UpdateElroyLevel();
}
void SetSharedPelletCounterActive(bool active)
{
// Debug.Log($"{gameObject} SetSharedPelletCounterActive {active}");
sharedPelletCounterActive = active;
foreach (Ghost ghost in ghosts)
{
ghost.SetHousePelletCounterActive(!active);
}
}
public void PelletConsumed()
{
SetPelletsRemaining(pelletsRemaining - 1);
pelletTimeout = 0;
if (sharedPelletCounterActive)
{
IncrementSharedPelletCounter();
}
else
{
IncrementPrivatePelletCounter();
}
}
void IncrementSharedPelletCounter()
{
sharedPelletCounter++;
for (int ghostIndex = 0; ghostIndex < sharedPelletCounterReleaseValues.Length; ghostIndex++)
{
Ghost ghost = ghosts[ghostIndex];
if (ghost.GetGhostState() == PacManGhostState.Home && sharedPelletCounter == sharedPelletCounterReleaseValues[ghostIndex])
{
ghost.Release();
if (ghostIndex == sharedPelletCounterReleaseValues.Length - 1)
{
SetSharedPelletCounterActive(false);
}
}
}
}
void IncrementPrivatePelletCounter()
{
for (int ghostIndex = 0; ghostIndex < ghosts.Length; ghostIndex++)
{
Ghost ghost = ghosts[ghostIndex];
if (ghost.GetGhostState() == PacManGhostState.Home)
{
ghost.IncrementHousePelletCounter();
break;
}
}
}
void UpdateElroyLevel()
{
// Debug.Log($"{gameObject} Updating Elroy Level with pelletsRemaining {pelletsRemaining} with elroy2PelletCount {elroy2PelletCount} and elroy1PelletCount {elroy1PelletCount}");
int oldElroyLevel = elroyLevel;
if (pelletsRemaining < elroy2PelletCount) elroyLevel = 2;
else if (pelletsRemaining < elroy1PelletCount) elroyLevel = 1;
else elroyLevel = 0;
if (elroyLevel != oldElroyLevel)
{
blinky.SetElroy(elroyLevel);
}
}
public void SetActive(bool active)
{
gameObject.SetActive(active);
foreach (Ghost ghost in ghosts)
{
ghost.SetActive(active);
}
}
public void SetKinematic(bool kinematic)
{
foreach (Ghost ghost in ghosts)
{
ghost.SetKinematic(kinematic);
}
}
public Ghost[] Ghosts
{
get => ghosts;
}
}
}