using System.Runtime.InteropServices; using RoR2.ExpansionManagement; using UnityEngine; using UnityEngine.Networking; namespace RoR2; public class PortalSpawner : NetworkBehaviour { [SerializeField] private InteractableSpawnCard portalSpawnCard; [Range(0f, 1f)] [SerializeField] private float spawnChance; [SerializeField] [Tooltip("The portal is spawned relative to this transform. If null, it uses this object's transform for reference.")] private Transform spawnReferenceLocation; [SerializeField] private float minSpawnDistance; [Tooltip("The maximum spawn distance for the portal relative to the spawnReferenceLocation. If 0, it will be spawned at exactly the referenced location.")] [SerializeField] private float maxSpawnDistance; [SerializeField] private string spawnPreviewMessageToken; [SerializeField] private string spawnMessageToken; [SerializeField] private ChildLocator modelChildLocator; [SerializeField] private string previewChildName; [SerializeField] private ExpansionDef requiredExpansion; [SerializeField] private int minStagesCleared; [SerializeField] private string bannedEventFlag; [SerializeField] private string requiredEventFlag; [SerializeField] private string[] validStages; [SerializeField] private string[] invalidStages; [SerializeField] private int[] validStageTiers; private Xoroshiro128Plus rng; private GameObject previewChild; [SyncVar(hook = "OnWillSpawnUpdated")] private bool willSpawn; public bool NetworkwillSpawn { get { return willSpawn; } [param: In] set { if (NetworkServer.localClientActive && !base.syncVarHookGuard) { base.syncVarHookGuard = true; OnWillSpawnUpdated(value); base.syncVarHookGuard = false; } SetSyncVar(value, ref willSpawn, 1u); } } private void Start() { GetPreviewChild(); if (!NetworkServer.active) { return; } rng = new Xoroshiro128Plus(Run.instance.stageRng.nextUlong); bool flag = !requiredExpansion || Run.instance.IsExpansionEnabled(requiredExpansion); bool flag2 = Run.instance.stageClearCount >= minStagesCleared; bool flag3 = string.IsNullOrEmpty(bannedEventFlag) || !Run.instance.GetEventFlag(bannedEventFlag); if ((string.IsNullOrEmpty(requiredEventFlag) || Run.instance.GetEventFlag(requiredEventFlag)) && flag3 && rng.nextNormalizedFloat <= spawnChance && flag && flag2 && isValidStage()) { NetworkwillSpawn = true; if (!string.IsNullOrEmpty(spawnPreviewMessageToken)) { Chat.SendBroadcastChat(new Chat.SimpleChatMessage { baseToken = spawnPreviewMessageToken }); } if ((bool)previewChild) { previewChild.SetActive(value: true); } } } private void GetPreviewChild() { if ((bool)modelChildLocator) { Transform transform = modelChildLocator.FindChild(previewChildName); if ((bool)transform) { previewChild = transform.gameObject; } } } private bool isValidStage() { bool result = false; bool flag = false; if (validStages.Length != 0) { string baseSceneNameOverride = Stage.instance.sceneDef.baseSceneNameOverride; for (int i = 0; i < validStages.Length; i++) { if (validStages[i] == baseSceneNameOverride) { flag = true; } } } else if (validStages.Length == 0) { flag = true; } bool flag2 = false; if (invalidStages.Length != 0) { string baseSceneNameOverride2 = Stage.instance.sceneDef.baseSceneNameOverride; for (int j = 0; j < invalidStages.Length; j++) { if (invalidStages[j] == baseSceneNameOverride2) { flag2 = true; } } } bool flag3 = false; if (validStageTiers.Length != 0) { int stageOrder = Stage.instance.sceneDef.stageOrder; for (int k = 0; k < validStageTiers.Length; k++) { if (validStageTiers[k] == stageOrder) { flag3 = true; } } } else if (validStageTiers.Length == 0) { flag3 = true; } if (flag && !flag2 && flag3) { result = true; } _ = flag && flag2; return result; } [Server] public bool AttemptSpawnPortalServer() { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.PortalSpawner::AttemptSpawnPortalServer()' called on client"); return false; } if (willSpawn) { if ((bool)previewChild) { previewChild.SetActive(value: false); } NetworkwillSpawn = false; DirectorPlacementRule.PlacementMode placementMode = DirectorPlacementRule.PlacementMode.Approximate; if (maxSpawnDistance <= 0f) { placementMode = DirectorPlacementRule.PlacementMode.Direct; } Transform transform = spawnReferenceLocation; if (!transform) { transform = base.transform; } GameObject obj = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(portalSpawnCard, new DirectorPlacementRule { minDistance = minSpawnDistance, maxDistance = maxSpawnDistance, placementMode = placementMode, position = base.transform.position, spawnOnTarget = transform }, rng)); if ((bool)obj && !string.IsNullOrEmpty(spawnMessageToken)) { Chat.SendBroadcastChat(new Chat.SimpleChatMessage { baseToken = spawnMessageToken }); } return obj; } return false; } private void OnWillSpawnUpdated(bool newValue) { NetworkwillSpawn = newValue; if (previewChild == null) { GetPreviewChild(); } if ((bool)previewChild) { previewChild.SetActive(newValue); } } private void UNetVersion() { } public override bool OnSerialize(NetworkWriter writer, bool forceAll) { if (forceAll) { writer.Write(willSpawn); return true; } bool flag = false; if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(willSpawn); } if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); } return flag; } public override void OnDeserialize(NetworkReader reader, bool initialState) { if (initialState) { willSpawn = reader.ReadBoolean(); return; } int num = (int)reader.ReadPackedUInt32(); if (((uint)num & (true ? 1u : 0u)) != 0) { OnWillSpawnUpdated(reader.ReadBoolean()); } } public override void PreStartClient() { } }