Files
Nitrox/NitroxClient/GameLogic/CyclopsPawn.cs
2025-07-06 00:23:46 +02:00

153 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.MonoBehaviours;
using NitroxClient.MonoBehaviours.Cyclops;
using UnityEngine;
namespace NitroxClient.GameLogic;
/// <summary>
/// A virtual entity responsible for one player's movement in the cyclops.
/// It simulates the local player's movements by creating a fake player in a <see cref="VirtualCyclops"/>'s instance and then giving data about the real movement.
/// </summary>
public class CyclopsPawn
{
private static readonly List<CharacterController> controllers = [];
public static readonly int PLAYER_LAYER = 1 << LayerMask.NameToLayer("Player");
private readonly INitroxPlayer player;
private readonly NitroxCyclops cyclops;
private readonly Transform virtualTransform;
private readonly Transform realCyclopsTransform;
private readonly bool isLocalPlayer;
public readonly GameObject RealObject;
public GameObject Handle;
public CharacterController Controller;
public Func<bool> MaintainPredicate;
public Vector3 Position
{
get => Handle.transform.position;
set { Handle.transform.position = value; }
}
public CyclopsPawn(INitroxPlayer player, NitroxCyclops cyclops)
{
this.player = player;
this.cyclops = cyclops;
virtualTransform = VirtualCyclops.Instance.transform;
realCyclopsTransform = cyclops.transform;
if (player is ILocalNitroxPlayer)
{
isLocalPlayer = true;
RealObject = Player.mainObject;
CyclopsMotor cyclopsMotor = Player.mainObject.GetComponent<CyclopsMotor>();
MaintainPredicate = () => cyclopsMotor.canControl && !Player.main.isPiloting;
}
else if (player is RemotePlayer remotePlayer)
{
RealObject = remotePlayer.Body;
MaintainPredicate = () => !remotePlayer.PilotingChair;
}
Initialize($"{player.PlayerName}-Pawn", RealObject.transform.localPosition);
}
public void Initialize(string name, Vector3 localPosition)
{
PlayerController playerController = Player.main.GetComponent<PlayerController>();
GroundMotor groundMotor = Player.main.GetComponent<GroundMotor>();
CharacterController reference = Player.main.GetComponent<CharacterController>();
Handle = GameObject.CreatePrimitive(PrimitiveType.Capsule);
Handle.layer = 1 << PLAYER_LAYER;
Handle.name = name;
Handle.transform.parent = virtualTransform;
Handle.transform.localPosition = localPosition;
GameObject.DestroyImmediate(Handle.GetComponent<Collider>());
Controller = Handle.AddComponent<CharacterController>();
Controller.height = playerController.standheight - playerController.cameraOffset;
// Calculation from Groundmotor.SetControllerHeight
Vector3 center = groundMotor.colliderCenter;
center.y = -Controller.height * 0.5f - playerController.cameraOffset;
Controller.center = center;
Controller.radius = playerController.controllerRadius;
Controller.skinWidth = reference.skinWidth;
Controller.stepOffset = groundMotor.controller.stepOffset;
Controller.slopeLimit = groundMotor.controller.slopeLimit;
RegisterController();
Handle.AddComponent<CyclopsPawnIdentifier>().Pawn = this;
}
public void RegisterController()
{
foreach (CharacterController controller in controllers)
{
Physics.IgnoreCollision(controller, Controller);
}
controllers.Add(Controller);
}
public void SetReference()
{
Handle.transform.localPosition = RealObject.transform.localPosition;
if (!isLocalPlayer)
{
Handle.transform.localRotation = RealObject.transform.localRotation;
}
}
public void MaintainPosition()
{
RealObject.transform.localPosition = Handle.transform.localPosition;
RealObject.transform.rotation = realCyclopsTransform.rotation;
if (!isLocalPlayer)
{
RealObject.transform.localRotation = Handle.transform.localRotation;
}
}
public void Unregister()
{
if (cyclops)
{
if (isLocalPlayer)
{
cyclops.OnLocalPlayerExit();
}
else
{
cyclops.OnPlayerExit((RemotePlayer)player);
}
}
}
/// <summary>
/// Replicates openable being blocked only if the pawn causing the block is in the cyclops associated to the virtual one.
/// </summary>
public void BlockOpenable(Openable openable, bool blockState)
{
if (cyclops.Virtual)
{
openable.blocked = blockState;
cyclops.Virtual.ReplicateBlock(openable, blockState);
}
}
public void Terminate()
{
controllers.Remove(Controller);
GameObject.Destroy(Handle);
}
}
public class CyclopsPawnIdentifier : MonoBehaviour
{
public CyclopsPawn Pawn;
}