175 lines
7.3 KiB
C#
175 lines
7.3 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using NitroxClient.Communication;
|
|
using NitroxClient.Communication.Abstract;
|
|
using NitroxClient.GameLogic.Helper;
|
|
using NitroxClient.GameLogic.Spawning.Metadata;
|
|
using NitroxClient.GameLogic.Spawning.Metadata.Extractor;
|
|
using NitroxClient.MonoBehaviours;
|
|
using NitroxClient.Unity.Helper;
|
|
using NitroxModel.DataStructures;
|
|
using NitroxModel.DataStructures.GameLogic.Entities;
|
|
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
|
|
using NitroxModel.DataStructures.Util;
|
|
using NitroxModel.Packets;
|
|
using NitroxModel_Subnautica.DataStructures;
|
|
using UnityEngine;
|
|
|
|
namespace NitroxClient.GameLogic;
|
|
|
|
public class Vehicles
|
|
{
|
|
private readonly IPacketSender packetSender;
|
|
private readonly IMultiplayerSession multiplayerSession;
|
|
private readonly PlayerManager playerManager;
|
|
private readonly EntityMetadataManager entityMetadataManager;
|
|
private readonly Entities entities;
|
|
|
|
private readonly Dictionary<TechType, string> pilotingChairByTechType = [];
|
|
|
|
public Vehicles(IPacketSender packetSender, IMultiplayerSession multiplayerSession, PlayerManager playerManager, EntityMetadataManager entityMetadataManager, Entities entities)
|
|
{
|
|
this.packetSender = packetSender;
|
|
this.multiplayerSession = multiplayerSession;
|
|
this.playerManager = playerManager;
|
|
this.entityMetadataManager = entityMetadataManager;
|
|
this.entities = entities;
|
|
}
|
|
|
|
private PilotingChair FindPilotingChairWithCache(GameObject parent, TechType techType)
|
|
{
|
|
if (!parent)
|
|
{
|
|
return null;
|
|
}
|
|
if (pilotingChairByTechType.TryGetValue(techType, out string path))
|
|
{
|
|
if (path == string.Empty)
|
|
{
|
|
return null;
|
|
}
|
|
return parent.transform.Find(path).GetComponent<PilotingChair>();
|
|
}
|
|
else
|
|
{
|
|
PilotingChair chair = parent.GetComponentInChildren<PilotingChair>(true);
|
|
pilotingChairByTechType.Add(techType, chair ? chair.gameObject.GetHierarchyPath(parent) : string.Empty);
|
|
return chair;
|
|
}
|
|
}
|
|
|
|
public void BroadcastDestroyedVehicle(NitroxId id)
|
|
{
|
|
using (PacketSuppressor<VehicleOnPilotModeChanged>.Suppress())
|
|
{
|
|
EntityDestroyed entityDestroyed = new(id);
|
|
packetSender.Send(entityDestroyed);
|
|
}
|
|
}
|
|
|
|
public void BroadcastDestroyedCyclops(GameObject cyclops, NitroxId id)
|
|
{
|
|
CyclopsMetadataExtractor.CyclopsGameObject cyclopsGameObject = new() { GameObject = cyclops };
|
|
Optional<EntityMetadata> metadata = entityMetadataManager.Extract(cyclopsGameObject);
|
|
|
|
if (metadata.HasValue && metadata.Value is CyclopsMetadata cyclopsMetadata)
|
|
{
|
|
cyclopsMetadata.IsDestroyed = true;
|
|
entities.BroadcastMetadataUpdate(id, cyclopsMetadata);
|
|
}
|
|
}
|
|
|
|
public static void EngagePlayerMovementSuppressor(Vehicle vehicle)
|
|
{
|
|
// TODO: Properly prevent the vehicle from sending position update as long as it's not free from the animation
|
|
PacketSuppressor<PlayerMovement> playerMovementSuppressor = PacketSuppressor<PlayerMovement>.Suppress();
|
|
vehicle.StartCoroutine(AllowMovementPacketsAfterDockingAnimation());
|
|
return;
|
|
|
|
/*
|
|
A poorly timed movement packet will cause major problems when docking because the remote
|
|
player will think that the player is no longer in a vehicle. Unfortunately, the game calls
|
|
the vehicle exit code before the animation completes so we need to suppress any side effects.
|
|
Two thing we want to protect against:
|
|
|
|
1) If a movement packet is received when docking, the player might exit the vehicle early,
|
|
and it will show them sitting outside the vehicle during the docking animation.
|
|
|
|
2) If a movement packet is received when undocking, the player game object will be stuck in
|
|
place until after the player exits the vehicle. This causes the player body to stretch to
|
|
the current cyclops position.
|
|
*/
|
|
IEnumerator AllowMovementPacketsAfterDockingAnimation()
|
|
{
|
|
yield return Yielders.WaitFor3Seconds;
|
|
playerMovementSuppressor.Dispose();
|
|
}
|
|
}
|
|
|
|
public void BroadcastOnPilotModeChanged(GameObject gameObject, bool isPiloting)
|
|
{
|
|
if (gameObject.TryGetIdOrWarn(out NitroxId vehicleId))
|
|
{
|
|
VehicleOnPilotModeChanged packet = new(vehicleId, multiplayerSession.Reservation.PlayerId, isPiloting);
|
|
packetSender.Send(packet);
|
|
}
|
|
}
|
|
|
|
public void SetOnPilotMode(GameObject gameObject, ushort playerId, bool isPiloting)
|
|
{
|
|
if (playerManager.TryFind(playerId, out RemotePlayer remotePlayer))
|
|
{
|
|
if (gameObject.TryGetComponent(out Vehicle vehicle))
|
|
{
|
|
remotePlayer.SetVehicle(isPiloting ? vehicle : null);
|
|
}
|
|
else if (gameObject.GetComponent<SubRoot>())
|
|
{
|
|
if (!isPiloting)
|
|
{
|
|
remotePlayer.SetPilotingChair(null);
|
|
return;
|
|
}
|
|
PilotingChair pilotingChair = FindPilotingChairWithCache(gameObject, TechType.Cyclops);
|
|
remotePlayer.SetPilotingChair(pilotingChair);
|
|
}
|
|
// TODO: [FUTURE] For any mods adding new vehicle with a piloting chair, there should be something done right here
|
|
}
|
|
}
|
|
|
|
public void SetOnPilotMode(NitroxId vehicleId, ushort playerId, bool isPiloting)
|
|
{
|
|
if (NitroxEntity.TryGetObjectFrom(vehicleId, out GameObject vehicleObject))
|
|
{
|
|
SetOnPilotMode(vehicleObject, playerId, isPiloting);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes ALL <see cref="NitroxEntity"/> on the <see cref="GameObject"/> and its children.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Subnautica pre-emptively loads a prefab of each vehicle (such as a cyclops) during the initial game load. This allows the game to instantaniously
|
|
/// use this prefab for the first constructor event. Subsequent constructor events will use this prefab as a template. However, this is problematic
|
|
/// because the template + children are now tagged with NitroxEntity because players are interacting with it. We need to remove any NitroxEntity from
|
|
/// the new gameObject that used the template.
|
|
/// </remarks>
|
|
public static void RemoveNitroxEntitiesTagging(GameObject constructedObject)
|
|
{
|
|
NitroxEntity[] nitroxEntities = constructedObject.GetComponentsInChildren<NitroxEntity>(true);
|
|
|
|
foreach (NitroxEntity nitroxEntity in nitroxEntities)
|
|
{
|
|
nitroxEntity.Remove();
|
|
Object.DestroyImmediate(nitroxEntity);
|
|
}
|
|
}
|
|
|
|
public static VehicleWorldEntity BuildVehicleWorldEntity(GameObject constructedObject, NitroxId constructedObjectId, TechType techType, NitroxId constructorId = null)
|
|
{
|
|
VehicleWorldEntity vehicleEntity = new(constructorId, DayNightCycle.main.timePassedAsFloat, constructedObject.transform.ToLocalDto(), string.Empty, false, constructedObjectId, techType.ToDto(), null);
|
|
VehicleChildEntityHelper.PopulateChildren(constructedObjectId, constructedObject.GetFullHierarchyPath(), vehicleEntity.ChildEntities, constructedObject);
|
|
return vehicleEntity;
|
|
}
|
|
}
|