875 lines
31 KiB
C#
875 lines
31 KiB
C#
#define RECORDING_DEMO
|
|
|
|
namespace Marro.PacManUdon
|
|
{
|
|
using UdonSharp;
|
|
using System;
|
|
using UnityEngine;
|
|
using VRC.SDKBase;
|
|
using VRC.Udon;
|
|
using VRC.SDK3.Components;
|
|
using VRC.Udon.Common.Interfaces;
|
|
|
|
enum PacManGhostType
|
|
{
|
|
Caught,
|
|
Scared,
|
|
ScaredWhite,
|
|
Blinky,
|
|
Pinky,
|
|
Inky,
|
|
Clyde,
|
|
Special,
|
|
}
|
|
|
|
public enum PacManGhostState
|
|
{
|
|
Normal,
|
|
CaughtScore,
|
|
Returning,
|
|
Entering,
|
|
Home,
|
|
Exiting
|
|
}
|
|
|
|
public enum PacManGhostStartState
|
|
{
|
|
Outside,
|
|
TargetingIdlePosition1,
|
|
TargetingIdlePosition2
|
|
}
|
|
|
|
public class Ghost : GridMover
|
|
{
|
|
[SerializeField] private PacManGhostType ghostType;
|
|
[SerializeField] private PacManGhostStartState startState;
|
|
|
|
private GhostManager ghostManager;
|
|
private Animator animator;
|
|
private new Renderer renderer;
|
|
private PacMan pacMan;
|
|
private Ghost blinky;
|
|
private ScoreBonusDisplay scoreBonusDisplay;
|
|
|
|
private Vector3 startPosition;
|
|
private Quaternion startRotation;
|
|
private Vector3 startScale;
|
|
|
|
private Vector2 homePosition;
|
|
private Vector2 idlePosition1;
|
|
private Vector2 idlePosition2;
|
|
private Vector2 cornerPosition;
|
|
|
|
private bool kinematic;
|
|
|
|
private bool horizontalOnly;
|
|
private int housePelletCounterLimit;
|
|
|
|
private bool faceInStartingDirectionUntilUnfrozen;
|
|
private bool specialLook;
|
|
|
|
private bool followingPredefinedPath;
|
|
private Vector2[] predefinedPath;
|
|
private int predefinedPathIndex;
|
|
|
|
[UdonSynced] int rngState;
|
|
[UdonSynced] private Vector2 syncedPosition;
|
|
[UdonSynced] private float speed;
|
|
[UdonSynced] private Vector2 direction;
|
|
[UdonSynced] private Vector2 target;
|
|
[UdonSynced] private bool offGrid;
|
|
[UdonSynced] private bool inTunnel;
|
|
[UdonSynced] private PacManGhostState ghostState;
|
|
[UdonSynced] private bool isScared;
|
|
[UdonSynced] private bool scattering;
|
|
[UdonSynced] private PacManGhostFrozenState frozenState;
|
|
[UdonSynced] private bool hideUntilUnfrozen;
|
|
[UdonSynced] private int housePelletCounter;
|
|
[UdonSynced] private bool housePelletCounterActive;
|
|
[UdonSynced] private bool turnAroundSoon;
|
|
|
|
public void Initialize(PacMan pacMan, Ghost blinky, Vector2 homePosition, Vector2 idlePosition1, Vector2 idlePosition2, Vector2 cornerPosition)
|
|
{
|
|
ghostManager = transform.parent.GetComponent<GhostManager>();
|
|
animator = GetComponent<Animator>();
|
|
renderer = GetComponent<Renderer>();
|
|
|
|
this.pacMan = pacMan;
|
|
this.blinky = blinky;
|
|
this.homePosition = homePosition;
|
|
this.idlePosition1 = idlePosition1;
|
|
this.idlePosition2 = idlePosition2;
|
|
this.cornerPosition = cornerPosition;
|
|
|
|
scoreBonusDisplay = transform.Find("ScoreBonusDisplay").gameObject.GetComponent<ScoreBonusDisplay>();
|
|
scoreBonusDisplay.Initialize();
|
|
startPosition = transform.localPosition;
|
|
startRotation = transform.localRotation;
|
|
startScale = transform.localScale;
|
|
|
|
frozenState = PacManGhostFrozenState.Frozen;
|
|
// Debug.Log($"{gameObject} Begin localScale = {initialScale}");
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
// Debug.Log($"{gameObject} Reset!");
|
|
transform.localPosition = startPosition;
|
|
transform.localRotation = startRotation;
|
|
transform.localScale = startScale;
|
|
// Debug.Log($"{gameObject} Reset localScale = {transform.localScale}");
|
|
|
|
if (startState == PacManGhostStartState.Outside)
|
|
{
|
|
ghostState = PacManGhostState.Exiting;
|
|
OffGridTargetReached();
|
|
}
|
|
else
|
|
{
|
|
if (startState == PacManGhostStartState.TargetingIdlePosition1)
|
|
{
|
|
SetOffGridTarget(idlePosition1, false);
|
|
}
|
|
ghostState = PacManGhostState.Entering;
|
|
OffGridTargetReached();
|
|
}
|
|
|
|
isScared = false;
|
|
inTunnel = false;
|
|
kinematic = false;
|
|
followingPredefinedPath = false;
|
|
turnAroundSoon = false;
|
|
specialLook = false;
|
|
|
|
rngState = 1;
|
|
UpdateSpeed();
|
|
|
|
faceInStartingDirectionUntilUnfrozen = true;
|
|
UpdateAnimator();
|
|
RequestSerialization();
|
|
|
|
// Debug.Log($"{gameObject} reset with state: {state}, target: {target}, offGrid: {offGrid}");
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
if (ghostType == PacManGhostType.Blinky)
|
|
{
|
|
// ghostManager.gameStateManager.statusDisplay.SetDebugText(2, $"{turnAroundSoon}");
|
|
}
|
|
if (frozenState == PacManGhostFrozenState.Frozen ||
|
|
(frozenState == PacManGhostFrozenState.FrozenIfNotCaught && ((ghostState != PacManGhostState.Returning && ghostState != PacManGhostState.Entering) || hideUntilUnfrozen)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Vector2 position = GetPosition();
|
|
Vector2 nextPosition = GridMoverTools.GetNextPosition(position, direction, speed);
|
|
|
|
nextPosition = ProcessNextPosition(position, nextPosition);
|
|
|
|
SetPosition(nextPosition);
|
|
}
|
|
|
|
private Vector2 ProcessNextPosition(Vector2 position, Vector2 nextPosition)
|
|
{
|
|
if (turnAroundSoon && ghostState == PacManGhostState.Normal
|
|
&& GridMoverTools.CrossesTileBorder(position, nextPosition, direction.x != 0, direction.y != 0))
|
|
{
|
|
// Debug.Log($"{gameObject} turned around");
|
|
SetDirection(direction * -1);
|
|
turnAroundSoon = false;
|
|
}
|
|
|
|
if (kinematic)
|
|
{
|
|
return nextPosition;
|
|
}
|
|
|
|
if (offGrid || ghostState == PacManGhostState.Returning)
|
|
{
|
|
bool XAxisAlligned = CheckAndAllignToTargetX(position, nextPosition, target);
|
|
bool YAxisAlligned = CheckAndAllignToTargetY(position, nextPosition, target);
|
|
if (offGrid)
|
|
{
|
|
if (XAxisAlligned)
|
|
{
|
|
nextPosition.x = target.x;
|
|
}
|
|
if (YAxisAlligned)
|
|
{
|
|
nextPosition.y = target.y;
|
|
}
|
|
}
|
|
if (XAxisAlligned && YAxisAlligned)
|
|
{
|
|
if (!offGrid)
|
|
{
|
|
nextPosition = target;
|
|
}
|
|
OffGridTargetReached();
|
|
// Debug.Log($"{gameObject} reached offGridTarget {target} at position {position}, new state {state}, offGrid is now {offGrid}, new direction: {direction}");
|
|
}
|
|
if ((XAxisAlligned || YAxisAlligned) && offGrid)
|
|
{
|
|
SetDirection(GetOffGridDirectionToTarget(nextPosition, target, direction));
|
|
// Debug.Log($"{gameObject} Alligned X Axis: {XAxisAlligned}, Y Axis: {YAxisAlligned} with position: {position}, new nextPosition: {nextPosition}, new target: {target}, now moving in direction {direction}");
|
|
// nextPosition = GridMover.GetNextPosition(position, direction, speed);
|
|
}
|
|
}
|
|
if (!offGrid && followingPredefinedPath)
|
|
{
|
|
nextPosition = ProcessPredefinedPath(position, nextPosition);
|
|
}
|
|
else if (!offGrid && GridMoverTools.CrossesTileCenter(position, nextPosition, direction.x != 0, direction.y != 0))
|
|
{
|
|
Vector2 gridPosition = GridMoverTools.PositionToGrid(position);
|
|
Vector2[] availableDirections = GetAvailableDirections(gridPosition, direction * -1);
|
|
if (availableDirections.Length > 1)
|
|
{
|
|
target = GetGridTarget(gridPosition);
|
|
SetDirection(GetGridDirectionToTargetGreedy(availableDirections, gridPosition, target));
|
|
nextPosition = GridMoverTools.GetNextPosition(gridPosition, direction, speed);
|
|
// Debug.Log($"GetNextPosition at gridPosition {gridPosition} with direction {direction} and speed {speed} gives nextPosition {nextPosition}");
|
|
}
|
|
else if (availableDirections.Length == 1 && availableDirections[0] != direction)
|
|
{
|
|
SetDirection(availableDirections[0]);
|
|
nextPosition = GridMoverTools.GetNextPosition(gridPosition, direction, speed);
|
|
}
|
|
// Debug.Log($"{gameObject} crossed tile center {gridPosition}, new target: {target}, new direction: {direction}");
|
|
}
|
|
|
|
return nextPosition;
|
|
}
|
|
|
|
private Vector2 ProcessPredefinedPath(Vector2 position, Vector2 nextPosition)
|
|
{
|
|
if (GridMoverTools.CrossesTileCenter(position, nextPosition, direction.x != 0, direction.y != 0))
|
|
{
|
|
// Find the next valid direction which isn't Vector2.zero
|
|
int nextValidDirectionIndex = predefinedPathIndex;
|
|
while (predefinedPath[nextValidDirectionIndex] == Vector2.zero)
|
|
{
|
|
nextValidDirectionIndex += 1;
|
|
}
|
|
if (!GridMoverTools.CheckCollisionInDirection(transform, nextPosition, predefinedPath[nextValidDirectionIndex]))
|
|
{
|
|
// If we're at a Vector2.zero, we skip applying the direction and only increment.
|
|
if (nextValidDirectionIndex == predefinedPathIndex)
|
|
{
|
|
SetDirection(predefinedPath[nextValidDirectionIndex]);
|
|
nextPosition = GridMoverTools.PositionToGrid(nextPosition) + direction.normalized * 0.01f;
|
|
|
|
// Check if we've reached the end of the path, which includes making sure the path doesn't end on Vector2.zero
|
|
do
|
|
{
|
|
nextValidDirectionIndex += 1;
|
|
if (nextValidDirectionIndex >= predefinedPath.Length)
|
|
{
|
|
followingPredefinedPath = false;
|
|
break;
|
|
}
|
|
} while (predefinedPath[nextValidDirectionIndex] == Vector2.zero);
|
|
}
|
|
|
|
// ghostManager.gameStateManager.statusDisplay.SetDebugText(1, predefinedPathIndex.ToString());
|
|
|
|
predefinedPathIndex++;
|
|
}
|
|
}
|
|
return nextPosition;
|
|
}
|
|
|
|
Vector2 GetGridTarget(Vector2 gridPosition)
|
|
{
|
|
// if (followingPredefinedPath)
|
|
// {
|
|
// predefinedPathIndex++;
|
|
// if (predefinedPathIndex >= predefinedPath.Length)
|
|
// {
|
|
// followingPredefinedPath = false;
|
|
// }
|
|
// return gridPosition + predefinedPath[predefinedPathIndex];
|
|
// }
|
|
if (isScared)
|
|
{
|
|
switch (PseudoRNG() % 4)
|
|
{
|
|
default:
|
|
return gridPosition;
|
|
case 0:
|
|
return gridPosition + Vector2.up;
|
|
case 1:
|
|
return gridPosition + Vector2.left;
|
|
case 2:
|
|
return gridPosition + Vector2.down;
|
|
case 3:
|
|
return gridPosition + Vector2.right;
|
|
}
|
|
}
|
|
switch (ghostState)
|
|
{
|
|
default:
|
|
return gridPosition;
|
|
case PacManGhostState.Normal:
|
|
if (scattering)
|
|
{
|
|
return cornerPosition;
|
|
}
|
|
switch (ghostType)
|
|
{
|
|
default:
|
|
return gridPosition;
|
|
case PacManGhostType.Blinky:
|
|
return GridMoverTools.PositionToGrid(pacMan.transform.localPosition);
|
|
case PacManGhostType.Pinky:
|
|
return GridMoverTools.PositionToGrid(pacMan.transform.localPosition) + pacMan.GetDirection() * 4;
|
|
case PacManGhostType.Inky:
|
|
return 2 * GridMoverTools.PositionToGrid(pacMan.transform.localPosition) + 4 * pacMan.GetDirection() - GridMoverTools.PositionToGrid(blinky.transform.localPosition);
|
|
case PacManGhostType.Clyde:
|
|
if (Vector2.Distance(gridPosition, GridMoverTools.PositionToGrid(pacMan.transform.localPosition)) >= 8)
|
|
{
|
|
return GridMoverTools.PositionToGrid(pacMan.transform.localPosition);
|
|
}
|
|
else
|
|
{
|
|
// Debug.Log($"{gameObject} goes to cornerPosition {cornerPosition}");
|
|
return cornerPosition;
|
|
}
|
|
}
|
|
case PacManGhostState.Returning:
|
|
return homePosition;
|
|
}
|
|
}
|
|
|
|
bool CheckAndAllignToTargetX(Vector2 currentPosition, Vector2 nextPosition, Vector2 target)
|
|
{
|
|
return (currentPosition.x - target.x) * (nextPosition.x - target.x) <= 0.01;
|
|
}
|
|
|
|
bool CheckAndAllignToTargetY(Vector2 currentPosition, Vector2 nextPosition, Vector2 target)
|
|
{
|
|
return (currentPosition.y - target.y) * (nextPosition.y - target.y) <= 0.01;
|
|
}
|
|
|
|
void OffGridTargetReached()
|
|
{
|
|
switch (ghostState)
|
|
{
|
|
case PacManGhostState.Returning:
|
|
offGrid = true;
|
|
SetState(PacManGhostState.Entering);
|
|
SetOffGridTarget(idlePosition2, false);
|
|
break;
|
|
case PacManGhostState.Entering:
|
|
offGrid = true;
|
|
SetState(PacManGhostState.Home);
|
|
ghostManager.GhostReturned();
|
|
if (!target.Equals(idlePosition1))
|
|
{ // This is idlePosition1 if PacManGhostStartState == TargetingIdlePosition1
|
|
SetOffGridTarget(idlePosition2, false);
|
|
}
|
|
TryToExit();
|
|
break;
|
|
case PacManGhostState.Home:
|
|
if (target.Equals(idlePosition1))
|
|
{
|
|
SetOffGridTarget(idlePosition2, false);
|
|
}
|
|
else
|
|
{
|
|
SetOffGridTarget(idlePosition1, false);
|
|
}
|
|
break;
|
|
case PacManGhostState.Exiting:
|
|
offGrid = false;
|
|
SetState(PacManGhostState.Normal);
|
|
SetDirection(Vector2.left);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetOffGridTarget(Vector2 newTarget, bool startHorizontal)
|
|
{
|
|
if (startHorizontal)
|
|
{
|
|
SetDirection(GetOffGridDirectionToTarget(GetPosition(), newTarget, Vector2.right));
|
|
}
|
|
else
|
|
{
|
|
SetDirection(GetOffGridDirectionToTarget(GetPosition(), newTarget, Vector2.down));
|
|
}
|
|
// Debug.Log($"{gameObject} SetOffGridTarget with position {GetPosition()}, newTarget {newTarget}, startHorizontal {startHorizontal} resulted in direction {direction}");
|
|
target = newTarget;
|
|
}
|
|
|
|
public int PseudoRNG()
|
|
{
|
|
rngState ^= rngState << 13;
|
|
rngState ^= rngState >> 17;
|
|
rngState ^= rngState << 5;
|
|
return rngState;
|
|
}
|
|
|
|
Vector2[] GetAvailableDirections(Vector2 gridPosition, Vector2 discardDirection)
|
|
{
|
|
Vector2[] directions;
|
|
Vector2[] availableDirections;
|
|
if (horizontalOnly && ghostState == PacManGhostState.Normal)
|
|
{
|
|
directions = new Vector2[] { new Vector2(1, 0), new Vector2(-1, 0) };
|
|
availableDirections = new Vector2[2];
|
|
}
|
|
else
|
|
{
|
|
directions = new Vector2[] { new Vector2(0, 1), new Vector2(1, 0), new Vector2(0, -1), new Vector2(-1, 0) };
|
|
availableDirections = new Vector2[4];
|
|
}
|
|
|
|
int availableDirectionsNum = 0;
|
|
for (int i = 0; i < directions.Length; i++)
|
|
{
|
|
if (directions[i].Equals(discardDirection) || GridMoverTools.CheckCollisionInDirection(transform, gridPosition, directions[i]))
|
|
{
|
|
continue;
|
|
}
|
|
availableDirections[availableDirectionsNum] = directions[i];
|
|
availableDirectionsNum++;
|
|
}
|
|
Vector2[] availableDirectionsTrimmed = new Vector2[availableDirectionsNum];
|
|
// Debug.Log($"{gameObject} Number of available directions: {availableDirectionsTrimmed.Length}");
|
|
Array.Copy(availableDirections, 0, availableDirectionsTrimmed, 0, availableDirectionsNum);
|
|
|
|
return availableDirectionsTrimmed;
|
|
}
|
|
|
|
Vector2 GetGridDirectionToTargetGreedy(Vector2[] availableDirections, Vector2 gridPosition, Vector2 targetGridPosition)
|
|
{
|
|
Vector2 bestDirection = Vector2.zero;
|
|
float bestDistance = float.MaxValue;
|
|
for (int i = 0; i < availableDirections.Length; i++)
|
|
{
|
|
Vector2 direction = availableDirections[i];
|
|
|
|
// Debug.Log("Evaluating direction " + direction);
|
|
float distance = Vector2.Distance(gridPosition + direction, targetGridPosition);
|
|
if (distance < bestDistance)
|
|
{
|
|
bestDistance = distance;
|
|
bestDirection = direction;
|
|
}
|
|
}
|
|
// Debug.Log("Closest next tile is in direction " + bestDirection);
|
|
return bestDirection;
|
|
}
|
|
|
|
Vector2 GetOffGridDirectionToTarget(Vector2 position, Vector2 target, Vector2 direction)
|
|
{
|
|
if ((direction.x != 0 && position.x != target.x) || position.y == target.y)
|
|
{
|
|
// Debug.Log($"{gameObject} getOffGridDirectionToTarget with position {position}, target {target} and direction {direction} gives movement on the X axis in direction {new Vector2(target.x-position.x, 0).normalized}");
|
|
return new Vector2(target.x - position.x, 0).normalized;
|
|
}
|
|
else
|
|
{
|
|
// Debug.Log($"{gameObject} getOffGridDirectionToTarget with position {position}, target {target} and direction {direction} gives movement on the Y axis in direction {new Vector2(0, target.y-position.y).normalized}");
|
|
return new Vector2(0, target.y - position.y).normalized;
|
|
}
|
|
}
|
|
|
|
private void UpdateAnimator()
|
|
{
|
|
if (!gameObject.activeInHierarchy)
|
|
return;
|
|
|
|
// Debug.Log($"{gameObject} UpdateAnimator with state: {ghostState}, isScared: {isScared}, direction: {direction}");
|
|
if (specialLook)
|
|
{
|
|
animator.SetFloat("GhostType", GhostTypeToAnimationValue(PacManGhostType.Special));
|
|
}
|
|
else if (isScared)
|
|
{
|
|
float currentGhostType = animator.GetFloat("GhostType");
|
|
if (currentGhostType > 0.5f && currentGhostType < 2.5f)
|
|
{
|
|
return;
|
|
}
|
|
animator.SetFloat("GhostType", GhostTypeToAnimationValue(PacManGhostType.Scared));
|
|
}
|
|
else
|
|
{
|
|
switch (ghostState)
|
|
{
|
|
default:
|
|
case PacManGhostState.Normal:
|
|
case PacManGhostState.Home:
|
|
case PacManGhostState.Exiting:
|
|
// Debug.Log($"{gameObject} Set GhostType in animator to: {GhostTypeToAnimationValue(ghostType)}");
|
|
animator.SetFloat("GhostType", GhostTypeToAnimationValue(ghostType));
|
|
break;
|
|
case PacManGhostState.Returning:
|
|
case PacManGhostState.Entering:
|
|
animator.SetFloat("GhostType", GhostTypeToAnimationValue(PacManGhostType.Caught));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (faceInStartingDirectionUntilUnfrozen && startState == PacManGhostStartState.TargetingIdlePosition1)
|
|
{
|
|
animator.SetFloat("DirX", 0);
|
|
animator.SetFloat("DirY", 1);
|
|
}
|
|
else if (faceInStartingDirectionUntilUnfrozen && startState == PacManGhostStartState.TargetingIdlePosition2)
|
|
{
|
|
animator.SetFloat("DirX", 0);
|
|
animator.SetFloat("DirY", -1);
|
|
}
|
|
else if (specialLook || !direction.Equals(Vector2.zero))
|
|
{
|
|
animator.SetFloat("DirX", direction.x);
|
|
animator.SetFloat("DirY", direction.y);
|
|
}
|
|
}
|
|
|
|
private float GhostTypeToAnimationValue(PacManGhostType ghostType)
|
|
{
|
|
switch (ghostType)
|
|
{
|
|
default:
|
|
Debug.LogError("Invalid ghost animation value!");
|
|
return 0;
|
|
case PacManGhostType.Caught:
|
|
return 0;
|
|
case PacManGhostType.Scared:
|
|
return 1;
|
|
case PacManGhostType.ScaredWhite:
|
|
return 2;
|
|
case PacManGhostType.Blinky:
|
|
return 3;
|
|
case PacManGhostType.Pinky:
|
|
return 4;
|
|
case PacManGhostType.Inky:
|
|
return 5;
|
|
case PacManGhostType.Clyde:
|
|
return 6;
|
|
case PacManGhostType.Special:
|
|
return 7;
|
|
}
|
|
}
|
|
|
|
void UpdateSpeed()
|
|
{
|
|
speed = ghostManager.GetTargetSpeed(this, ghostState, isScared, inTunnel);
|
|
Debug.Log($"Ghost updated speed to {speed}, level: {ghostManager.elroyLevel}");
|
|
}
|
|
|
|
public void ResetHousePelletCounter()
|
|
{
|
|
housePelletCounter = 0;
|
|
}
|
|
|
|
public void IncrementHousePelletCounter()
|
|
{
|
|
housePelletCounter++;
|
|
TryToExit();
|
|
}
|
|
|
|
void TryToExit()
|
|
{
|
|
if ((housePelletCounterActive && housePelletCounter >= housePelletCounterLimit) || startState == PacManGhostStartState.Outside)
|
|
{
|
|
Release();
|
|
}
|
|
}
|
|
|
|
public void Caught(int scoreBonus)
|
|
{
|
|
isScared = false;
|
|
// direction = direction * -1;
|
|
target = homePosition;
|
|
scoreBonusDisplay.Display(scoreBonus);
|
|
SetVisibility(false);
|
|
SetState(PacManGhostState.CaughtScore);
|
|
}
|
|
|
|
public void ReturnHome()
|
|
{
|
|
SetVisibility(true);
|
|
scoreBonusDisplay.Hide();
|
|
SetState(PacManGhostState.Returning);
|
|
}
|
|
|
|
public void BecomeScared()
|
|
{
|
|
if (ghostState == PacManGhostState.Returning || ghostState == PacManGhostState.Entering)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ghostState == PacManGhostState.Normal || ghostState == PacManGhostState.Home || ghostState == PacManGhostState.Exiting)
|
|
{
|
|
turnAroundSoon = true;
|
|
}
|
|
|
|
SetScared(true);
|
|
}
|
|
|
|
public void CalmDown()
|
|
{
|
|
if (isScared)
|
|
{
|
|
SetScared(false);
|
|
}
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
// Debug.Log($"{gameObject} was released.");
|
|
if (ghostState == PacManGhostState.Home)
|
|
{
|
|
// Debug.Log($"{gameObject} released.");
|
|
SetOffGridTarget(homePosition, true);
|
|
SetState(PacManGhostState.Exiting);
|
|
}
|
|
}
|
|
|
|
public void SetState(PacManGhostState state)
|
|
{
|
|
ghostState = state;
|
|
UpdateAnimator();
|
|
UpdateSpeed();
|
|
if (Networking.IsOwner(gameObject))
|
|
{
|
|
RequestSerialization();
|
|
}
|
|
}
|
|
|
|
private void SetScared(bool scared)
|
|
{
|
|
isScared = scared;
|
|
UpdateAnimator();
|
|
UpdateSpeed();
|
|
|
|
if (isScared)
|
|
{
|
|
SetWhite(false);
|
|
}
|
|
|
|
if (Networking.IsOwner(gameObject))
|
|
{
|
|
RequestSerialization();
|
|
}
|
|
}
|
|
|
|
public void SetScattering(bool scattering, bool reverseDirection = true)
|
|
{
|
|
if (reverseDirection && this.scattering != scattering)
|
|
{
|
|
if (ghostState == PacManGhostState.Normal || ghostState == PacManGhostState.Home || ghostState == PacManGhostState.Exiting
|
|
// This is afaik not normal PacMan behaviour, but is needed to accomidate slight timing differences
|
|
|| ghostState == PacManGhostState.Entering && ghostManager.gameController.GameState == PacManGameState.AttractModeDemo
|
|
)
|
|
{
|
|
turnAroundSoon = true;
|
|
}
|
|
}
|
|
this.scattering = scattering;
|
|
UpdateAnimator();
|
|
RequestSerialization();
|
|
}
|
|
|
|
public void SetFrozen(bool frozen, bool ignoreIfCaught = false, bool keepAnimating = false)
|
|
{
|
|
if (frozen && !ignoreIfCaught)
|
|
{
|
|
frozenState = PacManGhostFrozenState.Frozen;
|
|
}
|
|
else if (frozen && ignoreIfCaught)
|
|
{
|
|
frozenState = PacManGhostFrozenState.FrozenIfNotCaught;
|
|
}
|
|
else
|
|
{
|
|
frozenState = PacManGhostFrozenState.NotFrozen;
|
|
}
|
|
animator.speed = frozen && !keepAnimating ? 0 : 1; // This would cause issues if the returning sprite was animated, luckily it isn't :)
|
|
|
|
if (frozen == false)
|
|
{
|
|
|
|
if (faceInStartingDirectionUntilUnfrozen)
|
|
{
|
|
faceInStartingDirectionUntilUnfrozen = false;
|
|
UpdateAnimator();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetHousePelletCounterActive(bool active)
|
|
{
|
|
housePelletCounterActive = active;
|
|
}
|
|
|
|
public void SetHousePelletCounterLimit(int limit)
|
|
{
|
|
housePelletCounterLimit = limit;
|
|
}
|
|
|
|
public void SetWhite(bool white)
|
|
{
|
|
if (!isScared || !gameObject.activeInHierarchy)
|
|
return;
|
|
|
|
if (white)
|
|
{
|
|
animator.SetFloat("GhostType", 2);
|
|
}
|
|
else
|
|
{
|
|
animator.SetFloat("GhostType", 1);
|
|
}
|
|
}
|
|
|
|
public void SetElroy(int elroyLevel)
|
|
{
|
|
// Debug.Log($"{gameObject} became Elroy level {elroyLevel}");
|
|
if (elroyLevel > 0)
|
|
{
|
|
SetScattering(false, reverseDirection: false);
|
|
}
|
|
UpdateSpeed();
|
|
}
|
|
|
|
public void SetActive(bool active)
|
|
{
|
|
gameObject.SetActive(active);
|
|
renderer.enabled = active;
|
|
if (active)
|
|
{
|
|
UpdateAnimator();
|
|
}
|
|
}
|
|
|
|
public void SetKinematic(bool kinematic)
|
|
{
|
|
this.kinematic = kinematic;
|
|
}
|
|
|
|
public void SetPredefinedPath(Vector2[] predefinedPath)
|
|
{
|
|
this.predefinedPath = predefinedPath;
|
|
followingPredefinedPath = true;
|
|
predefinedPathIndex = 0;
|
|
}
|
|
|
|
public void SetInTunnel(bool inTunnel)
|
|
{
|
|
this.inTunnel = inTunnel;
|
|
UpdateSpeed();
|
|
}
|
|
|
|
public void SetSpecialLook(bool enabled)
|
|
{
|
|
specialLook = enabled;
|
|
UpdateAnimator();
|
|
}
|
|
|
|
void SetVisibility(bool visible)
|
|
{
|
|
renderer.enabled = visible;
|
|
}
|
|
|
|
public PacManGhostState GetGhostState()
|
|
{
|
|
return ghostState;
|
|
}
|
|
|
|
public bool IsScared => isScared;
|
|
|
|
public override Vector2 GetPosition()
|
|
{
|
|
return (Vector2)transform.localPosition;
|
|
}
|
|
|
|
public override void SetPosition(Vector2 position)
|
|
{
|
|
GridMoverTools.SetPosition(position, transform);
|
|
}
|
|
|
|
public override Vector2 GetDirection()
|
|
{
|
|
return direction;
|
|
}
|
|
|
|
public void SetDirection(Vector2 direction)
|
|
{
|
|
this.direction = direction;
|
|
UpdateAnimator();
|
|
RequestSerialization();
|
|
}
|
|
|
|
public void SetSpeed(float speed)
|
|
{
|
|
this.speed = speed;
|
|
UpdateAnimator();
|
|
}
|
|
|
|
public override void OnPreSerialization()
|
|
{
|
|
syncedPosition = GetPosition();
|
|
}
|
|
|
|
public override void OnDeserialization()
|
|
{
|
|
SetPosition(syncedPosition);
|
|
UpdateAnimator();
|
|
}
|
|
|
|
void OnTriggerEnter(Collider other)
|
|
{
|
|
if (Networking.IsOwner(gameObject) && other.gameObject.GetComponent<PacManGhostCollider>())
|
|
{
|
|
if (isScared)
|
|
{
|
|
// Debug.Log($"{gameObject} was cought!");
|
|
ghostManager.GhostCaughtQueue(this);
|
|
}
|
|
else if (ghostState == PacManGhostState.Normal)
|
|
{
|
|
// Debug.Log($"{gameObject} cought PacMan!");
|
|
ghostManager.CapturedPacMan();
|
|
}
|
|
}
|
|
else if (other.gameObject.GetComponent<GhostHorizontalOnlyIndicator>())
|
|
{
|
|
horizontalOnly = true;
|
|
}
|
|
else if (other.gameObject.GetComponent<GhostTunnelIndicator>())
|
|
{
|
|
SetInTunnel(true);
|
|
}
|
|
}
|
|
|
|
void OnTriggerExit(Collider other)
|
|
{
|
|
if (other.gameObject.GetComponent<GhostHorizontalOnlyIndicator>())
|
|
{
|
|
horizontalOnly = false;
|
|
}
|
|
else if (other.gameObject.GetComponent<GhostTunnelIndicator>())
|
|
{
|
|
SetInTunnel(false);
|
|
}
|
|
}
|
|
|
|
PacManGhostState State
|
|
{
|
|
set
|
|
{
|
|
SetState(value);
|
|
}
|
|
get => ghostState;
|
|
}
|
|
}
|
|
} |