using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.InteropServices; using System.Xml.Linq; using HG; using JetBrains.Annotations; using RoR2.ConVar; using RoR2.ExpansionManagement; using RoR2.Navigation; using RoR2.Networking; using RoR2.Stats; using Unity; using UnityEngine; using UnityEngine.Networking; namespace RoR2; [DisallowMultipleComponent] [RequireComponent(typeof(NetworkRuleBook))] [RequireComponent(typeof(RunArtifactManager))] public class Run : NetworkBehaviour { [Serializable] public struct RunStopwatch : IEquatable { public float offsetFromFixedTime; public bool isPaused; public bool Equals(RunStopwatch other) { if (offsetFromFixedTime.Equals(other.offsetFromFixedTime)) { return isPaused == other.isPaused; } return false; } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is RunStopwatch other) { return Equals(other); } return false; } public override int GetHashCode() { return (offsetFromFixedTime.GetHashCode() * 397) ^ isPaused.GetHashCode(); } } [Serializable] public struct TimeStamp : IEquatable, IComparable { public readonly float t; private static float tNow; public static readonly TimeStamp zero; public static readonly TimeStamp positiveInfinity; public static readonly TimeStamp negativeInfinity; public float timeUntil => t - tNow; public float timeSince => tNow - t; public float timeUntilClamped => Mathf.Max(timeUntil, 0f); public float timeSinceClamped => Mathf.Max(timeSince, 0f); public bool hasPassed => t <= tNow; public bool isInfinity => float.IsInfinity(t); public bool isPositiveInfinity => float.IsPositiveInfinity(t); public bool isNegativeInfinity => float.IsNegativeInfinity(t); public static TimeStamp now => new TimeStamp(tNow); public override int GetHashCode() { return t.GetHashCode(); } public static void Update() { tNow = instance.time; } private TimeStamp(float t) { this.t = t; } public bool Equals(TimeStamp other) { return t.Equals(other.t); } public override bool Equals(object obj) { if (obj is TimeStamp) { return Equals((TimeStamp)obj); } return false; } public int CompareTo(TimeStamp other) { return t.CompareTo(other.t); } public static TimeStamp operator +(TimeStamp a, float b) { return new TimeStamp(a.t + b); } public static TimeStamp operator -(TimeStamp a, float b) { return new TimeStamp(a.t - b); } public static float operator -(TimeStamp a, TimeStamp b) { return a.t - b.t; } public static bool operator <(TimeStamp a, TimeStamp b) { return a.t < b.t; } public static bool operator >(TimeStamp a, TimeStamp b) { return a.t > b.t; } public static bool operator <=(TimeStamp a, TimeStamp b) { return a.t <= b.t; } public static bool operator >=(TimeStamp a, TimeStamp b) { return a.t >= b.t; } public static bool operator ==(TimeStamp a, TimeStamp b) { return a.Equals(b); } public static bool operator !=(TimeStamp a, TimeStamp b) { return !a.Equals(b); } public static float operator -(TimeStamp a, FixedTimeStamp b) { return a.t - b.t; } public static TimeStamp Deserialize(NetworkReader reader) { return new TimeStamp(reader.ReadSingle()); } public static void Serialize(NetworkWriter writer, TimeStamp timeStamp) { writer.Write(timeStamp.t); } public static void ToXml(XElement element, TimeStamp src) { element.Value = TextSerialization.ToStringInvariant(src.t); } public static bool FromXml(XElement element, ref TimeStamp dest) { if (TextSerialization.TryParseInvariant(element.Value, out float result)) { dest = new TimeStamp(result); return true; } return false; } static TimeStamp() { zero = new TimeStamp(0f); positiveInfinity = new TimeStamp(float.PositiveInfinity); negativeInfinity = new TimeStamp(float.NegativeInfinity); HGXml.Register(ToXml, FromXml); } } [Serializable] public struct FixedTimeStamp : IEquatable, IComparable { public readonly float t; private static float tNow; public static readonly FixedTimeStamp zero; public static readonly FixedTimeStamp positiveInfinity; public static readonly FixedTimeStamp negativeInfinity; public float timeUntil => t - tNow; public float timeSince => tNow - t; public float timeUntilClamped => Mathf.Max(timeUntil, 0f); public float timeSinceClamped => Mathf.Max(timeSince, 0f); public bool hasPassed => t <= tNow; public bool isInfinity => float.IsInfinity(t); public bool isPositiveInfinity => float.IsPositiveInfinity(t); public bool isNegativeInfinity => float.IsNegativeInfinity(t); public static FixedTimeStamp now => new FixedTimeStamp(tNow); public override int GetHashCode() { return t.GetHashCode(); } public static void Update() { tNow = instance.fixedTime; } private FixedTimeStamp(float t) { this.t = t; } public bool Equals(FixedTimeStamp other) { return t.Equals(other.t); } public override bool Equals(object obj) { if (obj is FixedTimeStamp) { return Equals((FixedTimeStamp)obj); } return false; } public int CompareTo(FixedTimeStamp other) { return t.CompareTo(other.t); } public static FixedTimeStamp operator +(FixedTimeStamp a, float b) { return new FixedTimeStamp(a.t + b); } public static FixedTimeStamp operator -(FixedTimeStamp a, float b) { return new FixedTimeStamp(a.t - b); } public static float operator -(FixedTimeStamp a, FixedTimeStamp b) { return a.t - b.t; } public static bool operator <(FixedTimeStamp a, FixedTimeStamp b) { return a.t < b.t; } public static bool operator >(FixedTimeStamp a, FixedTimeStamp b) { return a.t > b.t; } public static bool operator <=(FixedTimeStamp a, FixedTimeStamp b) { return a.t <= b.t; } public static bool operator >=(FixedTimeStamp a, FixedTimeStamp b) { return a.t >= b.t; } public static bool operator ==(FixedTimeStamp a, FixedTimeStamp b) { return a.Equals(b); } public static bool operator !=(FixedTimeStamp a, FixedTimeStamp b) { return !a.Equals(b); } public static FixedTimeStamp Deserialize(NetworkReader reader) { return new FixedTimeStamp(reader.ReadSingle()); } public static void Serialize(NetworkWriter writer, FixedTimeStamp timeStamp) { writer.Write(timeStamp.t); } public static void ToXml(XElement element, FixedTimeStamp src) { element.Value = TextSerialization.ToStringInvariant(src.t); } public static bool FromXml(XElement element, ref FixedTimeStamp dest) { if (TextSerialization.TryParseInvariant(element.Value, out float result)) { dest = new FixedTimeStamp(result); return true; } return false; } static FixedTimeStamp() { zero = new FixedTimeStamp(0f); positiveInfinity = new FixedTimeStamp(float.PositiveInfinity); negativeInfinity = new FixedTimeStamp(float.NegativeInfinity); HGXml.Register(ToXml, FromXml); } } private NetworkRuleBook networkRuleBookComponent; [Tooltip("This is assigned to the prefab automatically by GameModeCatalog at runtime. Do not set this value manually.")] [HideInInspector] public GameModeIndex gameModeIndex = GameModeIndex.Invalid; public string nameToken = ""; [Tooltip("Whether or not the user can select this game mode for play in the game mode selector UI.")] public bool userPickable = true; [Tooltip("Dictates items given when rebirth is active when the user doesn't have a stored item.")] public PickupDropTable rebirthDropTable; public static int stagesPerLoop = 5; public static float baseGravity = -30f; [SyncVar] private NetworkGuid _uniqueId; [SyncVar] private NetworkDateTime startTimeUtc; [ShowFieldObsolete] [Obsolete("Use startingSceneGroup instead.")] [Tooltip("The pool of scenes to select the first scene of the run from.")] public SceneDef[] startingScenes = Array.Empty(); [Tooltip("The pool of scenes to select the first scene of the run from.")] public SceneCollection startingSceneGroup; public ItemMask availableItems; public ItemMask expansionLockedItems; public EquipmentMask availableEquipment; public EquipmentMask expansionLockedEquipment; public bool isRunWeekly; [SyncVar] public float fixedTime; public float time; [HideInInspector] public bool isRunning; [SyncVar] private RunStopwatch runStopwatch; [SyncVar] public int stageClearCount; public SceneDef nextStageScene; public SceneDef[] blacklistedScenesForFirstScene = Array.Empty(); public GameObject gameOverPrefab; public GameObject lobbyBackgroundPrefab; public GameObject uiPrefab; private ulong _seed; public Xoroshiro128Plus runRNG; public Xoroshiro128Plus nextStageRng; public Xoroshiro128Plus stageRngGenerator; public Xoroshiro128Plus stageRng; public Xoroshiro128Plus bossRewardRng; public Xoroshiro128Plus treasureRng; public Xoroshiro128Plus spawnRng; public Xoroshiro128Plus randomSurvivorOnRespawnRng; public float difficultyCoefficient = 1f; public float compensatedDifficultyCoefficient = 1f; public float oneOverCompensatedDifficultyCoefficientSquared = 1f; [SyncVar] private int selectedDifficultyInternal = 1; public int shopPortalCount; private static int ambientLevelCap = 99; private bool lowerPricedChestsTimerOn; private float lowerPricedChestsTimer; private float lowerPricedChestsDuration = 10f; private static readonly StringConVar cvRunSceneOverride = new StringConVar("run_scene_override", ConVarFlags.Cheat, "", "Overrides the first scene to enter in a run."); private readonly HashSet unlockablesUnlockedByAnyUser = new HashSet(); private readonly HashSet unlockablesUnlockedByAllUsers = new HashSet(); private readonly HashSet unlockablesAlreadyFullyObtained = new HashSet(); private bool shutdown; private Dictionary userMasters = new Dictionary(); private bool allowNewParticipants; public readonly List availableTier1DropList = new List(); public readonly List availableTier2DropList = new List(); public readonly List availableTier3DropList = new List(); public readonly List availableEquipmentDropList = new List(); public readonly List availableLunarEquipmentDropList = new List(); public readonly List availableLunarItemDropList = new List(); public readonly List availableLunarCombinedDropList = new List(); public readonly List availableBossDropList = new List(); public readonly List availableVoidTier1DropList = new List(); public readonly List availableVoidTier2DropList = new List(); public readonly List availableVoidTier3DropList = new List(); public readonly List availableVoidBossDropList = new List(); public WeightedSelection> smallChestDropTierSelector = new WeightedSelection>(); public WeightedSelection> mediumChestDropTierSelector = new WeightedSelection>(); public WeightedSelection> largeChestDropTierSelector = new WeightedSelection>(); private readonly HashSet eventFlags = new HashSet(); public static Run instance { get; private set; } public RuleBook ruleBook => networkRuleBookComponent.ruleBook; public bool isRunStopwatchPaused => runStopwatch.isPaused; public virtual int loopClearCount => stageClearCount / stagesPerLoop; public virtual bool spawnWithPod => instance.stageClearCount == 0; public virtual bool autoGenerateSpawnPoints => true; public virtual bool canFamilyEventTrigger => true; public IList uiInstances { get; protected set; } = new List(); public ulong seed { get { return _seed; } set { _seed = value; OnSeedSet(); } } public DifficultyIndex selectedDifficulty { get { return (DifficultyIndex)selectedDifficultyInternal; } set { NetworkselectedDifficultyInternal = (int)value; } } public int livingPlayerCount => PlayerCharacterMasterController.GetPlayersWithBodiesCount(); public int participatingPlayerCount => PlayerCharacterMasterController.instances.Count; public float ambientLevel { get; protected set; } public int ambientLevelFloor { get; protected set; } public float teamlessDamageCoefficient => difficultyCoefficient; public bool isGameOverServer { get; private set; } public NetworkGuid Network_uniqueId { get { return _uniqueId; } [param: In] set { SetSyncVar(value, ref _uniqueId, 1u); } } public NetworkDateTime NetworkstartTimeUtc { get { return startTimeUtc; } [param: In] set { SetSyncVar(value, ref startTimeUtc, 2u); } } public float NetworkfixedTime { get { return fixedTime; } [param: In] set { SetSyncVar(value, ref fixedTime, 4u); } } public RunStopwatch NetworkrunStopwatch { get { return runStopwatch; } [param: In] set { SetSyncVar(value, ref runStopwatch, 8u); } } public int NetworkstageClearCount { get { return stageClearCount; } [param: In] set { SetSyncVar(value, ref stageClearCount, 16u); } } public int NetworkselectedDifficultyInternal { get { return selectedDifficultyInternal; } [param: In] set { SetSyncVar(value, ref selectedDifficultyInternal, 32u); } } public static event Action onServerRunSetRuleBookGlobal; public static event Action onRunSetRuleBookGlobal; public static event Action onRunAmbientLevelUp; public static event Action onPlayerFirstCreatedServer; public static event Action onServerGameOver; public static event Action onClientGameOverGlobal; public static event Action onRunStartGlobal; public static event Action onRunDestroyGlobal; public static event Action onAvailablePickupsModified; protected virtual void OnEnable() { instance = SingletonHelper.Assign(instance, this); } protected virtual void OnDisable() { instance = SingletonHelper.Unassign(instance, this); if (instance == null) { RewiredIntegrationManager.RefreshAfterRunFinish(); } } protected void Awake() { networkRuleBookComponent = GetComponent(); networkRuleBookComponent.onRuleBookUpdated += OnRuleBookUpdated; availableItems = ItemMask.Rent(); expansionLockedItems = ItemMask.Rent(); availableEquipment = EquipmentMask.Rent(); expansionLockedEquipment = EquipmentMask.Rent(); if (NetworkServer.active) { Network_uniqueId = (NetworkGuid)Guid.NewGuid(); NetworkstartTimeUtc = (NetworkDateTime)DateTime.UtcNow; } } [Server] public void SetRuleBook(RuleBook newRuleBook) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::SetRuleBook(RoR2.RuleBook)' called on client"); } else { networkRuleBookComponent.SetRuleBook(newRuleBook); } } private void OnRuleBookUpdated(NetworkRuleBook networkRuleBookComponent) { RuleBook ruleBook = networkRuleBookComponent.ruleBook; selectedDifficulty = ruleBook.FindDifficulty(); ruleBook.GenerateItemMask(availableItems); ruleBook.GenerateEquipmentMask(availableEquipment); expansionLockedItems.Clear(); foreach (ItemDef allItemDef in ItemCatalog.allItemDefs) { if ((bool)allItemDef && (bool)allItemDef.requiredExpansion && !IsExpansionEnabled(allItemDef.requiredExpansion)) { expansionLockedItems.Add(allItemDef.itemIndex); } } expansionLockedEquipment.Clear(); foreach (EquipmentIndex item in EquipmentCatalog.allEquipment) { EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(item); if ((bool)equipmentDef && (bool)equipmentDef.requiredExpansion && !IsExpansionEnabled(equipmentDef.requiredExpansion)) { expansionLockedEquipment.Add(equipmentDef.equipmentIndex); } } if (NetworkServer.active) { Run.onServerRunSetRuleBookGlobal?.Invoke(this, ruleBook); } Run.onRunSetRuleBookGlobal?.Invoke(this, ruleBook); } public Guid GetUniqueId() { return (Guid)_uniqueId; } public DateTime GetStartTimeUtc() { return (DateTime)startTimeUtc; } [Server] private void SetRunStopwatchPaused(bool isPaused) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::SetRunStopwatchPaused(System.Boolean)' called on client"); } else if (isPaused != runStopwatch.isPaused) { RunStopwatch networkrunStopwatch = runStopwatch; networkrunStopwatch.isPaused = isPaused; float num = GetRunStopwatch(); if (isPaused) { networkrunStopwatch.offsetFromFixedTime = num; } else { networkrunStopwatch.offsetFromFixedTime = num - fixedTime; } NetworkrunStopwatch = networkrunStopwatch; } } public float GetRunStopwatch() { if (runStopwatch.isPaused) { return runStopwatch.offsetFromFixedTime; } return fixedTime + runStopwatch.offsetFromFixedTime; } [Server] public void SetRunStopwatch(float t) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::SetRunStopwatch(System.Single)' called on client"); return; } RunStopwatch networkrunStopwatch = runStopwatch; if (networkrunStopwatch.isPaused) { networkrunStopwatch.offsetFromFixedTime = t; } else { networkrunStopwatch.offsetFromFixedTime = t - fixedTime; } NetworkrunStopwatch = networkrunStopwatch; } public virtual GameObject InstantiateUi(Transform uiRoot) { GameObject gameObject = null; if ((bool)uiRoot && (bool)uiPrefab) { gameObject = UnityEngine.Object.Instantiate(uiPrefab, uiRoot); uiInstances.Add(gameObject); } return gameObject; } public void UnregisterUi(GameObject uiInstance) { uiInstances.Remove(uiInstance); } private void GenerateStageRNG() { stageRng = new Xoroshiro128Plus(stageRngGenerator.nextUlong); bossRewardRng = new Xoroshiro128Plus(stageRng.nextUlong); treasureRng = new Xoroshiro128Plus(stageRng.nextUlong); spawnRng = new Xoroshiro128Plus(stageRng.nextUlong); randomSurvivorOnRespawnRng = new Xoroshiro128Plus(stageRng.nextUlong); } protected void OnAmbientLevelUp() { Run.onRunAmbientLevelUp?.Invoke(this); } protected virtual void FixedUpdate() { NetworkfixedTime = fixedTime + Time.fixedDeltaTime; FixedTimeStamp.Update(); if (NetworkServer.active) { SetRunStopwatchPaused(!ShouldUpdateRunStopwatch()); } OnFixedUpdate(); } public void RecalculateDifficultyCoefficent() { RecalculateDifficultyCoefficentInternal(); } protected virtual void RecalculateDifficultyCoefficentInternal() { float num = GetRunStopwatch(); DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(selectedDifficulty); float num2 = Mathf.Floor(num * (1f / 60f)); float num3 = (float)participatingPlayerCount * 0.3f; float num4 = 0.7f + num3; float num5 = 0.7f + num3; float num6 = Mathf.Pow(participatingPlayerCount, 0.2f); float num7 = 0.0506f * difficultyDef.scalingValue * num6; float num8 = 0.0506f * difficultyDef.scalingValue * num6; float num9 = Mathf.Pow(1.15f, stageClearCount); compensatedDifficultyCoefficient = (num5 + num8 * num2) * num9; difficultyCoefficient = (num4 + num7 * num2) * num9; oneOverCompensatedDifficultyCoefficientSquared = 1f / (compensatedDifficultyCoefficient * compensatedDifficultyCoefficient); float num10 = (num4 + num7 * (num * (1f / 60f))) * Mathf.Pow(1.15f, stageClearCount); ambientLevel = Mathf.Min((num10 - num4) / 0.33f + 1f, ambientLevelCap); int num11 = ambientLevelFloor; ambientLevelFloor = Mathf.FloorToInt(ambientLevel); if (num11 != ambientLevelFloor && num11 != 0 && ambientLevelFloor > num11) { OnAmbientLevelUp(); } } private void InitializeTimeStamps() { NetworkfixedTime = 0f; time = 0f; FixedTimeStamp.Update(); TimeStamp.Update(); } protected virtual void OnFixedUpdate() { RecalculateDifficultyCoefficent(); } public bool GetLowerPricedChestsTimer() { return lowerPricedChestsTimerOn; } protected void Update() { time = Mathf.Clamp(time + Time.deltaTime, fixedTime, fixedTime + Time.fixedDeltaTime); TimeStamp.Update(); if ((double)GetRunStopwatch() - Math.Floor(GetRunStopwatch() * (1f / 60f)) * 60.0 <= 0.1 && !lowerPricedChestsTimerOn) { lowerPricedChestsTimerOn = true; } if (lowerPricedChestsTimerOn) { lowerPricedChestsTimer += Time.fixedDeltaTime; } if (lowerPricedChestsTimer >= lowerPricedChestsDuration) { lowerPricedChestsTimerOn = false; lowerPricedChestsTimer = 0f; } } protected virtual bool ShouldUpdateRunStopwatch() { SceneDef mostRecentSceneDef = SceneCatalog.mostRecentSceneDef; if (mostRecentSceneDef.sceneType != SceneType.Stage && mostRecentSceneDef.sceneType != SceneType.TimedIntermission) { return false; } return livingPlayerCount > 0; } [Obsolete("Use the overload that accepts an UnlockableDef instead. This method may be removed from future releases.", false)] [Server] public bool CanUnlockableBeGrantedThisRun(string unlockableName) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::CanUnlockableBeGrantedThisRun(System.String)' called on client"); return false; } return CanUnlockableBeGrantedThisRun(UnlockableCatalog.GetUnlockableDef(unlockableName)); } [Server] public virtual bool CanUnlockableBeGrantedThisRun(UnlockableDef unlockableDef) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::CanUnlockableBeGrantedThisRun(RoR2.UnlockableDef)' called on client"); return false; } return !unlockablesAlreadyFullyObtained.Contains(unlockableDef); } [Obsolete("Use the overload that accepts an UnlockableDef instead. This method may be removed from future releases.", false)] [Server] public void GrantUnlockToAllParticipatingPlayers(string unlockableName) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::GrantUnlockToAllParticipatingPlayers(System.String)' called on client"); } else { GrantUnlockToAllParticipatingPlayers(UnlockableCatalog.GetUnlockableDef(unlockableName)); } } [Server] public void GrantUnlockToAllParticipatingPlayers(UnlockableDef unlockableDef) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::GrantUnlockToAllParticipatingPlayers(RoR2.UnlockableDef)' called on client"); } else { if (!unlockableDef || unlockableDef.index == UnlockableIndex.None || unlockablesAlreadyFullyObtained.Contains(unlockableDef)) { return; } unlockablesAlreadyFullyObtained.Add(unlockableDef); foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (readOnlyInstances.isParticipating) { readOnlyInstances.ServerHandleUnlock(unlockableDef); } } } } [Server] [Obsolete("Use the overload that accepts an UnlockableDef instead. This method may be removed from future releases.", false)] public void GrantUnlockToSinglePlayer(string unlockableName, CharacterBody body) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::GrantUnlockToSinglePlayer(System.String,RoR2.CharacterBody)' called on client"); } else { GrantUnlockToSinglePlayer(UnlockableCatalog.GetUnlockableDef(unlockableName), body); } } [Server] public void GrantUnlockToSinglePlayer(UnlockableDef unlockableDef, CharacterBody body) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::GrantUnlockToSinglePlayer(RoR2.UnlockableDef,RoR2.CharacterBody)' called on client"); } else if ((bool)unlockableDef && unlockableDef.index != UnlockableIndex.None && (bool)body) { NetworkUser networkUser = Util.LookUpBodyNetworkUser(body); if ((bool)networkUser) { networkUser.ServerHandleUnlock(unlockableDef); } } } [Obsolete("Use the overload that accepts an UnlockableDef instead. This method may be removed from future releases.", false)] [Server] public bool IsUnlockableUnlocked(string unlockableName) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::IsUnlockableUnlocked(System.String)' called on client"); return false; } return IsUnlockableUnlocked(UnlockableCatalog.GetUnlockableDef(unlockableName)); } [Server] public virtual bool IsUnlockableUnlocked(UnlockableDef unlockableDef) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::IsUnlockableUnlocked(RoR2.UnlockableDef)' called on client"); return false; } return unlockablesUnlockedByAnyUser.Contains(unlockableDef); } [Server] [Obsolete("Use the overload that accepts an UnlockableDef instead. This method may be removed from future releases.", false)] public bool DoesEveryoneHaveThisUnlockableUnlocked(string unlockableName) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::DoesEveryoneHaveThisUnlockableUnlocked(System.String)' called on client"); return false; } return DoesEveryoneHaveThisUnlockableUnlocked(UnlockableCatalog.GetUnlockableDef(unlockableName)); } [Server] public virtual bool DoesEveryoneHaveThisUnlockableUnlocked(UnlockableDef unlockableDef) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::DoesEveryoneHaveThisUnlockableUnlocked(RoR2.UnlockableDef)' called on client"); return false; } return unlockablesUnlockedByAllUsers.Contains(unlockableDef); } [Server] [Obsolete("Use the overload that accepts an UnlockableDef instead. This method may be removed from future releases.", false)] public void ForceUnlockImmediate(string unlockableName) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::ForceUnlockImmediate(System.String)' called on client"); } else { ForceUnlockImmediate(UnlockableCatalog.GetUnlockableDef(unlockableName)); } } [Server] public void ForceUnlockImmediate(UnlockableDef unlockableDef) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::ForceUnlockImmediate(RoR2.UnlockableDef)' called on client"); } else { unlockablesUnlockedByAnyUser.Add(unlockableDef); } } public void PickNextStageSceneFromCurrentSceneDestinations() { WeightedSelection weightedSelection = new WeightedSelection(); SceneCatalog.mostRecentSceneDef.AddDestinationsToWeightedSelection(weightedSelection, CanPickStage); PickNextStageScene(weightedSelection); } public bool CanPickStage(SceneDef sceneDef) { if ((bool)sceneDef.requiredExpansion) { return IsExpansionEnabled(sceneDef.requiredExpansion); } return true; } public void PickNextStageScene(WeightedSelection choices) { if (choices.Count != 0) { if (ruleBook.stageOrder == StageOrder.Normal) { nextStageScene = choices.Evaluate(nextStageRng.nextNormalizedFloat); return; } SceneDef[] array = SceneCatalog.allStageSceneDefs.Where(IsValidNextStage).ToArray(); nextStageScene = nextStageRng.NextElementUniform(array); } bool IsValidNextStage(SceneDef sceneDef) { if (nextStageScene != null && nextStageScene.baseSceneName == sceneDef.baseSceneName) { return false; } if (!sceneDef.hasAnyDestinations) { return false; } if (stageClearCount == 0 && blacklistedScenesForFirstScene.Contains(sceneDef)) { return false; } return sceneDef.validForRandomSelection; } } public virtual ulong GenerateSeedForNewRun() { return RoR2Application.rng.nextUlong; } [Server] public void RefreshUnlockAvailability() { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::RefreshUnlockAvailability()' called on client"); } else { BuildUnlockAvailability(); } } protected virtual void BuildUnlockAvailability() { unlockablesUnlockedByAnyUser.Clear(); unlockablesUnlockedByAllUsers.Clear(); unlockablesAlreadyFullyObtained.Clear(); int num = 0; Dictionary dictionary = new Dictionary(); foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (!readOnlyInstances.isParticipating) { continue; } num++; foreach (UnlockableDef unlockable in readOnlyInstances.unlockables) { unlockablesUnlockedByAnyUser.Add(unlockable); if (!dictionary.ContainsKey(unlockable)) { dictionary.Add(unlockable, 0); } UnlockableDef key = unlockable; int value = dictionary[key] + 1; dictionary[key] = value; } } if (num <= 0) { return; } foreach (KeyValuePair item in dictionary) { if (item.Value == num) { unlockablesUnlockedByAllUsers.Add(item.Key); unlockablesAlreadyFullyObtained.Add(item.Key); } } } protected virtual void Start() { InitializeTimeStamps(); OnRuleBookUpdated(networkRuleBookComponent); if (NetworkServer.active) { runRNG = new Xoroshiro128Plus(seed); nextStageRng = new Xoroshiro128Plus(runRNG.nextUlong); stageRngGenerator = new Xoroshiro128Plus(runRNG.nextUlong); GenerateStageRNG(); } allowNewParticipants = true; UnityEngine.Object.DontDestroyOnLoad(base.gameObject); ReadOnlyCollection readOnlyInstancesList = NetworkUser.readOnlyInstancesList; for (int i = 0; i < readOnlyInstancesList.Count; i++) { OnUserAdded(readOnlyInstancesList[i]); } allowNewParticipants = false; if (NetworkServer.active) { WeightedSelection weightedSelection = new WeightedSelection(); string @string = cvRunSceneOverride.GetString(); if (@string != "") { weightedSelection.AddChoice(SceneCatalog.GetSceneDefFromSceneName(@string), 1f); } else if ((bool)startingSceneGroup) { startingSceneGroup.AddToWeightedSelection(weightedSelection, CanPickStage); } else { for (int j = 0; j < startingScenes.Length; j++) { if (CanPickStage(startingScenes[j])) { weightedSelection.AddChoice(startingScenes[j], 1f); } } } PickNextStageScene(weightedSelection); if (!nextStageScene) { Debug.LogError("Cannot set next scene. nextStageScene is null!"); } NetworkManager.singleton.ServerChangeScene(nextStageScene.cachedName); } BuildUnlockAvailability(); BuildDropTable(); isRunning = true; Run.onRunStartGlobal?.Invoke(this); if (NetworkServer.active && RunArtifactManager.instance.IsArtifactEnabled(DLC2Content.Artifacts.Rebirth)) { ServerGiveRebirthItems(); } if (!NetworkClient.active || !RunArtifactManager.instance.IsArtifactEnabled(DLC2Content.Artifacts.Rebirth)) { return; } foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { if (readOnlyInstances.isLocalPlayer) { readOnlyInstances.localUser.userProfile.RebirthItem = null; break; } } } protected virtual void OnDestroy() { Run.onRunDestroyGlobal?.Invoke(this); if ((bool)GameOverController.instance) { UnityEngine.Object.Destroy(GameOverController.instance.gameObject); } ReadOnlyCollection readOnlyInstancesList = CharacterBody.readOnlyInstancesList; for (int num = readOnlyInstancesList.Count - 1; num >= 0; num--) { if ((bool)readOnlyInstancesList[num]) { UnityEngine.Object.Destroy(readOnlyInstancesList[num].gameObject); } } ReadOnlyCollection readOnlyInstancesList2 = CharacterMaster.readOnlyInstancesList; for (int num2 = readOnlyInstancesList2.Count - 1; num2 >= 0; num2--) { if ((bool)readOnlyInstancesList2[num2]) { UnityEngine.Object.Destroy(readOnlyInstancesList2[num2].gameObject); } } if ((bool)Stage.instance) { UnityEngine.Object.Destroy(Stage.instance.gameObject); } Chat.Clear(); if (!shutdown && PlatformSystems.networkManager.isNetworkActive) { HandlePostRunDestination(); } ItemMask.Return(availableItems); ItemMask.Return(expansionLockedItems); EquipmentMask.Return(availableEquipment); EquipmentMask.Return(expansionLockedEquipment); PlatformSystems.saveSystem.SaveLoggedInUserProfiles(); } [Server] private void ServerGiveRebirthItems() { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::ServerGiveRebirthItems()' called on client"); } else { if (!NetworkServer.active) { return; } ItemIndex itemIndex = ItemIndex.None; foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances) { Inventory inventory = instance.master.inventory; NetworkUser networkUser = instance.networkUser; if (networkUser.rebirthItem != ItemIndex.None) { if ((bool)inventory) { itemIndex = networkUser.rebirthItem; networkUser.NetworkrebirthItem = ItemIndex.None; } } else if (rebirthDropTable != null) { Xoroshiro128Plus rng = new Xoroshiro128Plus(Run.instance.treasureRng.nextUlong); itemIndex = PickupCatalog.GetPickupDef(rebirthDropTable.GenerateDrop(rng)).itemIndex; } if ((bool)inventory) { inventory.GiveItem(itemIndex); } } } } protected virtual void HandlePostRunDestination() { if (NetworkServer.active) { NetworkManager.singleton.ServerChangeScene("lobby"); } } protected void OnApplicationQuit() { shutdown = true; } [Server] public CharacterMaster GetUserMaster(NetworkUserId networkUserId) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'RoR2.CharacterMaster RoR2.Run::GetUserMaster(RoR2.NetworkUserId)' called on client"); return null; } userMasters.TryGetValue(networkUserId, out var value); return value; } [Server] public void OnServerSceneChanged(string sceneName) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::OnServerSceneChanged(System.String)' called on client"); return; } BeginStage(); isGameOverServer = false; } [Server] private void BeginStage() { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::BeginStage()' called on client"); } else { NetworkServer.Spawn(UnityEngine.Object.Instantiate(LegacyResourcesAPI.Load("Prefabs/Stage"))); } } [Server] private void EndStage() { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::EndStage()' called on client"); } else if ((bool)Stage.instance) { UnityEngine.Object.Destroy(Stage.instance); } } public void OnUserAdded(NetworkUser user) { if (NetworkServer.active) { SetupUserCharacterMaster(user); } } public void OnUserRemoved(NetworkUser user) { } public virtual bool ShouldAllowNewParticipant(NetworkUser user) { if (!allowNewParticipants) { return (bool)ruleBook.GetRuleChoice(RuleCatalog.FindRuleDef("Misc.AllowDropIn"))?.extraData; } return true; } [Server] private void SetupUserCharacterMaster(NetworkUser user) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::SetupUserCharacterMaster(RoR2.NetworkUser)' called on client"); } else if (!user) { Debug.LogError("SetupUserCharacterMaster user is null"); } else { if ((bool)user.masterObject) { return; } CharacterMaster characterMaster = GetUserMaster(user.id); bool flag = !characterMaster && ShouldAllowNewParticipant(user); if (flag) { characterMaster = UnityEngine.Object.Instantiate(LegacyResourcesAPI.Load("Prefabs/CharacterMasters/CommandoMaster"), Vector3.zero, Quaternion.identity).GetComponent(); if ((bool)characterMaster) { userMasters[user.id] = characterMaster; if (ruleBook != null) { characterMaster.GiveMoney(ruleBook.startingMoney); } else { Debug.LogError("Can't give new master money because there's no rulebook"); } DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(selectedDifficulty); if ((bool)characterMaster.inventory) { if (selectedDifficulty == DifficultyIndex.Easy) { characterMaster.inventory.GiveItem(RoR2Content.Items.DrizzlePlayerHelper); } else if (difficultyDef.countsAsHardMode) { characterMaster.inventory.GiveItem(RoR2Content.Items.MonsoonPlayerHelper); } } else { Debug.LogError("Instantiated master has no inventory"); } NetworkServer.Spawn(characterMaster.gameObject); } else { Debug.LogError("Instantiated master object has no CharacterMaster"); } } if ((bool)characterMaster) { PlayerCharacterMasterController component = characterMaster.GetComponent(); if ((bool)component) { component.LinkToNetworkUserServer(user); } else { Debug.LogError("CharacterMaster has no PlayerCharacterMasterController"); } if (flag) { Run.onPlayerFirstCreatedServer?.Invoke(this, component); } } } } [Server] public virtual void HandlePlayerFirstEntryAnimation(CharacterBody body, Vector3 spawnPosition, Quaternion spawnRotation) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::HandlePlayerFirstEntryAnimation(RoR2.CharacterBody,UnityEngine.Vector3,UnityEngine.Quaternion)' called on client"); } else if ((bool)body) { if ((bool)body.preferredPodPrefab) { GameObject gameObject = UnityEngine.Object.Instantiate(body.preferredPodPrefab, body.transform.position, spawnRotation); VehicleSeat component = gameObject.GetComponent(); if ((bool)component) { component.AssignPassenger(body.gameObject); } else { Debug.LogError("Body " + body.name + " has preferred pod " + body.preferredPodPrefab.name + ", but that object has no VehicleSeat."); } NetworkServer.Spawn(gameObject); } else { body.SetBodyStateToPreferredInitialState(); } } else { Debug.LogError("Can't handle player first entry animation for null body."); } } public virtual void OnServerBossAdded(BossGroup bossGroup, CharacterMaster characterMaster) { } public virtual void OnServerBossDefeated(BossGroup bossGroup) { } public virtual void OnServerCharacterBodySpawned(CharacterBody characterBody) { } public virtual void OnServerTeleporterPlaced(SceneDirector sceneDirector, GameObject teleporter) { } public virtual void OnPlayerSpawnPointsPlaced(SceneDirector sceneDirector) { } public virtual GameObject GetTeleportEffectPrefab(GameObject objectToTeleport) { return LegacyResourcesAPI.Load("Prefabs/Effects/TeleportOutBoom"); } public int GetDifficultyScaledCost(int baseCost, float difficultyCoefficient) { return (int)((float)baseCost * Mathf.Pow(difficultyCoefficient, 1.25f)); } public int GetDifficultyScaledCost(int baseCost) { return GetDifficultyScaledCost(baseCost, instance.difficultyCoefficient); } public void BuildDropTable() { availableTier1DropList.Clear(); availableTier2DropList.Clear(); availableTier3DropList.Clear(); availableLunarItemDropList.Clear(); availableEquipmentDropList.Clear(); availableBossDropList.Clear(); availableLunarEquipmentDropList.Clear(); availableVoidTier1DropList.Clear(); availableVoidTier2DropList.Clear(); availableVoidTier3DropList.Clear(); availableVoidBossDropList.Clear(); ItemIndex itemIndex = ItemIndex.Count; for (ItemIndex itemCount = (ItemIndex)ItemCatalog.itemCount; itemIndex < itemCount; itemIndex++) { if (availableItems.Contains(itemIndex)) { ItemDef itemDef = ItemCatalog.GetItemDef(itemIndex); List list = null; switch (itemDef.tier) { case ItemTier.Tier1: list = availableTier1DropList; break; case ItemTier.Tier2: list = availableTier2DropList; break; case ItemTier.Tier3: list = availableTier3DropList; break; case ItemTier.Lunar: list = availableLunarItemDropList; break; case ItemTier.Boss: list = availableBossDropList; break; case ItemTier.VoidTier1: list = availableVoidTier1DropList; break; case ItemTier.VoidTier2: list = availableVoidTier2DropList; break; case ItemTier.VoidTier3: list = availableVoidTier3DropList; break; case ItemTier.VoidBoss: list = availableVoidBossDropList; break; } if (list != null && itemDef.DoesNotContainTag(ItemTag.WorldUnique)) { list.Add(PickupCatalog.FindPickupIndex(itemIndex)); } } } EquipmentIndex equipmentIndex = (EquipmentIndex)0; for (EquipmentIndex equipmentCount = (EquipmentIndex)EquipmentCatalog.equipmentCount; equipmentIndex < equipmentCount; equipmentIndex++) { if (!availableEquipment.Contains(equipmentIndex)) { continue; } EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(equipmentIndex); if (equipmentDef.canDrop) { if (!equipmentDef.isLunar) { availableEquipmentDropList.Add(PickupCatalog.FindPickupIndex(equipmentIndex)); } else { availableLunarEquipmentDropList.Add(PickupCatalog.FindPickupIndex(equipmentIndex)); } } } smallChestDropTierSelector.Clear(); smallChestDropTierSelector.AddChoice(availableTier1DropList, 0.8f); smallChestDropTierSelector.AddChoice(availableTier2DropList, 0.2f); smallChestDropTierSelector.AddChoice(availableTier3DropList, 0.01f); mediumChestDropTierSelector.Clear(); mediumChestDropTierSelector.AddChoice(availableTier2DropList, 0.8f); mediumChestDropTierSelector.AddChoice(availableTier3DropList, 0.2f); largeChestDropTierSelector.Clear(); RefreshLunarCombinedDropList(); } public bool IsItemAvailable(ItemIndex itemIndex) { return availableItems.Contains(itemIndex); } public bool IsEquipmentAvailable(EquipmentIndex equipmentIndex) { return availableEquipment.Contains(equipmentIndex); } public bool IsItemExpansionLocked(ItemIndex itemIndex) { return expansionLockedItems.Contains(itemIndex); } public bool IsEquipmentExpansionLocked(EquipmentIndex equipmentIndex) { return expansionLockedEquipment.Contains(equipmentIndex); } public bool IsPickupAvailable(PickupIndex pickupIndex) { PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef.itemIndex != ItemIndex.None) { return IsItemAvailable(pickupDef.itemIndex); } if (pickupDef.equipmentIndex != EquipmentIndex.None) { return IsEquipmentAvailable(pickupDef.equipmentIndex); } return true; } [Server] public void DisablePickupDrop(PickupIndex pickupIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::DisablePickupDrop(RoR2.PickupIndex)' called on client"); return; } PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef.itemIndex != ItemIndex.None) { DisableItemDrop(pickupDef.itemIndex); } if (pickupDef.equipmentIndex != EquipmentIndex.None) { DisableEquipmentDrop(pickupDef.equipmentIndex); } } [Server] public void DisableItemDrop(ItemIndex itemIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::DisableItemDrop(RoR2.ItemIndex)' called on client"); return; } ItemDef itemDef = ItemCatalog.GetItemDef(itemIndex); List list = null; bool flag = false; switch (itemDef.tier) { case ItemTier.Tier1: list = availableTier1DropList; break; case ItemTier.Tier2: list = availableTier2DropList; break; case ItemTier.Tier3: list = availableTier3DropList; break; case ItemTier.Lunar: list = availableLunarItemDropList; break; case ItemTier.Boss: list = availableBossDropList; break; case ItemTier.VoidTier1: list = availableVoidTier1DropList; break; case ItemTier.VoidTier2: list = availableVoidTier2DropList; break; case ItemTier.VoidTier3: list = availableVoidTier3DropList; break; case ItemTier.VoidBoss: list = availableVoidBossDropList; break; } PickupIndex pickupIndex = PickupCatalog.FindPickupIndex(itemIndex); if (list != null && pickupIndex != PickupIndex.none) { list.Remove(pickupIndex); if (flag) { RefreshLunarCombinedDropList(); } Run.onAvailablePickupsModified?.Invoke(this); } } [Server] public void DisableEquipmentDrop(EquipmentIndex equipmentIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::DisableEquipmentDrop(RoR2.EquipmentIndex)' called on client"); return; } PickupIndex pickupIndex = PickupCatalog.FindPickupIndex(equipmentIndex); if (!(pickupIndex != PickupIndex.none)) { return; } List list = null; bool flag = false; if (PickupCatalog.GetPickupDef(pickupIndex).isLunar) { flag = true; list = availableLunarEquipmentDropList; } else { list = availableEquipmentDropList; } if (list.Contains(pickupIndex)) { list.Remove(pickupIndex); if (flag) { RefreshLunarCombinedDropList(); } Run.onAvailablePickupsModified?.Invoke(this); } } [Server] public void EnablePickupDrop(PickupIndex pickupIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::EnablePickupDrop(RoR2.PickupIndex)' called on client"); return; } PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef.itemIndex != ItemIndex.None) { EnableItemDrop(pickupDef.itemIndex); } if (pickupDef.equipmentIndex != EquipmentIndex.None) { EnableEquipmentDrop(pickupDef.equipmentIndex); } } [Server] public void EnableItemDrop(ItemIndex itemIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::EnableItemDrop(RoR2.ItemIndex)' called on client"); return; } ItemDef itemDef = ItemCatalog.GetItemDef(itemIndex); List list = null; bool flag = false; switch (itemDef.tier) { case ItemTier.Tier1: list = availableTier1DropList; break; case ItemTier.Tier2: list = availableTier2DropList; break; case ItemTier.Tier3: list = availableTier3DropList; break; case ItemTier.Lunar: list = availableLunarItemDropList; break; case ItemTier.Boss: list = availableBossDropList; break; case ItemTier.VoidTier1: list = availableVoidTier1DropList; break; case ItemTier.VoidTier2: list = availableVoidTier2DropList; break; case ItemTier.VoidTier3: list = availableVoidTier3DropList; break; case ItemTier.VoidBoss: list = availableVoidBossDropList; break; } PickupIndex pickupIndex = PickupCatalog.FindPickupIndex(itemIndex); if (list != null && pickupIndex != PickupIndex.none && !list.Contains(pickupIndex)) { list.Add(pickupIndex); if (flag) { RefreshLunarCombinedDropList(); } Run.onAvailablePickupsModified?.Invoke(this); } } [Server] public void EnableEquipmentDrop(EquipmentIndex equipmentIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::EnableEquipmentDrop(RoR2.EquipmentIndex)' called on client"); return; } PickupIndex pickupIndex = PickupCatalog.FindPickupIndex(equipmentIndex); if (!(pickupIndex != PickupIndex.none)) { return; } List list = null; bool flag = false; list = ((!PickupCatalog.GetPickupDef(pickupIndex).isLunar) ? availableEquipmentDropList : availableLunarEquipmentDropList); if (!list.Contains(pickupIndex)) { list.Add(pickupIndex); if (flag) { RefreshLunarCombinedDropList(); } Run.onAvailablePickupsModified?.Invoke(this); } } [Server] private void RefreshLunarCombinedDropList() { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::RefreshLunarCombinedDropList()' called on client"); return; } availableLunarCombinedDropList.Clear(); availableLunarCombinedDropList.AddRange(availableLunarEquipmentDropList); availableLunarCombinedDropList.AddRange(availableLunarItemDropList); } [ConCommand(commandName = "run_end", flags = ConVarFlags.SenderMustBeServer, helpText = "Ends the current run.")] private static void CCRunEnd(ConCommandArgs args) { if ((bool)instance) { UnityEngine.Object.Destroy(instance.gameObject); } } [ConCommand(commandName = "run_print_unlockables", flags = ConVarFlags.SenderMustBeServer, helpText = "Prints all unlockables available in this run.")] private static void CCRunPrintUnlockables(ConCommandArgs args) { if (!instance) { throw new ConCommandException("No run is currently in progress."); } List list = new List(); foreach (UnlockableDef item in instance.unlockablesUnlockedByAnyUser) { list.Add(item.cachedName); } } [ConCommand(commandName = "run_print_seed", flags = ConVarFlags.None, helpText = "Prints the seed of the current run.")] private static void CCRunPrintSeed(ConCommandArgs args) { if (!instance) { throw new ConCommandException("No run is currently in progress."); } Debug.LogFormat("Current run seed: {0}", instance.seed); } [ConCommand(commandName = "run_set_stages_cleared", flags = (ConVarFlags.ExecuteOnServer | ConVarFlags.Cheat), helpText = "Sets the current number of stages cleared in the run.")] private static void CCRunSetStagesCleared(ConCommandArgs args) { if (!instance) { throw new ConCommandException("No run is currently in progress."); } instance.NetworkstageClearCount = args.GetArgInt(0); } public virtual void AdvanceStage(SceneDef nextScene) { if ((bool)Stage.instance) { Stage.instance.CompleteServer(); SceneType sceneType = SceneCatalog.GetSceneDefForCurrentScene().sceneType; if (sceneType == SceneType.Stage || sceneType == SceneType.UntimedStage) { NetworkstageClearCount = stageClearCount + 1; } } GenerateStageRNG(); RecalculateDifficultyCoefficent(); NetworkManager.singleton.ServerChangeScene(nextScene.cachedName); } public void BeginGameOver([NotNull] GameEndingDef gameEndingDef) { if (isGameOverServer) { return; } if ((bool)Stage.instance && gameEndingDef.isWin) { Stage.instance.CompleteServer(); } isGameOverServer = true; if (gameEndingDef.lunarCoinReward != 0) { for (int i = 0; i < NetworkUser.readOnlyInstancesList.Count; i++) { NetworkUser networkUser = NetworkUser.readOnlyInstancesList[i]; if ((bool)networkUser && networkUser.isParticipating) { networkUser.AwardLunarCoins(gameEndingDef.lunarCoinReward); } } } StatManager.ForceUpdate(); GameObject obj = UnityEngine.Object.Instantiate(gameOverPrefab); GameOverController component = obj.GetComponent(); component.SetRunReport(RunReport.Generate(this, gameEndingDef)); Run.onServerGameOver?.Invoke(this, gameEndingDef); NetworkServer.Spawn(obj); isRunning = false; component.CallRpcClientGameOver(); } public virtual void OnClientGameOver(RunReport runReport) { isRunning = false; Run.onClientGameOverGlobal?.Invoke(this, runReport); } public virtual void OverrideRuleChoices(RuleChoiceMask mustInclude, RuleChoiceMask mustExclude, ulong runSeed) { ForceChoice(mustInclude, mustExclude, "Misc.KeepMoneyBetweenStages.Off"); } protected void ForceChoice(RuleChoiceMask mustInclude, RuleChoiceMask mustExclude, RuleChoiceDef choiceDef) { foreach (RuleChoiceDef choice in choiceDef.ruleDef.choices) { mustInclude[choice.globalIndex] = false; mustExclude[choice.globalIndex] = true; } mustInclude[choiceDef.globalIndex] = true; mustExclude[choiceDef.globalIndex] = false; } protected void ForceChoice(RuleChoiceMask mustInclude, RuleChoiceMask mustExclude, string choiceDefGlobalName) { ForceChoice(mustInclude, mustExclude, RuleCatalog.FindChoiceDef(choiceDefGlobalName)); } public virtual Vector3 FindSafeTeleportPosition(CharacterBody characterBody, Transform targetDestination) { return FindSafeTeleportPosition(characterBody, targetDestination, float.NegativeInfinity, float.NegativeInfinity); } public virtual Vector3 FindSafeTeleportPosition(CharacterBody characterBody, Transform targetDestination, float idealMinDistance, float idealMaxDistance) { Vector3 vector = (targetDestination ? targetDestination.position : characterBody.transform.position); SpawnCard spawnCard = ScriptableObject.CreateInstance(); spawnCard.hullSize = characterBody.hullClassification; spawnCard.nodeGraphType = MapNodeGroup.GraphType.Ground; spawnCard.prefab = LegacyResourcesAPI.Load("SpawnCards/HelperPrefab"); Vector3 result = vector; GameObject gameObject = null; if (idealMaxDistance > 0f && idealMinDistance < idealMaxDistance) { gameObject = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(spawnCard, new DirectorPlacementRule { placementMode = DirectorPlacementRule.PlacementMode.Approximate, minDistance = idealMinDistance, maxDistance = idealMaxDistance, position = vector }, RoR2Application.rng)); } if (!gameObject) { gameObject = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(spawnCard, new DirectorPlacementRule { placementMode = DirectorPlacementRule.PlacementMode.NearestNode, position = vector }, RoR2Application.rng)); if ((bool)gameObject) { result = gameObject.transform.position; } } if ((bool)gameObject) { UnityEngine.Object.Destroy(gameObject); } UnityEngine.Object.Destroy(spawnCard); return result; } [Server] public void SetEventFlag(string name) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::SetEventFlag(System.String)' called on client"); } else { eventFlags.Add(name); } } [Server] public bool GetEventFlag(string name) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Boolean RoR2.Run::GetEventFlag(System.String)' called on client"); return false; } return eventFlags.Contains(name); } [Server] public void ResetEventFlag(string name) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.Run::ResetEventFlag(System.String)' called on client"); } else { eventFlags.Remove(name); } } public virtual bool ShouldAllowNonChampionBossSpawn() { return stageClearCount > 0; } public bool IsExpansionEnabled([NotNull] ExpansionDef expansionDef) { if (expansionDef.enabledChoice != null) { return ruleBook.IsChoiceActive(expansionDef.enabledChoice); } return false; } protected virtual void OnSeedSet() { } private void UNetVersion() { } public override bool OnSerialize(NetworkWriter writer, bool forceAll) { if (forceAll) { GeneratedNetworkCode._WriteNetworkGuid_None(writer, _uniqueId); GeneratedNetworkCode._WriteNetworkDateTime_None(writer, startTimeUtc); writer.Write(fixedTime); GeneratedNetworkCode._WriteRunStopwatch_Run(writer, runStopwatch); writer.WritePackedUInt32((uint)stageClearCount); writer.WritePackedUInt32((uint)selectedDifficultyInternal); return true; } bool flag = false; if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } GeneratedNetworkCode._WriteNetworkGuid_None(writer, _uniqueId); } if ((base.syncVarDirtyBits & 2u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } GeneratedNetworkCode._WriteNetworkDateTime_None(writer, startTimeUtc); } if ((base.syncVarDirtyBits & 4u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(fixedTime); } if ((base.syncVarDirtyBits & 8u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } GeneratedNetworkCode._WriteRunStopwatch_Run(writer, runStopwatch); } if ((base.syncVarDirtyBits & 0x10u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.WritePackedUInt32((uint)stageClearCount); } if ((base.syncVarDirtyBits & 0x20u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.WritePackedUInt32((uint)selectedDifficultyInternal); } if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); } return flag; } public override void OnDeserialize(NetworkReader reader, bool initialState) { if (initialState) { _uniqueId = GeneratedNetworkCode._ReadNetworkGuid_None(reader); startTimeUtc = GeneratedNetworkCode._ReadNetworkDateTime_None(reader); fixedTime = reader.ReadSingle(); runStopwatch = GeneratedNetworkCode._ReadRunStopwatch_Run(reader); stageClearCount = (int)reader.ReadPackedUInt32(); selectedDifficultyInternal = (int)reader.ReadPackedUInt32(); return; } int num = (int)reader.ReadPackedUInt32(); if (((uint)num & (true ? 1u : 0u)) != 0) { _uniqueId = GeneratedNetworkCode._ReadNetworkGuid_None(reader); } if (((uint)num & 2u) != 0) { startTimeUtc = GeneratedNetworkCode._ReadNetworkDateTime_None(reader); } if (((uint)num & 4u) != 0) { fixedTime = reader.ReadSingle(); } if (((uint)num & 8u) != 0) { runStopwatch = GeneratedNetworkCode._ReadRunStopwatch_Run(reader); } if (((uint)num & 0x10u) != 0) { stageClearCount = (int)reader.ReadPackedUInt32(); } if (((uint)num & 0x20u) != 0) { selectedDifficultyInternal = (int)reader.ReadPackedUInt32(); } } public override void PreStartClient() { } }