using System.Collections; using System.Collections.Generic; using System.Linq; using NitroxClient.Communication; using NitroxClient.Communication.Abstract; using NitroxClient.MonoBehaviours; using NitroxClient.Unity.Helper; using NitroxModel_Subnautica.DataStructures; using NitroxModel.DataStructures; using NitroxModel.DataStructures.Util; using NitroxModel.Packets; using NitroxModel_Subnautica.DataStructures.GameLogic; using NitroxModel_Subnautica.Packets; using UnityEngine; using static NitroxClient.GameLogic.Spawning.Metadata.Extractor.CyclopsMetadataExtractor; namespace NitroxClient.GameLogic { public class Cyclops { private readonly IPacketSender packetSender; private readonly Vehicles vehicles; private readonly Entities entities; public Cyclops(IPacketSender packetSender, Vehicles vehicles, Entities entities) { this.packetSender = packetSender; this.vehicles = vehicles; this.entities = entities; } public void BroadcastMetadataChange(NitroxId id) { GameObject gameObject = NitroxEntity.RequireObjectFrom(id); CyclopsGameObject cyclops = new CyclopsGameObject() { GameObject = gameObject }; entities.EntityMetadataChanged(cyclops, id); } public void BroadcastLaunchDecoy(NitroxId id) { CyclopsDecoyLaunch packet = new CyclopsDecoyLaunch(id); packetSender.Send(packet); } public void BroadcastActivateFireSuppression(NitroxId id) { CyclopsFireSuppression packet = new CyclopsFireSuppression(id); packetSender.Send(packet); } public void LaunchDecoy(NitroxId id) { GameObject cyclops = NitroxEntity.RequireObjectFrom(id); CyclopsDecoyManager decoyManager = cyclops.RequireComponent(); using (PacketSuppressor.Suppress()) { decoyManager.Invoke(nameof(CyclopsDecoyManager.LaunchWithDelay), 3f); decoyManager.decoyLaunchButton.UpdateText(); decoyManager.subRoot.voiceNotificationManager.PlayVoiceNotification(decoyManager.subRoot.decoyNotification, false, true); decoyManager.subRoot.BroadcastMessage("UpdateTotalDecoys", decoyManager.decoyCount, SendMessageOptions.DontRequireReceiver); CyclopsDecoyLaunchButton decoyLaunchButton = cyclops.RequireComponentInChildren(); decoyLaunchButton.StartCooldown(); } } public void StartFireSuppression(NitroxId id) { GameObject cyclops = NitroxEntity.RequireObjectFrom(id); CyclopsFireSuppressionSystemButton fireSuppButton = cyclops.RequireComponentInChildren(); using (PacketSuppressor.Suppress()) { // Infos from SubFire.StartSystem fireSuppButton.subFire.StartCoroutine(StartFireSuppressionSystem(fireSuppButton.subFire)); fireSuppButton.StartCooldown(); } } // Remake of the StartSystem Coroutine from original player. Some Methods are not used from the original coroutine // For example no temporaryClose as this will be initiated anyway from the originating Player // Also the fire extiguishing will not start cause the initial player is already extiguishing the fires. Else this could double/triple/... the extinguishing private IEnumerator StartFireSuppressionSystem(SubFire fire) { fire.subRoot.voiceNotificationManager.PlayVoiceNotification(fire.subRoot.fireSupressionNotification, false, true); yield return Yielders.WaitFor3Seconds; fire.fireSuppressionActive = true; fire.subRoot.fireSuppressionState = true; fire.subRoot.BroadcastMessage("NewAlarmState", null, SendMessageOptions.DontRequireReceiver); fire.Invoke(nameof(SubFire.CancelFireSuppression), fire.fireSuppressionSystemDuration); float doorCloseDuration = 30f; fire.gameObject.BroadcastMessage("TemporaryLock", doorCloseDuration, SendMessageOptions.DontRequireReceiver); } /// /// Triggers a packet /// public void OnCreateDamagePoint(SubRoot subRoot) { BroadcastDamageState(subRoot, Optional.Empty); } /// /// Called when the player repairs a . Right now it's not possible to partially repair because it would be difficult to implement. /// s are coupled with , which is used with just about anything that has health. /// I would need to hook onto , or maybe the repair gun event to catch when something repairs a damage point, which I don't /// believe is worth the effort. A is already fully repaired in a little over a second. This can trigger sending /// and packets /// public void OnDamagePointRepaired(SubRoot subRoot, CyclopsDamagePoint damagePoint, float repairAmount) { if (!subRoot.TryGetIdOrWarn(out NitroxId subId)) { return; } for (int i = 0; i < subRoot.damageManager.damagePoints.Length; i++) { if (subRoot.damageManager.damagePoints[i] == damagePoint) { CyclopsDamagePointRepaired packet = new(subId, i, repairAmount); packetSender.Send(packet); return; } } } /// /// Send out a packet /// private void BroadcastDamageState(SubRoot subRoot, Optional info) { if (!subRoot.TryGetIdOrWarn(out NitroxId subId)) { return; } LiveMixin subHealth = subRoot.gameObject.RequireComponent(); if (subHealth.health <= 0) { return; } CyclopsDamageInfoData damageInfo = null; if (info.HasValue) { DamageInfo damage = info.Value; Optional dealerId = damage.dealer.GetId(); // 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. damageInfo = new CyclopsDamageInfoData(subId, dealerId, damage.originalDamage, damage.damage, damage.position.ToDto(), damage.type); } int[] damagePointIndexes = GetActiveDamagePoints(subRoot).ToArray(); CyclopsFireData[] firePoints = GetActiveRoomFires(subRoot.GetComponent()).ToArray(); CyclopsDamage packet = new(subId, subRoot.GetComponent().health, subRoot.damageManager.subLiveMixin.health, subRoot.GetComponent().liveMixin.health, damagePointIndexes, firePoints, damageInfo); packetSender.Send(packet); } /// /// Get all of the index locations of s in . /// private IEnumerable GetActiveDamagePoints(SubRoot subRoot) { for (int i = 0; i < subRoot.damageManager.damagePoints.Length; i++) { if (subRoot.damageManager.damagePoints[i].gameObject.activeSelf) { yield return i; } } } /// /// Get all of the index locations of all the fires on the . contains /// a static list of all possible fire nodes. /// private IEnumerable GetActiveRoomFires(SubFire subFire) { if (!subFire.subRoot.TryGetIdOrWarn(out NitroxId subRootId)) { yield break; } foreach (KeyValuePair roomFire in subFire.roomFires) { for (int i = 0; i < roomFire.Value.spawnNodes.Length; i++) { if (roomFire.Value.spawnNodes[i].childCount > 0) { if (!roomFire.Value.spawnNodes[i].GetComponentInChildren().TryGetIdOrWarn(out NitroxId fireId)) { yield break; } yield return new CyclopsFireData(fireId, subRootId, roomFire.Key, i); } } } } } }