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

179 lines
6.1 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NitroxClient.GameLogic;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.GameLogic.Entities.Bases;
using NitroxModel.DataStructures.Util;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
namespace NitroxClient.MonoBehaviours;
/// <summary>
/// Saves the moonpools NitroxEntity of a <see cref="Base"/> to assign them back after each <see cref="Base.RebuildGeometry"/>.
/// </summary>
/// <remarks>
/// To recognize pieces even after the base is rebuilt, we use the base anchor (<see cref="Base.anchor"/>) to get an absolute cell value.
/// </remarks>
public class MoonpoolManager : MonoBehaviour
{
private Entities entities;
private Base @base;
private NitroxId baseId;
private Dictionary<Int3, MoonpoolEntity> moonpoolsByCell;
public MoonpoolEntity LatestRegisteredMoonpool { get; private set; }
public void Awake()
{
entities = this.Resolve<Entities>();
if (!TryGetComponent(out @base))
{
Log.Error($"Tried adding a {nameof(MoonpoolManager)} to a GameObject that isn't a bases, deleting it.");
Destroy(this);
return;
}
@base.TryGetNitroxId(out baseId);
moonpoolsByCell = new();
@base.onPostRebuildGeometry += OnPostRebuildGeometry;
}
public void OnDestroy()
{
@base.onPostRebuildGeometry -= OnPostRebuildGeometry;
}
public void LateAssignNitroxEntity(NitroxId baseId)
{
this.baseId = baseId;
NitroxId nextId = baseId.Increment(); // To be recognizable, we need it to be deterministic
foreach (MoonpoolEntity moonpoolEntity in moonpoolsByCell.Values)
{
moonpoolEntity.ParentId = baseId;
moonpoolEntity.Id = nextId;
nextId = nextId.Increment();
}
}
public void OnPostRebuildGeometry(Base _)
{
foreach (KeyValuePair<Int3, MoonpoolEntity> moonpoolEntry in moonpoolsByCell)
{
AssignNitroxEntityToMoonpool(moonpoolEntry.Key, moonpoolEntry.Value.Id);
}
}
private void AssignNitroxEntityToMoonpool(Int3 absoluteCell, NitroxId moonpoolId, TaskResult<Optional<GameObject>> result = null)
{
Int3 relativeCell = Relative(absoluteCell);
Transform baseCellTransform = @base.GetCellObject(relativeCell);
if (!baseCellTransform)
{
Log.Warn($"[{nameof(AssignNitroxEntityToMoonpool)}] CellObject not found for RelativeCell: {relativeCell}, AbsoluteCell: {absoluteCell}");
return;
}
if (baseCellTransform.TryGetComponentInChildren(out VehicleDockingBay vehicleDockingBay, true))
{
result?.Set(vehicleDockingBay.gameObject);
NitroxEntity.SetNewId(vehicleDockingBay.gameObject, moonpoolId);
}
}
public Optional<GameObject> RegisterMoonpool(Transform constructableTransform, NitroxId moonpoolId)
{
return RegisterMoonpool(Absolute(constructableTransform.position), moonpoolId);
}
public Optional<GameObject> RegisterMoonpool(Int3 absoluteCell, NitroxId moonpoolId)
{
moonpoolsByCell[absoluteCell] = new(moonpoolId, baseId, absoluteCell.ToDto());
TaskResult<Optional<GameObject>> resultObject = new();
AssignNitroxEntityToMoonpool(absoluteCell, moonpoolId, resultObject);
LatestRegisteredMoonpool = moonpoolsByCell[absoluteCell];
return resultObject.Get();
}
public NitroxId DeregisterMoonpool(Transform constructableTransform)
{
Int3 absoluteCell = Absolute(constructableTransform.position);
if (moonpoolsByCell.TryGetValue(absoluteCell, out MoonpoolEntity moonpoolEntity))
{
moonpoolsByCell.Remove(absoluteCell);
return moonpoolEntity.Id;
}
return null;
}
public void LoadMoonpools(IEnumerable<MoonpoolEntity> moonpoolEntities)
{
moonpoolsByCell.Clear();
foreach (MoonpoolEntity moonpoolEntity in moonpoolEntities)
{
moonpoolsByCell[moonpoolEntity.Cell.ToUnity()] = moonpoolEntity;
}
}
public IEnumerator SpawnVehicles()
{
foreach (MoonpoolEntity moonpoolEntity in moonpoolsByCell.Values)
{
VehicleWorldEntity moonpoolVehicleEntity = moonpoolEntity.ChildEntities.OfType<VehicleWorldEntity>().FirstOrFallback(null);
if (moonpoolVehicleEntity != null)
{
yield return entities.SpawnEntityAsync(moonpoolVehicleEntity);
}
}
}
private Int3 Absolute(Vector3 position)
{
return Absolute(@base.WorldToGrid(position));
}
private Int3 Absolute(Int3 baseCell)
{
return baseCell - @base.GetAnchor();
}
private Int3 Relative(Int3 baseCell)
{
return baseCell + @base.GetAnchor();
}
public List<MoonpoolEntity> GetSavedMoonpools()
{
return moonpoolsByCell.Values.ToList();
}
public Dictionary<NitroxId, NitroxInt3> GetMoonpoolsUpdate()
{
return moonpoolsByCell.ToDictionary(entry => entry.Value.Id, entry => entry.Key.ToDto());
}
[Conditional("DEBUG")]
public void PrintDebug()
{
Log.Debug($"MoonpoolManager's registered moonpools (anchor: {@base.GetAnchor()}):");
foreach (MoonpoolEntity moonpoolEntity in moonpoolsByCell.Values)
{
Int3 absoluteCell = moonpoolEntity.Cell.ToUnity();
Int3 baseCell = Relative(moonpoolEntity.Cell.ToUnity());
Log.Debug($"AbsoluteCell: {absoluteCell}, BaseCell: {baseCell}, id: {moonpoolEntity.Id}");
}
}
public static IEnumerator RestoreMoonpools(IEnumerable<MoonpoolEntity> moonpoolEntities, Base @base)
{
MoonpoolManager moonpoolManager = @base.gameObject.EnsureComponent<MoonpoolManager>();
moonpoolManager.LoadMoonpools(moonpoolEntities);
moonpoolManager.OnPostRebuildGeometry(@base);
yield return moonpoolManager.SpawnVehicles();
}
}