public static bool Prefix(Incubator __instance, GUIHand hand) { if (skipPrefix) { return(true); } // Request a simulation lock on the incubator so that we can authoritatively spawn the resulting creatures if (__instance.powered && !__instance.hatched && Inventory.main.container.Contains(TechType.HatchingEnzymes)) { SimulationOwnership simulationOwnership = NitroxServiceLocator.LocateService <SimulationOwnership>(); // the server only knows about the the main incubator platform which is the direct parent GameObject platform = __instance.gameObject.transform.parent.gameObject; NitroxId id = NitroxEntity.GetId(platform); HandInteraction <Incubator> context = new HandInteraction <Incubator>(__instance, hand); LockRequest <HandInteraction <Incubator> > lockRequest = new LockRequest <HandInteraction <Incubator> >(id, SimulationLockType.EXCLUSIVE, ReceivedSimulationLockResponse, context); simulationOwnership.RequestSimulationLock(lockRequest); } return(false); }
public void BroadcastItemAdd(Pickupable pickupable, Transform containerTransform) { NitroxId itemId = NitroxEntity.GetId(pickupable.gameObject); byte[] bytes = SerializationHelper.GetBytes(pickupable.gameObject); NitroxId ownerId = GetOwner(containerTransform); ItemData itemData; Plantable plant = pickupable.GetComponent <Plantable>(); if (plant && plant.currentPlanter) { // special case: we want to remember the time when the plant was added, so we can simulate growth itemData = new PlantableItemData(ownerId, itemId, bytes, DayNightCycle.main.timePassedAsDouble); } else { itemData = new ItemData(ownerId, itemId, bytes); } if (packetSender.Send(new ItemContainerAdd(itemData))) { Log.Debug($"Sent: Added item {pickupable.GetTechType()} to container {containerTransform.gameObject.GetHierarchyPath()}"); } }
public NitroxId GetCyclopsLockerId(Transform ownerTransform) { string LockerId = ownerTransform.gameObject.name.Substring(7, 1); GameObject locker = ownerTransform.parent.gameObject.FindChild("submarine_locker_01_0" + LockerId); if (locker != null) { StorageContainer SC = locker.GetComponentInChildren <StorageContainer>(); if (SC != null) { return(NitroxEntity.GetId(SC.gameObject)); } else { throw new Exception("Could not find StorageContainer From Object: submarine_locker_01_0" + LockerId); } } else { throw new Exception("Could not find Locker Object: submarine_locker_01_0" + LockerId); } }
public void BroadcastVehicleUndocking(VehicleDockingBay dockingBay, Vehicle vehicle) { NitroxId dockId; if (dockingBay.GetSubRoot() is BaseRoot) { dockId = NitroxEntity.GetId(dockingBay.GetComponentInParent <BaseRoot>().gameObject); } else if (dockingBay.GetSubRoot() is SubRoot) { dockId = NitroxEntity.GetId(dockingBay.GetSubRoot().gameObject); } else { dockId = NitroxEntity.GetId(dockingBay.GetComponentInParent <ConstructableBase>().gameObject); } NitroxId vehicleId = NitroxEntity.GetId(vehicle.gameObject); ushort playerId = multiplayerSession.Reservation.PlayerId; VehicleUndocking packet = new VehicleUndocking(vehicleId, dockId, playerId); packetSender.Send(packet); }
public static bool Prefix(Bench __instance, GUIHand hand) { if (skipPrefix) { return(true); } SimulationOwnership simulationOwnership = NitroxServiceLocator.LocateService <SimulationOwnership>(); NitroxId id = NitroxEntity.GetId(__instance.gameObject); if (simulationOwnership.HasExclusiveLock(id)) { Log.Debug($"Already have an exclusive lock on the bench/chair: {id}"); return(true); } HandInteraction <Bench> context = new HandInteraction <Bench>(__instance, hand); LockRequest <HandInteraction <Bench> > lockRequest = new LockRequest <HandInteraction <Bench> >(id, SimulationLockType.EXCLUSIVE, ReceivedSimulationLockResponse, context); simulationOwnership.RequestSimulationLock(lockRequest); return(false); }
public static bool Prefix(PropulsionCannon __instance, GameObject target) { if (skipPrefixPatch) { return(true); } SimulationOwnership simulationOwnership = NitroxServiceLocator.LocateService <SimulationOwnership>(); NitroxId id = NitroxEntity.GetId(target); if (simulationOwnership.HasExclusiveLock(id)) { Log.Debug($"Already have an exclusive lock on the grabbed propulsion cannon object: {id}"); return(true); } PropulsionGrab context = new(__instance, target); LockRequest <PropulsionGrab> lockRequest = new(id, SimulationLockType.EXCLUSIVE, ReceivedSimulationLockResponse, context); simulationOwnership.RequestSimulationLock(lockRequest); return(false); }
public static bool Prefix(Vehicle __instance, GUIHand hand) { if (skipPrefix) { return(true); } vehicle = __instance; guiHand = hand; SimulationOwnership simulationOwnership = NitroxServiceLocator.LocateService <SimulationOwnership>(); NitroxId id = NitroxEntity.GetId(__instance.gameObject); if (simulationOwnership.HasExclusiveLock(id)) { Log.Debug($"Already have an exclusive lock on the vehicle: {id}"); return(true); } simulationOwnership.RequestSimulationLock(id, SimulationLockType.EXCLUSIVE, ReceivedSimulationLockResponse); return(false); }
public static float AddHealthOverride(LiveMixin live, float addHealth, Welder welder) { float result = 0f; if ((live.IsAlive() || live.canResurrect) && live.health < live.maxHealth) { float num = live.health; float newHealth = Math.Min(live.health + addHealth, live.maxHealth); result = newHealth - num; SimulationOwnership simulationOwnership = NitroxServiceLocator.LocateService <SimulationOwnership>(); NitroxId id = NitroxEntity.GetId(live.gameObject); // For now, we only control the LiveMixin for vehicles (not even repair nodes at a cyclops) // If we change that, this if should be removed! Vehicle vehicle = live.GetComponent <Vehicle>(); if (vehicle) { if (simulationOwnership.HasAnyLockType(id)) { result = live.AddHealth(addHealth); } else { // Another player simulates this entity. Send the weld info Log.Debug($"Broadcast weld action for {id}"); NitroxServiceLocator.LocateService <LocalPlayer>().BroadcastWeld(id, addHealth); } } else { result = live.AddHealth(addHealth); } } return(result); }
public static bool Prefix(CyclopsExternalDamageManager __instance, out bool __state) { // Block from creating points if they aren't the owner of the sub __state = NitroxServiceLocator.LocateService <SimulationOwnership>().HasAnyLockType(NitroxEntity.GetId(__instance.subRoot.gameObject)); return(__state); }
public static void Prefix(BaseDeconstructable __instance) { NitroxId id = NitroxEntity.GetId(__instance.gameObject); TransientLocalObjectManager.Add(TransientObjectType.LATEST_DECONSTRUCTED_BASE_PIECE_GUID, id); }
/// <summary> /// Create a new <see cref="Fire"/>. Majority of code copied from <see cref="SubFire.CreateFire(SubFire.RoomFire)"/>. Currently does not support Fires created outside of a Cyclops /// </summary> /// <param name="fireGuid">Id of the Fire. Used for identification when dousing the fire</param> /// <param name="subRootGuid">Id of the Cyclops <see cref="SubRoot"/></param> /// <param name="room">The room the Fire will be spawned in</param> /// <param name="spawnNodeIndex">Each <see cref="CyclopsRooms"/> has multiple static Fire spawn points called spawnNodes. If the wrong index is provided, /// the clients will see fires in different places from the owner</param> public void Create(CyclopsFireData fireData) { SubFire subFire = NitroxEntity.RequireObjectFrom(fireData.CyclopsId).GetComponent <SubRoot>().damageManager.subFire; Dictionary <CyclopsRooms, SubFire.RoomFire> roomFiresDict = subFire.roomFires; // Copied from SubFire_CreateFire_Patch, which copies from SubFire.CreateFire() Transform transform2 = roomFiresDict[fireData.Room].spawnNodes[fireData.NodeIndex]; // If a fire already exists at the node, replace the old Id with the new one if (transform2.childCount > 0) { Fire existingFire = transform2.GetComponentInChildren <Fire>(); if (NitroxEntity.GetId(existingFire.gameObject) != fireData.CyclopsId) { Log.Error("[Fires.Create Fire already exists at node index " + fireData.NodeIndex + "! Replacing existing Fire Id " + NitroxEntity.GetId(existingFire.gameObject) + " with Id " + fireData.CyclopsId + "]"); NitroxEntity.SetNewId(existingFire.gameObject, fireData.CyclopsId); } return; } List <Transform> availableNodes = subFire.availableNodes; availableNodes.Clear(); foreach (Transform transform in roomFiresDict[fireData.Room].spawnNodes) { if (transform.childCount == 0) { availableNodes.Add(transform); } } roomFiresDict[fireData.Room].fireValue++; PrefabSpawn component = transform2.GetComponent <PrefabSpawn>(); if (component == null) { return; } else { Log.Error("[FireCreatedProcessor Cannot create new Cyclops fire! PrefabSpawn component could not be found in fire node!" + " Fire Id: " + fireData.FireId + " SubRoot Id: " + fireData.CyclopsId + " Room: " + fireData.Room + " NodeIndex: " + fireData.NodeIndex + "]"); } GameObject gameObject = component.SpawnManual(); Fire componentInChildren = gameObject.GetComponentInChildren <Fire>(); if (componentInChildren) { componentInChildren.fireSubRoot = subFire.subRoot; NitroxEntity.SetNewId(componentInChildren.gameObject, fireData.FireId); } subFire.roomFires = roomFiresDict; subFire.availableNodes = availableNodes; }
// Send ping to other players public static void Postfix(CyclopsSonarButton __instance) { NitroxId id = NitroxEntity.GetId(__instance.subRoot.gameObject); NitroxServiceLocator.LocateService <Cyclops>().BroadcastSonarPing(id); }
public static void Prefix(Vehicle __instance) { NitroxId id = NitroxEntity.GetId(__instance.gameObject); NitroxServiceLocator.LocateService <SimulationOwnership>().StopSimulatingEntity(id); }
public static bool Prefix(VehicleDockingBay __instance, Collider other) { Vehicle vehicle = other.GetComponentInParent <Vehicle>(); prevInterpolatingVehicle = __instance.interpolatingVehicle; return(vehicle == null || NitroxServiceLocator.LocateService <SimulationOwnership>().HasAnyLockType(NitroxEntity.GetId(vehicle.gameObject))); }
public static void Prefix(ExosuitTorpedoArm __instance, bool __result, TorpedoType torpedoType, Transform siloTransform) { if (torpedoType != null) { ExosuitArmAction action = ExosuitArmAction.START_USE_TOOL; if (siloTransform == __instance.siloSecond) { action = ExosuitArmAction.ALT_HIT; } if (siloTransform != __instance.siloFirst && siloTransform != __instance.siloSecond) { Log.Error("Exosuit torpedo arm siloTransform is not first or second silo " + NitroxEntity.GetId(__instance.gameObject)); } NitroxServiceLocator.LocateService <ExosuitModuleEvent>().BroadcastArmAction(TechType.ExosuitTorpedoArmModule, __instance, action, Player.main.camRoot.GetAimingTransform().forward, Player.main.camRoot.GetAimingTransform().rotation ); } }
public NitroxId GetEscapePodStorageId(Transform ownerTransform) { StorageContainer SC = ownerTransform.parent.gameObject.RequireComponentInChildren <StorageContainer>(); return(NitroxEntity.GetId(SC.gameObject)); }
public static void Postfix(PingInstance instance) { if (!instance) { return; } NitroxServiceLocator.LocateService <IPacketSender>().Send(new PingRenamed(NitroxEntity.GetId(instance.gameObject), instance.GetLabel(), SerializationHelper.GetBytes(instance.gameObject))); }
private static void Callback(Rocket rocketInstanceAttachedToConstructor, TechType currentStageTech) { NitroxId rocketId = NitroxEntity.GetId(rocketInstanceAttachedToConstructor.gameObject); NitroxServiceLocator.LocateService <Rockets>().BroadcastRocketStateUpdate(rocketId, currentStageTech); }
//We need to get TechType from parameters because CraftData can't resolve TechType.Cyclops by himself public VehicleModel BuildVehicleModelFrom(GameObject gameObject, TechType techType) { if (VehicleHelper.IsVehicle(techType)) { List <InteractiveChildObjectIdentifier> childIdentifiers = VehicleChildObjectIdentifierHelper.ExtractInteractiveChildren(gameObject); Optional <Vehicle> opvehicle = Optional.OfNullable(gameObject.GetComponent <Vehicle>()); NitroxId constructedObjectId = NitroxEntity.GetId(gameObject); NitroxVector3[] hsb = VehicleHelper.GetPrimalDefaultColours(); string name = string.Empty; float health = 200f; if (opvehicle.HasValue) { //Seamoth & Exosuit Optional <LiveMixin> livemixin = Optional.OfNullable(opvehicle.Value.GetComponent <LiveMixin>()); if (livemixin.HasValue) { health = livemixin.Value.health; } name = opvehicle.Value.GetName(); if (techType == TechType.Exosuit) { //For odd reasons the default colors aren't set yet for exosuit so we force it opvehicle.Value.ReflectionCall("RegenerateRenderInfo", false, false); } hsb = opvehicle.Value.subName.AliveOrNull()?.GetColors().ToDto(); } else { //Cyclops try { GameObject target = NitroxEntity.RequireObjectFrom(constructedObjectId); SubNameInput subNameInput = target.RequireComponentInChildren <SubNameInput>(); SubName subNameTarget = (SubName)subNameInput.ReflectionGet("target"); name = subNameTarget.GetName(); hsb = subNameTarget.AliveOrNull()?.GetColors().ToDto(); Optional <LiveMixin> livemixin = Optional.OfNullable(target.GetComponent <LiveMixin>()); if (livemixin.HasValue) { health = livemixin.Value.health; } } catch (Exception ex) { Log.Error($"{nameof(Vehicles)}: Error while trying to spawn a cyclops ({constructedObjectId})", ex); } } return(VehicleModelFactory.BuildFrom( techType.ToDto(), constructedObjectId, gameObject.transform.position.ToDto(), gameObject.transform.rotation.ToDto(), childIdentifiers, Optional.Empty, name, hsb ?? VehicleHelper.GetDefaultColours(techType), //Shouldn't be null now, but just in case health )); } else { Log.Error($"{nameof(Vehicles)}: Impossible to build from a non-vehicle GameObject (Received {techType})"); } return(null); }
public void ConstructionComplete(GameObject ghost, Optional <Base> lastTargetBase, Int3 lastTargetBaseOffset) { NitroxId baseId = null; Optional <object> opConstructedBase = TransientLocalObjectManager.Get(TransientObjectType.BASE_GHOST_NEWLY_CONSTRUCTED_BASE_GAMEOBJECT); NitroxId id = NitroxEntity.GetId(ghost); Log.Info($"Construction complete on {id} {ghost.name}"); if (opConstructedBase.HasValue) { GameObject constructedBase = (GameObject)opConstructedBase.Value; baseId = NitroxEntity.GetId(constructedBase); } // For base pieces, we must switch the id from the ghost to the newly constructed piece. // Furniture just uses the same game object as the ghost for the final product. if (ghost.GetComponent <ConstructableBase>()) { Int3 latestCell = lastTargetBaseOffset; Base latestBase = lastTargetBase.HasValue ? lastTargetBase.Value : ((GameObject)opConstructedBase.Value).GetComponent <Base>(); baseId = NitroxEntity.GetId(latestBase.gameObject); Transform cellTransform = latestBase.GetCellObject(latestCell); // This check ensures that the latestCell actually leads us to the correct entity. The lastTargetBaseOffset is unreliable as the base shape // can change which makes the target offset change. It may be possible to fully deprecate lastTargetBaseOffset and only rely on GetClosestCell; // however, there may still be pieces were the ghost base's target offset is authoratitive due to incorrect game object positioning. if (latestCell == default(Int3) || cellTransform == null) { latestBase.GetClosestCell(ghost.transform.position, out latestCell, out Vector3 _, out float _); cellTransform = latestBase.GetCellObject(latestCell); Validate.NotNull(cellTransform, "Unable to find cell transform at " + latestCell); } GameObject finishedPiece = null; // There can be multiple objects in a cell (such as a corridor with hatches built into it) // we look for a object that is able to be deconstructed that hasn't been tagged yet. foreach (Transform child in cellTransform) { bool isNewBasePiece = !child.GetComponent <NitroxEntity>() && child.GetComponent <BaseDeconstructable>(); if (isNewBasePiece) { finishedPiece = child.gameObject; break; } } Validate.NotNull(finishedPiece, $"Could not find finished piece in cell {latestCell}"); Log.Info($"Setting id to finished piece: {finishedPiece.name} {id}"); Object.Destroy(ghost); NitroxEntity.SetNewId(finishedPiece, id); BasePieceSpawnProcessor.RunSpawnProcessor(finishedPiece.GetComponent <BaseDeconstructable>(), latestBase, latestCell, finishedPiece); } else if (ghost.TryGetComponent(out Constructable constructable)) { FurnitureSpawnProcessor.RunSpawnProcessor(constructable); } else { Log.Error($"Found ghost which is neither base piece nor a constructable: {ghost.name}"); } Log.Info($"Construction Completed {id} in base {baseId}"); ConstructionCompleted constructionCompleted = new ConstructionCompleted(id, baseId); packetSender.Send(constructionCompleted); }
public static bool Prefix(SubFire __instance, SubFire.RoomFire startInRoom, out bool __state) { __state = NitroxServiceLocator.LocateService <SimulationOwnership>().HasAnyLockType(NitroxEntity.GetId(__instance.subRoot.gameObject)); // Block any new fires if this player is not the owner return(__state); }
/// <summary> /// Send out a <see cref="CyclopsDamage"/> packet /// </summary> private void BroadcastDamageState(SubRoot subRoot, Optional <DamageInfo> info) { NitroxId subId = NitroxEntity.GetId(subRoot.gameObject); LiveMixin subHealth = subRoot.gameObject.RequireComponent <LiveMixin>(); if (subHealth.health > 0) { CyclopsDamageInfoData damageInfo = null; if (info.HasValue) { DamageInfo damage = info.Value; // Source of the damage. Used if the damage done to the Cyclops was not calculated on other clients. Currently it's just used to figure out what sounds and // visual effects should be used. CyclopsDamageInfoData serializedDamageInfo = new CyclopsDamageInfoData(subId, damage.dealer != null ? NitroxEntity.GetId(damage.dealer) : null, damage.originalDamage, damage.damage, damage.position, damage.type); } int[] damagePointIndexes = GetActiveDamagePoints(subRoot).ToArray(); CyclopsFireData[] firePoints = GetActiveRoomFires(subRoot.GetComponent <SubFire>()).ToArray(); CyclopsDamage packet = new CyclopsDamage(subId, subRoot.GetComponent <LiveMixin>().health, subRoot.damageManager.subLiveMixin.health, subRoot.GetComponent <SubFire>().liveMixin.health, damagePointIndexes, firePoints, damageInfo); packetSender.Send(packet); } else { // RIP CyclopsDestroyed packet = new CyclopsDestroyed(subId); packetSender.Send(packet); } }
public static bool Prefix(SubRoot __instance, DamageInfo damageInfo) { return(NitroxServiceLocator.LocateService <SimulationOwnership>().HasAnyLockType(NitroxEntity.GetId(__instance.gameObject))); }
public static void Postfix(CyclopsSilentRunningAbilityButton __instance) { NitroxId id = NitroxEntity.GetId(__instance.subRoot.gameObject); NitroxServiceLocator.LocateService <Cyclops>().BroadcastChangeSilentRunning(id, true); }
public static void Postfix(CyclopsHornButton __instance) { NitroxId id = NitroxEntity.GetId(__instance.subRoot.gameObject); NitroxServiceLocator.LocateService <Cyclops>().BroadcastLaunchDecoy(id); }
public void ConstructionComplete(GameObject ghost) { NitroxId baseId = null; Optional <object> opConstructedBase = TransientLocalObjectManager.Get(TransientObjectType.BASE_GHOST_NEWLY_CONSTRUCTED_BASE_GAMEOBJECT); NitroxId id = NitroxEntity.GetId(ghost); if (opConstructedBase.IsPresent()) { GameObject constructedBase = (GameObject)opConstructedBase.Get(); baseId = NitroxEntity.GetId(constructedBase); } // For base pieces, we must switch the id from the ghost to the newly constructed piece. // Furniture just uses the same game object as the ghost for the final product. if (ghost.GetComponent <ConstructableBase>() != null) { Optional <object> latestBaseOp = TransientLocalObjectManager.Get(TransientObjectType.LATEST_BASE_WITH_NEW_CONSTRUCTION); Int3 latestCell; Base latestBase; if (latestBaseOp.IsPresent()) { latestCell = TransientLocalObjectManager.Require <Int3>(TransientObjectType.LATEST_BASE_CELL_WITH_NEW_CONSTRUCTION); latestBase = (Base)latestBaseOp.Get(); } else { latestBase = ((GameObject)opConstructedBase.Get()).GetComponent <Base>(); Vector3 worldPosition; float distance; latestBase.GetClosestCell(ghost.transform.position, out latestCell, out worldPosition, out distance); } Transform cellTransform = latestBase.GetCellObject(latestCell); GameObject finishedPiece = null; // There can be multiple objects in a cell (such as a corridor with hatces built into it) // we look for a object that is able to be deconstucted that hasn't been tagged yet. foreach (Transform child in cellTransform) { bool isNewBasePiece = (child.GetComponent <NitroxEntity>() == null && child.GetComponent <BaseDeconstructable>() != null); if (isNewBasePiece) { finishedPiece = child.gameObject; break; } } Validate.NotNull(finishedPiece, "Could not find finished piece in cell " + latestCell); Log.Info("Setting id to finished piece: " + finishedPiece.name + " " + id); UnityEngine.Object.Destroy(ghost); NitroxEntity.SetNewId(finishedPiece, id); BasePieceSpawnProcessor customSpawnProcessor = BasePieceSpawnProcessor.From(finishedPiece.GetComponent <BaseDeconstructable>()); customSpawnProcessor.SpawnPostProcess(latestBase, latestCell, finishedPiece); } Log.Info("Construction Completed " + id); ConstructionCompleted constructionCompleted = new ConstructionCompleted(id, baseId); packetSender.Send(constructionCompleted); }
//We need to get TechType from parameters because CraftData can't resolve TechType.Cyclops by himself public VehicleModel BuildVehicleModelFrom(GameObject gameObject, TechType techType) { if (IsVehicle(techType)) { List <InteractiveChildObjectIdentifier> childIdentifiers = VehicleChildObjectIdentifierHelper.ExtractInteractiveChildren(gameObject); Optional <Vehicle> opvehicle = Optional.OfNullable(gameObject.GetComponent <Vehicle>()); NitroxId constructedObjectId = NitroxEntity.GetId(gameObject); Vector3[] Colours = new Vector3[5]; Vector3[] HSB = new Vector3[5]; string name = string.Empty; float health = 200f; if (opvehicle.HasValue) { //Seamoth & Exosuit Optional <LiveMixin> livemixin = Optional.OfNullable(opvehicle.Value.GetComponent <LiveMixin>()); if (livemixin.HasValue) { health = livemixin.Value.health; } name = opvehicle.Value.GetName(); Colours = HSB = opvehicle.Value.subName?.GetColors(); } else { //Cyclops try { GameObject target = NitroxEntity.RequireObjectFrom(constructedObjectId); SubNameInput subNameInput = target.RequireComponentInChildren <SubNameInput>(); SubName subNameTarget = (SubName)subNameInput.ReflectionGet("target"); Colours = subNameTarget?.GetColors(); name = subNameTarget?.GetName(); Optional <LiveMixin> livemixin = Optional.OfNullable(target.GetComponent <LiveMixin>()); if (livemixin.HasValue) { health = livemixin.Value.health; } } catch (Exception ex) { Log.Error($"{nameof(Vehicles)}: Error while trying to spawn a cyclops ({constructedObjectId})", ex); } } return(VehicleModelFactory.BuildFrom( techType.Model(), constructedObjectId, gameObject.transform.position, gameObject.transform.rotation, childIdentifiers, Optional.Empty, name, Colours, HSB, health )); } else { Log.Error($"{nameof(Vehicles)}: Impossible to build from a non-vehicle GameObject (Received {techType})"); } return(null); }
public static void Prefix(CyclopsSonarButton __instance) { currentCyclopsId = NitroxEntity.GetId(__instance.subRoot.gameObject); }
public static void Postfix(CyclopsEngineChangeState __instance) { NitroxId id = NitroxEntity.GetId(__instance.subRoot.gameObject); Resolve <Cyclops>().BroadcastToggleEngineState(id, __instance.motorMode.engineOn, __instance.startEngine); }
public static void Postfix(CyclopsEngineChangeState __instance) { NitroxId id = NitroxEntity.GetId(__instance.subRoot.gameObject); NitroxServiceLocator.LocateService <Cyclops>().BroadcastToggleEngineState(id, __instance.motorMode.engineOn, (bool)__instance.ReflectionGet("startEngine")); }