160 lines
6.2 KiB
C#
160 lines
6.2 KiB
C#
using System.Collections;
|
|
using System.Linq;
|
|
using NitroxClient.GameLogic.Bases;
|
|
using NitroxClient.GameLogic.Helper;
|
|
using NitroxClient.GameLogic.Spawning.Abstract;
|
|
using NitroxClient.GameLogic.Spawning.WorldEntities;
|
|
using NitroxClient.MonoBehaviours;
|
|
using NitroxClient.MonoBehaviours.Cyclops;
|
|
using NitroxClient.Unity.Helper;
|
|
using NitroxModel.DataStructures;
|
|
using NitroxModel.DataStructures.GameLogic;
|
|
using NitroxModel.DataStructures.GameLogic.Entities;
|
|
using NitroxModel.DataStructures.GameLogic.Entities.Bases;
|
|
using NitroxModel.DataStructures.Util;
|
|
using NitroxModel_Subnautica.DataStructures;
|
|
using UnityEngine;
|
|
|
|
namespace NitroxClient.GameLogic.Spawning.Bases;
|
|
|
|
public class ModuleEntitySpawner : EntitySpawner<ModuleEntity>
|
|
{
|
|
private readonly Entities entities;
|
|
|
|
public ModuleEntitySpawner(Entities entities)
|
|
{
|
|
this.entities = entities;
|
|
}
|
|
|
|
protected override IEnumerator SpawnAsync(ModuleEntity entity, TaskResult<Optional<GameObject>> result)
|
|
{
|
|
if (NitroxEntity.TryGetObjectFrom(entity.Id, out GameObject gameObject) && gameObject)
|
|
{
|
|
Log.Error("Trying to respawn an already spawned module without a proper resync process.");
|
|
yield break;
|
|
}
|
|
Transform parent = BuildingHandler.GetParentOrGlobalRoot(entity.ParentId);
|
|
|
|
yield return RestoreModule(parent, entity, result);
|
|
|
|
if (!result.Get().HasValue)
|
|
{
|
|
Log.Error($"Module couldn't be spawned {entity}");
|
|
yield break;
|
|
}
|
|
GameObject moduleObject = result.Get().Value;
|
|
|
|
Optional<ItemsContainer> opContainer = InventoryContainerHelper.TryGetContainerByOwner(moduleObject);
|
|
if (opContainer.HasValue)
|
|
{
|
|
yield return entities.SpawnBatchAsync(entity.ChildEntities.OfType<InventoryItemEntity>().ToList<Entity>(), true);
|
|
}
|
|
|
|
Optional<Equipment> opEquipment = EquipmentHelper.FindEquipmentComponent(moduleObject);
|
|
if (opEquipment.HasValue)
|
|
{
|
|
yield return entities.SpawnBatchAsync(entity.ChildEntities.OfType<InstalledModuleEntity>().ToList<Entity>(), true);
|
|
}
|
|
|
|
if (moduleObject.TryGetComponent(out PowerSource powerSource))
|
|
{
|
|
// TODO: Have synced/restored power
|
|
powerSource.SetPower(powerSource.maxPower);
|
|
}
|
|
}
|
|
|
|
protected override bool SpawnsOwnChildren(ModuleEntity entity) => true;
|
|
|
|
public static IEnumerator RestoreModule(Transform parent, ModuleEntity moduleEntity, TaskResult<Optional<GameObject>> result = null)
|
|
{
|
|
if (!DefaultWorldEntitySpawner.TryGetCachedPrefab(out GameObject prefab, classId: moduleEntity.ClassId))
|
|
{
|
|
TaskResult<GameObject> prefabResult = new();
|
|
yield return DefaultWorldEntitySpawner.RequestPrefab(moduleEntity.ClassId, prefabResult);
|
|
if (!prefabResult.Get())
|
|
{
|
|
Log.Error($"Couldn't find a prefab for module of ClassId {moduleEntity.ClassId}");
|
|
yield break;
|
|
}
|
|
prefab = prefabResult.Get();
|
|
}
|
|
|
|
GameObject moduleObject = UnityEngine.Object.Instantiate(prefab);
|
|
Transform moduleTransform = moduleObject.transform;
|
|
moduleTransform.parent = parent;
|
|
moduleTransform.localPosition = moduleEntity.Transform.LocalPosition.ToUnity();
|
|
moduleTransform.localRotation = moduleEntity.Transform.LocalRotation.ToUnity();
|
|
moduleTransform.localScale = moduleEntity.Transform.LocalScale.ToUnity();
|
|
ApplyModuleData(moduleEntity, moduleObject, result);
|
|
MoveToGlobalRoot(moduleObject);
|
|
|
|
if (parent && parent.TryGetComponent(out NitroxCyclops nitroxCyclops) && nitroxCyclops.Virtual)
|
|
{
|
|
nitroxCyclops.Virtual.ReplicateConstructable(moduleObject.GetComponent<Constructable>());
|
|
}
|
|
|
|
yield return BuildingPostSpawner.ApplyPostSpawner(moduleObject, moduleEntity.Id);
|
|
}
|
|
|
|
public static void ApplyModuleData(ModuleEntity moduleEntity, GameObject moduleObject, TaskResult<Optional<GameObject>> result = null)
|
|
{
|
|
Constructable constructable = moduleObject.GetComponent<Constructable>();
|
|
constructable.SetIsInside(moduleEntity.IsInside);
|
|
if (moduleEntity.IsInside)
|
|
{
|
|
SkyEnvironmentChanged.Send(moduleObject, moduleObject.GetComponentInParent<SubRoot>(true));
|
|
}
|
|
else
|
|
{
|
|
SkyEnvironmentChanged.Send(moduleObject, (Component)null);
|
|
}
|
|
constructable.constructedAmount = moduleEntity.ConstructedAmount;
|
|
constructable.SetState(moduleEntity.ConstructedAmount >= 1f, false);
|
|
constructable.UpdateMaterial();
|
|
NitroxEntity.SetNewId(moduleObject, moduleEntity.Id);
|
|
|
|
result?.Set(moduleObject);
|
|
}
|
|
|
|
public static void FillObject(ModuleEntity moduleEntity, Constructable constructable)
|
|
{
|
|
moduleEntity.ClassId = constructable.GetComponent<PrefabIdentifier>().ClassId;
|
|
|
|
if (constructable.TryGetNitroxId(out NitroxId entityId))
|
|
{
|
|
moduleEntity.Id = entityId;
|
|
}
|
|
if (constructable.TryGetComponentInParent(out Base parentBase, true) &&
|
|
parentBase.TryGetNitroxId(out NitroxId parentId))
|
|
{
|
|
moduleEntity.ParentId = parentId;
|
|
}
|
|
moduleEntity.Transform = constructable.transform.ToLocalDto();
|
|
moduleEntity.TechType = constructable.techType.ToDto();
|
|
moduleEntity.ConstructedAmount = constructable.constructedAmount;
|
|
moduleEntity.IsInside = constructable.isInside;
|
|
}
|
|
|
|
/// <summary>
|
|
/// We don't want constructables to be put in CellRoots but in GlobalRoot, because when a player has simulation ownership over a base,
|
|
/// they also need to keep loaded everything which could be related to the said base (e.g. power relays)
|
|
/// </summary>
|
|
public static void MoveToGlobalRoot(GameObject gameObject)
|
|
{
|
|
if (!gameObject.TryGetComponent(out LargeWorldEntity largeWorldEntity))
|
|
{
|
|
return;
|
|
}
|
|
|
|
largeWorldEntity.cellLevel = LargeWorldEntity.CellLevel.Global;
|
|
largeWorldEntity.Start();
|
|
}
|
|
|
|
public static ModuleEntity From(Constructable constructable)
|
|
{
|
|
ModuleEntity module = ModuleEntity.MakeEmpty();
|
|
FillObject(module, constructable);
|
|
return module;
|
|
}
|
|
}
|