first commit
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using NitroxClient.Communication.Abstract;
|
||||
using NitroxClient.GameLogic.Bases;
|
||||
using NitroxClient.GameLogic.Spawning.Bases;
|
||||
using NitroxClient.MonoBehaviours;
|
||||
using NitroxModel.DataStructures;
|
||||
using NitroxModel.DataStructures.GameLogic.Bases;
|
||||
using NitroxModel.DataStructures.GameLogic.Entities.Bases;
|
||||
using NitroxModel.Helper;
|
||||
using NitroxModel.Packets;
|
||||
using NitroxPatcher.PatternMatching;
|
||||
using UnityEngine;
|
||||
using static System.Reflection.Emit.OpCodes;
|
||||
using static NitroxClient.GameLogic.Bases.BuildingHandler;
|
||||
|
||||
namespace NitroxPatcher.Patches.Dynamic;
|
||||
|
||||
public sealed partial class BaseDeconstructable_Deconstruct_Patch : NitroxPatch, IDynamicPatch
|
||||
{
|
||||
public static readonly MethodInfo TARGET_METHOD = Reflect.Method((BaseDeconstructable t) => t.Deconstruct());
|
||||
|
||||
private static TemporaryBuildData Temp => BuildingHandler.Main.Temp;
|
||||
private static BuildPieceIdentifier cachedPieceIdentifier;
|
||||
|
||||
public static readonly InstructionsPattern BaseDeconstructInstructionPattern1 = new()
|
||||
{
|
||||
Callvirt,
|
||||
Call,
|
||||
Ldloc_3,
|
||||
{ new() { OpCode = Callvirt, Operand = new(nameof(BaseGhost), nameof(BaseGhost.ClearTargetBase)) }, "Insert1" }
|
||||
};
|
||||
public static readonly InstructionsPattern BaseDeconstructInstructionPattern2 = new()
|
||||
{
|
||||
Ldloc_0,
|
||||
new() { OpCode = Callvirt, Operand = new(nameof(Base), nameof(Base.FixCorridorLinks)) },
|
||||
Ldloc_0,
|
||||
{ new() { OpCode = Callvirt, Operand = new(nameof(Base), nameof(Base.RebuildGeometry)) }, "Insert2" },
|
||||
};
|
||||
|
||||
public static IEnumerable<CodeInstruction> InstructionsToAdd(bool destroyed)
|
||||
{
|
||||
yield return new(Ldarg_0);
|
||||
yield return new(Ldloc_2);
|
||||
yield return new(Ldloc_0);
|
||||
yield return new(destroyed ? Ldc_I4_1 : Ldc_I4_0);
|
||||
yield return new(Call, Reflect.Method(() => PieceDeconstructed(default, default, default, default)));
|
||||
}
|
||||
|
||||
public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions) =>
|
||||
instructions.Transform(BaseDeconstructInstructionPattern1, (label, instruction) =>
|
||||
{
|
||||
if (label.Equals("Insert1"))
|
||||
{
|
||||
return InstructionsToAdd(true);
|
||||
}
|
||||
return null;
|
||||
}).Transform(BaseDeconstructInstructionPattern2, (label, instruction) =>
|
||||
{
|
||||
if (label.Equals("Insert2"))
|
||||
{
|
||||
return InstructionsToAdd(false);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
public static void Prefix(BaseDeconstructable __instance)
|
||||
{
|
||||
BuildUtils.TryGetIdentifier(__instance, out cachedPieceIdentifier, null, __instance.face);
|
||||
}
|
||||
|
||||
public static void PieceDeconstructed(BaseDeconstructable baseDeconstructable, ConstructableBase constructableBase, Base @base, bool destroyed)
|
||||
{
|
||||
if (!@base.TryGetNitroxId(out NitroxId baseId))
|
||||
{
|
||||
Log.Error("Couldn't find NitroxEntity on a deconstructed base, which is really problematic");
|
||||
return;
|
||||
}
|
||||
|
||||
GhostEntity ghostEntity = GhostEntitySpawner.From(constructableBase);
|
||||
ghostEntity.Id = baseId;
|
||||
if (destroyed)
|
||||
{
|
||||
// Base was destroyed and replaced with a simple ghost
|
||||
Log.Verbose("Transferring id from base to the new ghost");
|
||||
NitroxEntity.SetNewId(constructableBase.gameObject, baseId);
|
||||
|
||||
Log.Verbose("Base destroyed and replaced by a simple ghost");
|
||||
Resolve<IPacketSender>().Send(new BaseDeconstructed(baseId, ghostEntity));
|
||||
return;
|
||||
}
|
||||
if (!baseDeconstructable.GetComponentInParent<BaseCell>())
|
||||
{
|
||||
Log.Error("Couldn't find a BaseCell parent to the BaseDeconstructable");
|
||||
return;
|
||||
}
|
||||
|
||||
// If deconstruction was ordered by BuildingHandler, then we simply take the provided id
|
||||
if (Temp.Id != null)
|
||||
{
|
||||
// If it had an attached module, we'll also delete the NitroxEntity from the said module similarly to the code below
|
||||
if (NitroxEntity.TryGetObjectFrom(Temp.Id, out GameObject moduleObject) &&
|
||||
moduleObject.TryGetComponent(out IBaseModule baseModule) &&
|
||||
constructableBase.moduleFace.HasValue && constructableBase.moduleFace.Value.Equals(baseModule.moduleFace))
|
||||
{
|
||||
Object.Destroy(moduleObject.GetComponent<NitroxEntity>());
|
||||
}
|
||||
else if (constructableBase.techType.Equals(TechType.BaseMoonpool) && @base.TryGetComponent(out MoonpoolManager moonpoolManager))
|
||||
{
|
||||
moonpoolManager.DeregisterMoonpool(constructableBase.transform);
|
||||
}
|
||||
|
||||
NitroxEntity.SetNewId(constructableBase.gameObject, Temp.Id);
|
||||
// We don't need to go any further
|
||||
return;
|
||||
}
|
||||
|
||||
NitroxId pieceId = null;
|
||||
// If the destructed piece has an attached module, we'll transfer the NitroxEntity from it
|
||||
if (constructableBase.moduleFace.HasValue)
|
||||
{
|
||||
Base.Face moduleFace = constructableBase.moduleFace.Value;
|
||||
moduleFace.cell += @base.GetAnchor();
|
||||
Component geometryObject = @base.GetModule(moduleFace).AliveOrNull();
|
||||
if (geometryObject && geometryObject.TryGetNitroxEntity(out NitroxEntity moduleEntity))
|
||||
{
|
||||
pieceId = moduleEntity.Id;
|
||||
Object.Destroy(moduleEntity);
|
||||
Log.Verbose($"Successfully transferred NitroxEntity from module geometry {moduleEntity.Id}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (constructableBase.techType)
|
||||
{
|
||||
case TechType.BaseMoonpool:
|
||||
if (@base.TryGetComponent(out MoonpoolManager moonpoolManager))
|
||||
{
|
||||
pieceId = moonpoolManager.DeregisterMoonpool(constructableBase.transform); // pieceId can still be null
|
||||
}
|
||||
break;
|
||||
case TechType.BaseMapRoom:
|
||||
Int3 mapRoomFunctionalityCell = BuildUtils.GetMapRoomFunctionalityCell(constructableBase.model.GetComponent<BaseGhost>());
|
||||
MapRoomFunctionality mapRoomFunctionality = @base.GetMapRoomFunctionalityForCell(mapRoomFunctionalityCell);
|
||||
if (mapRoomFunctionality && mapRoomFunctionality.TryGetNitroxId(out NitroxId mapRoomId))
|
||||
{
|
||||
pieceId = mapRoomId;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Either couldn't find a MapRoomFunctionality associated with destroyed piece or couldn't find a NitroxEntity onto it.");
|
||||
}
|
||||
break;
|
||||
case TechType.BaseWaterPark:
|
||||
// When a BaseWaterPark doesn't have a moduleFace, it means that there's still another WaterPark so we don't need to destroy its id and it won't be an error
|
||||
break;
|
||||
default:
|
||||
if (baseDeconstructable.GetComponent<IBaseModuleGeometry>() != null)
|
||||
{
|
||||
Log.Error("Couldn't find the module's GameObject of IBaseModuleGeometry when transferring the NitroxEntity");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Else, if it's a local client deconstruction, we generate a new one
|
||||
pieceId ??= new();
|
||||
NitroxEntity.SetNewId(constructableBase.gameObject, pieceId);
|
||||
ghostEntity.Id = pieceId;
|
||||
ghostEntity.ParentId = baseId;
|
||||
|
||||
if (cachedPieceIdentifier == default)
|
||||
{
|
||||
BuildingHandler.Main.EnsureTracker(baseId).FailedOperations++;
|
||||
Log.Error($"[{nameof(PieceDeconstructed)}] Couldn't find a CachedPieceIdentifier for deconstructed object {constructableBase.gameObject}");
|
||||
return;
|
||||
}
|
||||
|
||||
BuildingHandler.Main.EnsureTracker(baseId).LocalOperations++;
|
||||
int operationId = BuildingHandler.Main.GetCurrentOperationIdOrDefault(baseId);
|
||||
|
||||
PieceDeconstructed pieceDeconstructed;
|
||||
if (Temp.MovedChildrenIdsByNewHostId != null)
|
||||
{
|
||||
pieceDeconstructed = new LargeWaterParkDeconstructed(baseId, pieceId, cachedPieceIdentifier, ghostEntity, BuildEntitySpawner.GetBaseData(@base), Temp.MovedChildrenIdsByNewHostId, operationId);
|
||||
}
|
||||
else
|
||||
{
|
||||
pieceDeconstructed = Temp.NewWaterPark == null ?
|
||||
new PieceDeconstructed(baseId, pieceId, cachedPieceIdentifier, ghostEntity, BuildEntitySpawner.GetBaseData(@base), operationId) :
|
||||
new WaterParkDeconstructed(baseId, pieceId, cachedPieceIdentifier, ghostEntity, BuildEntitySpawner.GetBaseData(@base), Temp.NewWaterPark, Temp.MovedChildrenIds, Temp.Transfer, operationId);
|
||||
}
|
||||
|
||||
Log.Verbose($"Base is not empty, sending packet {pieceDeconstructed}");
|
||||
|
||||
Resolve<IPacketSender>().Send(pieceDeconstructed);
|
||||
|
||||
Temp.Dispose();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user