r2mods/ilspy_dump/ror2_csproj/RoR2/SceneDirector.cs

443 lines
14 KiB
C#
Raw Normal View History

2024-10-04 07:26:37 +00:00
using System;
using System.Collections.Generic;
using RoR2.Navigation;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Networking;
namespace RoR2;
public class SceneDirector : MonoBehaviour
{
private struct NodeDistanceSqrPair
{
public NodeGraph.NodeIndex nodeIndex;
public float distanceSqr;
}
public delegate void GenerateSpawnPointsDelegate(SceneDirector sceneDirector, ref Action generationMethod);
public SpawnCard teleporterSpawnCard;
public float expRewardCoefficient;
public float eliteBias;
public float spawnDistanceMultiplier;
private int monsterCredit;
public GameObject teleporterInstance;
private Xoroshiro128Plus rng;
public DirectorCard lumerianEgg;
private InteractableSpawnCard halcyonShrine;
private static readonly WeightedSelection<DirectorCard> cardSelector = new WeightedSelection<DirectorCard>();
public int interactableCredit { get; set; }
public float onPopulateCreditMultiplier { get; set; } = 1f;
public static event Action<SceneDirector, DirectorCardCategorySelection> onGenerateInteractableCardSelection;
public static event GenerateSpawnPointsDelegate onPreGeneratePlayerSpawnPointsServer;
public static event Action<SceneDirector> onPrePopulateSceneServer;
public static event Action<SceneDirector> onPrePopulateMonstersSceneServer;
public static event Action<SceneDirector> onPostPopulateSceneServer;
private void Start()
{
if (!NetworkServer.active)
{
return;
}
rng = new Xoroshiro128Plus(Run.instance.stageRng.nextUint);
float num = 0.5f + (float)Run.instance.participatingPlayerCount * 0.5f;
ClassicStageInfo component = SceneInfo.instance.GetComponent<ClassicStageInfo>();
if ((bool)component)
{
interactableCredit = (int)((float)component.sceneDirectorInteractibleCredits * num);
if (component.bonusInteractibleCreditObjects != null)
{
for (int i = 0; i < component.bonusInteractibleCreditObjects.Length; i++)
{
ClassicStageInfo.BonusInteractibleCreditObject bonusInteractibleCreditObject = component.bonusInteractibleCreditObjects[i];
if ((bool)bonusInteractibleCreditObject.objectThatGrantsPointsIfEnabled && bonusInteractibleCreditObject.objectThatGrantsPointsIfEnabled.activeSelf)
{
interactableCredit += bonusInteractibleCreditObject.points;
}
}
}
Debug.LogFormat("Spending {0} credits on interactables...", interactableCredit);
monsterCredit = (int)((float)component.sceneDirectorMonsterCredits * Run.instance.difficultyCoefficient);
}
halcyonShrine = Addressables.LoadAssetAsync<InteractableSpawnCard>("RoR2/DLC2/iscShrineHalcyoniteTier1.asset").WaitForCompletion();
SceneDirector.onPrePopulateSceneServer?.Invoke(this);
PopulateScene();
SceneDirector.onPostPopulateSceneServer?.Invoke(this);
}
private void PlaceTeleporter()
{
if (!teleporterInstance && (bool)teleporterSpawnCard)
{
teleporterInstance = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(teleporterSpawnCard, new DirectorPlacementRule
{
placementMode = DirectorPlacementRule.PlacementMode.Random
}, rng));
Run.instance.OnServerTeleporterPlaced(this, teleporterInstance);
}
}
private static bool IsNodeSuitableForPod(NodeGraph nodeGraph, NodeGraph.NodeIndex nodeIndex)
{
if (nodeGraph.GetNodeFlags(nodeIndex, out var flags) && (flags & NodeFlags.NoCeiling) != 0)
{
return true;
}
return false;
}
private void PlacePlayerSpawnsViaNodegraph()
{
bool usePod = Stage.instance.usePod;
NodeGraph groundNodes = SceneInfo.instance.groundNodes;
NodeFlags requiredFlags = NodeFlags.None;
NodeFlags nodeFlags = NodeFlags.None;
nodeFlags |= NodeFlags.NoCharacterSpawn;
List<NodeGraph.NodeIndex> list = groundNodes.GetActiveNodesForHullMaskWithFlagConditions(HullMask.Golem, requiredFlags, nodeFlags);
if (usePod)
{
int num = list.Count - 1;
while (num >= 0 && list.Count > 1)
{
if (!IsNodeSuitableForPod(groundNodes, list[num]))
{
list.RemoveAt(num);
}
num--;
}
}
if (PlayerSpawnInhibitor.readOnlyInstancesList.Count > 0)
{
List<NodeGraph.NodeIndex> list2 = new List<NodeGraph.NodeIndex>();
for (int i = 0; i < list.Count; i++)
{
bool flag = false;
foreach (PlayerSpawnInhibitor readOnlyInstances in PlayerSpawnInhibitor.readOnlyInstancesList)
{
if (readOnlyInstances.IsInhibiting(groundNodes, list[i]))
{
flag = true;
break;
}
}
if (!flag)
{
list2.Add(list[i]);
}
}
if (list2.Count > 0)
{
list = list2;
}
}
NodeGraph.NodeIndex nodeIndex;
if ((bool)teleporterInstance)
{
Vector3 position = teleporterInstance.transform.position;
List<NodeDistanceSqrPair> list3 = new List<NodeDistanceSqrPair>();
for (int j = 0; j < list.Count; j++)
{
groundNodes.GetNodePosition(list[j], out var position2);
list3.Add(new NodeDistanceSqrPair
{
nodeIndex = list[j],
distanceSqr = (position - position2).sqrMagnitude
});
}
list3.Sort((NodeDistanceSqrPair a, NodeDistanceSqrPair b) => a.distanceSqr.CompareTo(b.distanceSqr));
int index = rng.RangeInt(list3.Count * 3 / 4, list3.Count);
nodeIndex = list3[index].nodeIndex;
}
else
{
nodeIndex = rng.NextElementUniform(list);
}
NodeGraphSpider nodeGraphSpider = new NodeGraphSpider(groundNodes, HullMask.Human);
nodeGraphSpider.AddNodeForNextStep(nodeIndex);
while (nodeGraphSpider.PerformStep())
{
List<NodeGraphSpider.StepInfo> collectedSteps = nodeGraphSpider.collectedSteps;
for (int num2 = collectedSteps.Count - 1; num2 >= 0; num2--)
{
if ((RoR2Application.maxPlayers <= list.Count && !list.Contains(collectedSteps[num2].node)) || (usePod && !IsNodeSuitableForPod(groundNodes, collectedSteps[num2].node)))
{
collectedSteps.RemoveAt(num2);
}
}
if (collectedSteps.Count >= RoR2Application.maxPlayers)
{
break;
}
}
List<NodeGraphSpider.StepInfo> collectedSteps2 = nodeGraphSpider.collectedSteps;
Util.ShuffleList(collectedSteps2, Run.instance.stageRng);
int num3 = Math.Min(nodeGraphSpider.collectedSteps.Count, RoR2Application.maxPlayers);
for (int k = 0; k < num3; k++)
{
SpawnPoint.AddSpawnPoint(groundNodes, collectedSteps2[k].node, rng);
}
}
private void RemoveAllExistingSpawnPoints()
{
List<SpawnPoint> list = new List<SpawnPoint>(SpawnPoint.readOnlyInstancesList);
for (int i = 0; i < list.Count; i++)
{
UnityEngine.Object.Destroy(list[i].gameObject);
}
}
private void CullExistingSpawnPoints()
{
List<SpawnPoint> list = new List<SpawnPoint>(SpawnPoint.readOnlyInstancesList);
if (!teleporterInstance)
{
return;
}
Vector3 teleporterPosition = teleporterInstance.transform.position;
list.Sort((SpawnPoint a, SpawnPoint b) => (teleporterPosition - a.transform.position).sqrMagnitude.CompareTo((teleporterPosition - b.transform.position).sqrMagnitude));
for (int num = list.Count; num >= 0; num--)
{
if (num < list.Count - RoR2Application.maxPlayers)
{
UnityEngine.Object.Destroy(list[num].gameObject);
}
}
}
private void DefaultPlayerSpawnPointGenerator()
{
bool num = SpawnPoint.readOnlyInstancesList.Count == 0;
bool flag = Run.instance.autoGenerateSpawnPoints && (bool)Stage.instance && !Stage.instance.usePod;
if (num || flag)
{
RemoveAllExistingSpawnPoints();
PlacePlayerSpawnsViaNodegraph();
}
else
{
CullExistingSpawnPoints();
}
}
private WeightedSelection<DirectorCard> GenerateInteractableCardSelection()
{
DirectorCardCategorySelection directorCardCategorySelection = ScriptableObject.CreateInstance<DirectorCardCategorySelection>();
if ((bool)ClassicStageInfo.instance && (bool)ClassicStageInfo.instance.interactableCategories)
{
directorCardCategorySelection.CopyFrom(ClassicStageInfo.instance.interactableCategories);
}
SceneDirector.onGenerateInteractableCardSelection?.Invoke(this, directorCardCategorySelection);
WeightedSelection<DirectorCard> result = directorCardCategorySelection.GenerateDirectorCardWeightedSelection();
UnityEngine.Object.Destroy(directorCardCategorySelection);
return result;
}
private void PopulateScene()
{
Run.instance.RecalculateDifficultyCoefficent();
WeightedSelection<DirectorCard> deck = GenerateInteractableCardSelection();
PlaceTeleporter();
Dictionary<DirectorCard, int> dictionary = new Dictionary<DirectorCard, int>();
interactableCredit = (int)Mathf.Floor((float)interactableCredit * onPopulateCreditMultiplier);
while (interactableCredit > 0)
{
DirectorCard directorCard = SelectCard(deck, interactableCredit);
if (directorCard == null)
{
break;
}
if (!directorCard.IsAvailable())
{
continue;
}
if (RunArtifactManager.instance.IsArtifactEnabled(CU8Content.Artifacts.Devotion) && (directorCard.spawnCard as InteractableSpawnCard).skipSpawnWhenDevotionArtifactEnabled)
{
directorCard = lumerianEgg;
}
if (!dictionary.ContainsKey(directorCard))
{
InteractableSpawnCard interactableSpawnCard = directorCard.spawnCard as InteractableSpawnCard;
if ((bool)interactableSpawnCard)
{
int value = int.MaxValue;
if (interactableSpawnCard.maxSpawnsPerStage >= 0)
{
value = interactableSpawnCard.maxSpawnsPerStage;
}
dictionary[directorCard] = value;
}
}
if (!dictionary.TryGetValue(directorCard, out var value2) || value2 <= 0)
{
continue;
}
dictionary[directorCard] = value2 - 1;
if (Run.instance.isRunWeekly && rng.nextNormalizedFloat > (directorCard.spawnCard as InteractableSpawnCard).prismaticTrialSpawnChance)
{
continue;
}
interactableCredit -= directorCard.cost;
if (!Run.instance)
{
continue;
}
for (int i = 0; i < 10; i++)
{
DirectorPlacementRule placementRule = new DirectorPlacementRule
{
placementMode = DirectorPlacementRule.PlacementMode.Random
};
GameObject gameObject = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(directorCard.spawnCard, placementRule, rng));
if ((bool)gameObject)
{
PurchaseInteraction component = gameObject.GetComponent<PurchaseInteraction>();
if ((bool)component && component.costType == CostTypeIndex.Money)
{
component.Networkcost = Run.instance.GetDifficultyScaledCost(component.cost);
}
break;
}
}
}
Action generationMethod = DefaultPlayerSpawnPointGenerator;
SceneDirector.onPreGeneratePlayerSpawnPointsServer?.Invoke(this, ref generationMethod);
generationMethod?.Invoke();
Run.instance.OnPlayerSpawnPointsPlaced(this);
SceneDirector.onPrePopulateMonstersSceneServer?.Invoke(this);
if ((bool)Run.instance && CombatDirector.cvDirectorCombatDisable.value)
{
monsterCredit = 0;
}
if (!SceneInfo.instance.sceneDef.hasSafeStart)
{
CombatDirector component2 = GetComponent<CombatDirector>();
if ((bool)component2)
{
float num = component2.expRewardCoefficient;
float num2 = component2.eliteBias;
float num3 = component2.spawnDistanceMultiplier;
component2.monsterCredit += monsterCredit;
component2.expRewardCoefficient = expRewardCoefficient;
component2.eliteBias = eliteBias;
component2.spawnDistanceMultiplier = spawnDistanceMultiplier;
monsterCredit = 0;
component2.onSpawnedServer.AddListener(OnMonsterSpawnedServer);
component2.SpendAllCreditsOnMapSpawns(TeleporterInteraction.instance ? TeleporterInteraction.instance.transform : null);
component2.onSpawnedServer.RemoveListener(OnMonsterSpawnedServer);
component2.expRewardCoefficient = num;
component2.eliteBias = num2;
component2.spawnDistanceMultiplier = num3;
}
}
if (!SceneInfo.instance.countsAsStage && !SceneInfo.instance.sceneDef.allowItemsToSpawnObjects)
{
return;
}
Xoroshiro128Plus xoroshiro128Plus = new Xoroshiro128Plus(rng.nextUlong);
int num4 = 0;
foreach (CharacterMaster readOnlyInstances in CharacterMaster.readOnlyInstancesList)
{
if (readOnlyInstances.inventory.GetItemCount(RoR2Content.Items.TreasureCache) > 0)
{
num4++;
}
}
for (int j = 0; j < num4; j++)
{
DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(LegacyResourcesAPI.Load<SpawnCard>("SpawnCards/InteractableSpawnCard/iscLockbox"), new DirectorPlacementRule
{
placementMode = DirectorPlacementRule.PlacementMode.Random
}, xoroshiro128Plus));
}
Xoroshiro128Plus xoroshiro128Plus2 = new Xoroshiro128Plus(rng.nextUlong);
int num5 = 0;
foreach (CharacterMaster readOnlyInstances2 in CharacterMaster.readOnlyInstancesList)
{
if (readOnlyInstances2.inventory.GetItemCount(DLC1Content.Items.TreasureCacheVoid) > 0)
{
num5++;
}
}
for (int k = 0; k < num5; k++)
{
DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(LegacyResourcesAPI.Load<SpawnCard>("SpawnCards/InteractableSpawnCard/iscLockboxVoid"), new DirectorPlacementRule
{
placementMode = DirectorPlacementRule.PlacementMode.Random
}, xoroshiro128Plus2));
}
Xoroshiro128Plus xoroshiro128Plus3 = new Xoroshiro128Plus(rng.nextUlong);
int num6 = 0;
foreach (CharacterMaster readOnlyInstances3 in CharacterMaster.readOnlyInstancesList)
{
if (readOnlyInstances3.inventory.GetItemCount(DLC1Content.Items.FreeChest) > 0)
{
num6++;
}
}
for (int l = 0; l < num6; l++)
{
DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(LegacyResourcesAPI.Load<SpawnCard>("SpawnCards/InteractableSpawnCard/iscFreeChest"), new DirectorPlacementRule
{
placementMode = DirectorPlacementRule.PlacementMode.Random
}, xoroshiro128Plus3));
}
static void OnMonsterSpawnedServer(GameObject masterObject)
{
GameObject bodyObject = masterObject.GetComponent<CharacterMaster>().GetBodyObject();
if ((bool)bodyObject)
{
EntityStateMachine[] components = bodyObject.GetComponents<EntityStateMachine>();
foreach (EntityStateMachine obj in components)
{
obj.initialStateType = obj.mainStateType;
}
}
}
}
private DirectorCard SelectCard(WeightedSelection<DirectorCard> deck, int maxCost)
{
cardSelector.Clear();
int i = 0;
for (int count = deck.Count; i < count; i++)
{
WeightedSelection<DirectorCard>.ChoiceInfo choice = deck.GetChoice(i);
if (choice.value.cost <= maxCost)
{
cardSelector.AddChoice(choice);
}
}
if (cardSelector.Count == 0)
{
return null;
}
return cardSelector.Evaluate(rng.nextNormalizedFloat);
}
public void ReduceMonsterCredits(int creditReduction)
{
monsterCredit = Mathf.Max(0, monsterCredit - creditReduction);
}
}