485 lines
15 KiB
C#
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;
|
|
}
|
|
}
|
|
} |