first commit
This commit is contained in:
174
NitroxClient/GameLogic/Vehicles.cs
Normal file
174
NitroxClient/GameLogic/Vehicles.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user