using Audio; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using static AstarManager; public class ScenarioManagerRebirth { private static ScenarioManagerRebirth instance = null; private static string SaveDataPath = ""; private static string ModPath = ""; private static float prefabUpdateTick = 0.25f; private static float prefabUpdateCheck = 0f; private static float cleanupUpdateTick = 60f; private static float cleanupUpdateCheck = 0f; public static List purgeKills = new List(); public static List ClearedPrefabs = new List(); public static List DiscoveredPrefabs = new List(); public static List dynamicPrefabs = new List(); public static bool dynamicPrefabsLoaded = false; public static bool processingDynamicPrefabs = false; public class DynamicPrefab { public string PrefabName { get; set; } public Vector3 Position { get; set; } public Vector3 Size { get; set; } public int Difficulty { get; set; } public string Biome { get; set; } public string Tags { get; set; } public DynamicPrefab(string prefabName, Vector3 position, Vector3 size, int difficulty, string biome, string tags) { PrefabName = prefabName; Position = position; Size = size; Difficulty = difficulty; Biome = biome; Tags = tags; } } public class PlayerKillInfo { public int PlayerID { get; set; } public int Kills { get; set; } public bool Redeemed { get; set; } } public class KillInfo { public int Kills { get; set; } public bool Redeemed { get; set; } } public class PurgeKills { public string Name { get; set; } public Vector3 Position { get; set; } public Dictionary Players { get; set; } = new Dictionary(); // Player ID as key, Kills as value } public class PrefabInfo { public string Name { get; set; } public Vector3 Position { get; set; } public Vector3 Size { get; set; } public int Difficulty { get; set; } public int EntitiesCleared { get; set; } public Dictionary Players { get; set; } // Player ID as key, player name as value public string PlayerName { get; set; } public string Biome { get; set; } } public static bool HasInstance => instance != null; public static ScenarioManagerRebirth Instance { get { return instance; } } public static void Init() { ScenarioManagerRebirth.instance = new ScenarioManagerRebirth(); Log.Out("Starting Scenario Manager"); var mod = ModManager.GetMod("zzz_REBIRTH__Utils", true); ModPath = mod.Path; SaveDataPath = SaveDataPath = GameIO.GetUserGameDataDir() + "/RebirthData/Scenarios"; // mod.Path + "/Scenarios"; Directory.CreateDirectory(SaveDataPath); ClearedPrefabs = new List(); DiscoveredPrefabs = new List(); foreach (KeyValuePair biome in RebirthVariables.biomes) { int key = biome.Key; // The int key (0, 1, 2, etc.) string value = biome.Value; // The string value ("pine_forest", "desert", etc.) RebirthUtilities.AddPurgeBiome(value, 0); } //Log.Out("ScenarioManagerRebirth-Init RebirthVariables.purgeBiomes.Count: " + RebirthVariables.purgeBiomes.Count); LoadScenario("purge"); if ((SingletonMonoBehaviour.Instance.IsServer && !GameManager.IsDedicatedServer) || SingletonMonoBehaviour.Instance.IsSinglePlayer) { foreach (var prefab in ScenarioManagerRebirth.ClearedPrefabs) { //Log.Out("ScenarioManagerRebirth-Init SCENARIO Purged prefab.Name: " + prefab.Name); Vector3 vector = new Vector3(prefab.Position.x + prefab.Size.x / 2f, prefab.Position.y, prefab.Position.z + prefab.Size.z / 2f); NavObject navPurged = NavObjectManager.Instance.RegisterNavObject("purge_waypoint", vector, "purge_waypoint"); if (navPurged != null) { navPurged.TrackedPosition = vector; } navPurged.trackedPosition = vector; //navPurged.ForceDisabled = true; //navPurged.IsActive = false; } foreach (var prefab in ScenarioManagerRebirth.DiscoveredPrefabs) { //Log.Out("ScenarioManagerRebirth-Init SCENARIO Discovered prefab.Name: " + prefab.Name); Vector3 vector = new Vector3(prefab.Position.x + prefab.Size.x / 2f, prefab.Position.y, prefab.Position.z + prefab.Size.z / 2f); NavObject navDiscovered = NavObjectManager.Instance.RegisterNavObject("discovered_waypoint_" + prefab.Difficulty, vector, "discovered_waypoint_" + prefab.Difficulty); if (navDiscovered != null) { navDiscovered.TrackedPosition = vector; } navDiscovered.trackedPosition = vector; } } ModEvents.GameUpdate.RegisterHandler(Update); } public static bool IsIgnoredPrefab(string prefabName) { // Check if the prefabName exists in the ignorePrefabs XML (case-insensitive) return RebirthVariables.ignorePrefabs .Descendants("prefab") .Any(p => p.Attribute("name")?.Value.ToLower() == prefabName.ToLower()); } public static List GetIgnoredDiscoveredPrefabs() { // Create an empty list to hold the matching PrefabInfo objects List ignoredPrefabs = new List(); // Get the list of prefab names from the ignorePrefabs XML var ignoredNames = RebirthVariables.ignorePrefabs .Descendants("prefab") .Select(p => p.Attribute("name")?.Value) .Where(n => !string.IsNullOrEmpty(n)) .ToList(); // Filter the DiscoveredPrefabs based on the prefab names in ignorePrefabs foreach (var prefab in DiscoveredPrefabs) { if (ignoredNames.Contains(prefab.Name)) { //Log.Out("ScenarioManager-Update ignoredPrefabs CLEANUP, prefab.Name: " + prefab.Name); //Log.Out("ScenarioManager-Update ignoredPrefabs CLEANUP, prefab.Position: " + prefab.Position); ignoredPrefabs.Add(prefab); } } return ignoredPrefabs; } public static void Update() { if (GameUtils.IsPlaytesting()) { return; } if ((Time.time - cleanupUpdateCheck) > cleanupUpdateTick) { cleanupUpdateCheck = Time.time; //Log.Out("ScenarioManager-Update CLEANUP, ClearedPrefabs.Count: " + ClearedPrefabs.Count); //Log.Out("ScenarioManager-Update CLEANUP, DiscoveredPrefabs.Count: " + DiscoveredPrefabs.Count); List ignoredPrefabs = GetIgnoredDiscoveredPrefabs(); foreach (var prefab in ignoredPrefabs) { // Example: remove them from DiscoveredPrefabs DiscoveredPrefabs.Remove(prefab); //Log.Out("ScenarioManager-Update ignoredPrefabs CLEANUP, prefab.Name: " + prefab.Name); Vector3 location = new Vector3(prefab.Position.x + prefab.Size.x / 2f, prefab.Position.y, prefab.Position.z + prefab.Size.z / 2f); if (SingletonMonoBehaviour.Instance.IsSinglePlayer) { NavObjectManager.Instance.UnRegisterNavObjectByPosition(location, "discovered_waypoint_" + prefab.Difficulty); } else { if (!GameManager.IsDedicatedServer) { NavObjectManager.Instance.UnRegisterNavObjectByPosition(location, "discovered_waypoint_" + prefab.Difficulty); } SingletonMonoBehaviour.Instance.SendPackage((NetPackage)NetPackageManager.GetPackage().Setup("discovered_waypoint_" + prefab.Difficulty, location.x, location.y, location.z), false); } } SaveDiscovered(); var matchingPrefabs = DiscoveredPrefabs .Where(discovered => ClearedPrefabs.Any(cleared => cleared.Name == discovered.Name && cleared.Position == discovered.Position)) .ToList(); // Optional: Log the number of matching prefabs found //Log.Out($"ScenarioManager-Update CLEANUP: {matchingPrefabs.Count} discovered prefabs already cleared."); // Perform any other cleanup actions or processing with matchingPrefabs foreach (var prefab in matchingPrefabs) { // Example: remove them from DiscoveredPrefabs DiscoveredPrefabs.Remove(prefab); //Log.Out("ScenarioManager-Update CLEANUP, prefab.Name: " + prefab.Name); Vector3 location = new Vector3(prefab.Position.x + prefab.Size.x / 2f, prefab.Position.y, prefab.Position.z + prefab.Size.z / 2f); if (SingletonMonoBehaviour.Instance.IsSinglePlayer) { NavObjectManager.Instance.UnRegisterNavObjectByPosition(location, "discovered_waypoint_" + prefab.Difficulty); } else { if (!GameManager.IsDedicatedServer) { NavObjectManager.Instance.UnRegisterNavObjectByPosition(location, "discovered_waypoint_" + prefab.Difficulty); } SingletonMonoBehaviour.Instance.SendPackage((NetPackage)NetPackageManager.GetPackage().Setup("discovered_waypoint_" + prefab.Difficulty, location.x, location.y, location.z), false); } } } if ((Time.time - prefabUpdateCheck) > prefabUpdateTick) { prefabUpdateCheck = Time.time; if (dynamicPrefabsLoaded) { /*Log.Out("ScenarioManager-Update LIST OF PREFABS LOADED, dynamicPrefabs.Count: " + dynamicPrefabs.Count); if (dynamicPrefabs.Count > 0) { GameManager.Instance.StartCoroutine(RebirthUtilities.processDynamicPrefabs("pine_forest")); GameManager.Instance.StartCoroutine(RebirthUtilities.processDynamicPrefabs("desert")); GameManager.Instance.StartCoroutine(RebirthUtilities.processDynamicPrefabs("snow")); GameManager.Instance.StartCoroutine(RebirthUtilities.processDynamicPrefabs("wasteland")); GameManager.Instance.StartCoroutine(RebirthUtilities.processDynamicPrefabs("burnt_forest")); } else { prefabUpdateTick = 5f; }*/ } else { if (RebirthVariables.testPurge) Log.Out("ScenarioManager-Update GET LIST OF PREFABS"); System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); watch.Start(); int numPrefabs = 0; int numPrefabs2 = 0; int numPrefabs3 = 0; List tempDynamicPrefabs = GameManager.Instance.GetDynamicPrefabDecorator()?.GetDynamicPrefabs(); if (tempDynamicPrefabs != null) { if (RebirthVariables.testPurge) Log.Out("ScenarioManager-Update RETRIEVED BASE LIST OF DYNAMIC PREFABS, tempDynamicPrefabs.Count: " + tempDynamicPrefabs.Count); if (tempDynamicPrefabs.Count > 0) { foreach (PrefabInstance _prefabInstance in tempDynamicPrefabs) { numPrefabs++; //if (!_prefabInstance.prefab.PrefabName.ToLower().Contains("_tile_") && !_prefabInstance.prefab.Tags.Test_AnySet(FastTags.Parse("rwgonly,streettile,part,hideui"))) if (_prefabInstance.prefab.SleeperVolumes.Count > 0 && !_prefabInstance.prefab.Tags.Test_AnySet(FastTags.Parse("rwgonly,streettile,hideui"))) { numPrefabs2++; BiomeDefinition biomeAt = GameManager.Instance.World.ChunkCache.ChunkProvider.GetBiomeProvider().GetBiomeAt(_prefabInstance.boundingBoxPosition.x, _prefabInstance.boundingBoxPosition.z); if (biomeAt != null) { numPrefabs3++; Vector3 location = new Vector3(_prefabInstance.boundingBoxPosition.x, _prefabInstance.boundingBoxPosition.y, _prefabInstance.boundingBoxPosition.z); if (RebirthVariables.testPurge) { Log.Out("ScenarioManager-Update _prefabInstance.prefab.PrefabName: " + _prefabInstance.prefab.PrefabName); Log.Out("ScenarioManager-Update _prefabInstance.boundingBoxPosition: " + _prefabInstance.boundingBoxPosition); Log.Out("ScenarioManager-Update _prefabInstance.boundingBoxSize: " + _prefabInstance.boundingBoxSize); Log.Out("ScenarioManager-Update _prefabInstance.prefab.Tags: " + _prefabInstance.prefab.Tags); Log.Out("ScenarioManager-Update biomeAt.m_sBiomeName: " + biomeAt.m_sBiomeName); Log.Out("ScenarioManager-Update _prefabInstance.prefab.SleeperVolumes.Count: " + _prefabInstance.prefab.SleeperVolumes.Count); } if (!IsIgnoredPrefab(_prefabInstance.prefab.PrefabName)) { AddDynamicPrefab(new DynamicPrefab(_prefabInstance.prefab.PrefabName, location, new Vector3(_prefabInstance.boundingBoxSize.x, _prefabInstance.boundingBoxSize.y, _prefabInstance.boundingBoxSize.z), _prefabInstance.prefab.DifficultyTier, biomeAt.m_sBiomeName, _prefabInstance.prefab.Tags.ToString())); } } } } if (!RebirthVariables.purgePrefabCount) { RebirthVariables.purgePrefabCount = true; foreach (KeyValuePair biome in RebirthVariables.biomes) { int key = biome.Key; // The int key (0, 1, 2, etc.) string value = biome.Value; // The string value ("pine_forest", "desert", etc.) for (int i = 0; i <= 6; i++) { int clearedPrefabs = GetClearedPrefabCount(value, i); RebirthUtilities.AddSpawnedPrefab(value, ScenarioManagerRebirth.RetrieveTotalBiomePrefabs(value, i), i); if (RebirthVariables.testPurge) Log.Out($"ScenarioManager-Update {value} PREFABS, DIFFICULTY {i}: {RebirthUtilities.GetSpawnedPrefab(value, i)} / cleared: {clearedPrefabs}"); } } } dynamicPrefabsLoaded = true; } } //watch.Stop(); //Log.Out("ScenarioManager-Update time to process: " + watch.ElapsedMilliseconds + " ms"); if (RebirthVariables.testPurge) { Log.Out("ScenarioManager-Update numPrefabs: " + numPrefabs); Log.Out("ScenarioManager-Update numPrefabs2: " + numPrefabs2); Log.Out("ScenarioManager-Update numPrefabs3: " + numPrefabs3); } } } } public static void AddDynamicPrefab(DynamicPrefab prefab) { dynamicPrefabs.Add(prefab); } // Remove from the list using prefabName and position public static bool RemoveDynamicPrefab(string prefabName, Vector3 position) { var prefabToRemove = dynamicPrefabs.Find(p => p.PrefabName == prefabName && p.Position == position); if (prefabToRemove != null) { dynamicPrefabs.Remove(prefabToRemove); return true; } return false; } // Retrieve the first entry for a given difficulty and biome public static DynamicPrefab RetrieveDynamicPrefab(int difficulty, string biome) { return dynamicPrefabs.Find(p => p.Difficulty == difficulty && p.Biome == biome); } public static DynamicPrefab RetrieveDynamicPrefab(List tempDynamicPrefabs, int difficulty, string biome) { return tempDynamicPrefabs.Find(p => p.Difficulty == difficulty && p.Biome == biome); } public static List RetrieveDynamicPrefabs(string biome) { // Filter prefabs based on the biome provided List filteredPrefabs = dynamicPrefabs.FindAll(prefab => prefab.Biome == biome); return filteredPrefabs; } public static List RetrieveDynamicPrefabsFromDistance(string biome, Vector3 playerPosition, float distance, int difficulty) { List filteredPrefabs = dynamicPrefabs.FindAll(prefab => prefab.Biome == biome && prefab.Difficulty == difficulty); foreach (var prefab in dynamicPrefabs) { // Calculate the distance between the prefab position and the player's position Vector3 location = new Vector3(prefab.Position.x + prefab.Size.x / 2f, prefab.Position.y, prefab.Position.z + prefab.Size.z / 2f); float distanceToPrefab = Vector3.Distance(location, playerPosition); // Check if the prefab is within the specified distance if (distanceToPrefab > distance) { filteredPrefabs.Remove(prefab); } } return filteredPrefabs; } public static int RetrieveTotalBiomePrefabs(string biome, int difficulty) { List filteredPrefabs = dynamicPrefabs.FindAll(prefab => prefab.Biome == biome && prefab.Difficulty == difficulty); //Log.Out("ScenarioManagerRebirth-RetrieveTotalBiomePrefabs filteredPrefabs.Count: " + filteredPrefabs.Count); return filteredPrefabs.Count; } private static void LoadScenario(string scenario) { string scenarioPath = $"{SaveDataPath}/{GamePrefs.GetString(EnumGamePrefs.GameWorld)}/{GamePrefs.GetString(EnumGamePrefs.GameName)}"; Directory.CreateDirectory(scenarioPath); string scenarioFilePath = $"{scenarioPath}/" + scenario + ".xml"; string discoveredFilePath = $"{scenarioPath}/discoveredPrefabs.xml"; string purgeKillsFilePath = $"{scenarioPath}/purgeKillsInfo.xml"; bool exists = System.IO.File.Exists(scenarioFilePath); bool createNew = false; if (exists) { //Log.Out("ScenarioManagerRebirth-LoadScenario SCENARIO FILE EXISTS"); int currentDay = GameUtils.WorldTimeToDays(GameManager.Instance.World.worldTime); //Log.Out("ScenarioManagerRebirth-LoadScenario currentDay: " + currentDay); float currentTimeOfDayHours = GameUtils.WorldTimeToHours(GameManager.Instance.World.worldTime); //Log.Out("ScenarioManagerRebirth-LoadScenario currentTimeOfDayHours: " + currentTimeOfDayHours); float currentTimeOfDayMinutes = GameUtils.WorldTimeToMinutes(GameManager.Instance.World.worldTime); //Log.Out("ScenarioManagerRebirth-LoadScenario currentTimeOfDayMinutes: " + currentTimeOfDayMinutes); if (currentDay == 1 && currentTimeOfDayHours == 7 && currentTimeOfDayMinutes < 20) { //Log.Out("ScenarioManagerRebirth-LoadScenario EARLY MORNING"); try { Directory.Delete(scenarioFilePath); } catch { } if (scenario == "purge") { try { Directory.Delete(purgeKillsFilePath); } catch { } } createNew = true; } } else { //Log.Out("ScenarioManagerRebirth-LoadScenario SCENARIO FILE DOES NOT EXIST"); createNew = true; } if (createNew) { //Log.Out("ScenarioManagerRebirth-LoadScenario CREATE NEW FILE"); SaveScenario(scenario); // Save an empty file if it doesn't exist SaveDiscovered(); if (scenario == "purge") { SavePurgeKills(); } return; } //Log.Out("ScenarioManagerRebirth-LoadScenario LOAD EXISTING FILE"); if (scenario == "purge") { XDocument xmlDoc = XDocument.Load(scenarioFilePath); ClearedPrefabs = (from prefab in xmlDoc.Root.Elements("Prefab") select new PrefabInfo { Name = prefab.Element("Name").Value, Position = new Vector3( float.Parse(prefab.Element("Position").Element("X").Value), float.Parse(prefab.Element("Position").Element("Y").Value), float.Parse(prefab.Element("Position").Element("Z").Value)), Size = new Vector3( float.Parse(prefab.Element("Size").Element("X").Value), float.Parse(prefab.Element("Size").Element("Y").Value), float.Parse(prefab.Element("Size").Element("Z").Value)), Difficulty = int.Parse(prefab.Element("Difficulty").Value), EntitiesCleared = int.Parse(prefab.Element("EntitiesCleared").Value), Players = prefab.Element("Players").Elements("Player") .ToDictionary(p => int.Parse(p.Element("ID").Value), p => p.Element("Name").Value), Biome = prefab.Element("Biome").Value }).ToList(); if (File.Exists(discoveredFilePath)) { xmlDoc = XDocument.Load(discoveredFilePath); DiscoveredPrefabs = (from prefab in xmlDoc.Root.Elements("Prefab") select new PrefabInfo { Name = prefab.Element("Name").Value, Position = new Vector3( float.Parse(prefab.Element("Position").Element("X").Value), float.Parse(prefab.Element("Position").Element("Y").Value), float.Parse(prefab.Element("Position").Element("Z").Value)), Size = new Vector3( float.Parse(prefab.Element("Size").Element("X").Value), float.Parse(prefab.Element("Size").Element("Y").Value), float.Parse(prefab.Element("Size").Element("Z").Value)), Difficulty = int.Parse(prefab.Element("Difficulty").Value), EntitiesCleared = int.Parse(prefab.Element("EntitiesCleared").Value), Players = prefab.Element("Players").Elements("Player") .ToDictionary(p => int.Parse(p.Element("ID").Value), p => p.Element("Name").Value), Biome = prefab.Element("Biome").Value }).ToList(); } if (File.Exists(purgeKillsFilePath)) { xmlDoc = XDocument.Load(purgeKillsFilePath); purgeKills = (from purgeKill in xmlDoc.Root.Elements("PurgeKill") select new PurgeKills { Name = purgeKill.Element("Name").Value, Position = new Vector3( float.Parse(purgeKill.Element("Position").Element("X").Value), float.Parse(purgeKill.Element("Position").Element("Y").Value), float.Parse(purgeKill.Element("Position").Element("Z").Value)), Players = purgeKill.Element("Players").Elements("Player") .ToDictionary(p => int.Parse(p.Element("ID").Value), p => new KillInfo { Kills = int.Parse(p.Element("Kills").Value), Redeemed = bool.Parse(p.Element("Redeemed").Value) // Load Redeemed value }) }).ToList(); } } } public static void SaveScenario(string scenario) { string scenarioPath = $"{SaveDataPath}/{GamePrefs.GetString(EnumGamePrefs.GameWorld)}/{GamePrefs.GetString(EnumGamePrefs.GameName)}"; Directory.CreateDirectory(scenarioPath); string scenarioFilePath = $"{scenarioPath}/" + scenario + ".xml"; XElement root = new XElement("Prefabs", from prefab in ClearedPrefabs select new XElement("Prefab", new XElement("Name", prefab.Name), new XElement("Position", new XElement("X", prefab.Position.x), new XElement("Y", prefab.Position.y), new XElement("Z", prefab.Position.z)), new XElement("Size", new XElement("X", prefab.Size.x), new XElement("Y", prefab.Size.y), new XElement("Z", prefab.Size.z)), new XElement("Difficulty", prefab.Difficulty), new XElement("EntitiesCleared", prefab.EntitiesCleared), new XElement("Players", from player in prefab.Players select new XElement("Player", new XElement("ID", player.Key), new XElement("Name", player.Value))), new XElement("Biome", prefab.Biome) )); XDocument xmlDoc = new XDocument(root); xmlDoc.Save(scenarioFilePath); } public static void SaveDiscovered() { string scenarioPath = $"{SaveDataPath}/{GamePrefs.GetString(EnumGamePrefs.GameWorld)}/{GamePrefs.GetString(EnumGamePrefs.GameName)}"; Directory.CreateDirectory(scenarioPath); string filePath = $"{scenarioPath}/discoveredPrefabs.xml"; XElement root = new XElement("Prefabs", from prefab in DiscoveredPrefabs select new XElement("Prefab", new XElement("Name", prefab.Name), new XElement("Position", new XElement("X", prefab.Position.x), new XElement("Y", prefab.Position.y), new XElement("Z", prefab.Position.z)), new XElement("Size", new XElement("X", prefab.Size.x), new XElement("Y", prefab.Size.y), new XElement("Z", prefab.Size.z)), new XElement("Difficulty", prefab.Difficulty), new XElement("EntitiesCleared", prefab.EntitiesCleared), new XElement("Players", from player in prefab.Players select new XElement("Player", new XElement("ID", player.Key), new XElement("Name", player.Value))), new XElement("Biome", prefab.Biome) )); XDocument xmlDoc = new XDocument(root); xmlDoc.Save(filePath); } // PURGE KILLS HELPERS public static int GetTotalPurgeKills(string name, Vector3 position) { // Get the list of players from the GetPurgeKillPlayers function var players = GetPurgeKillPlayers(name, position); // Sum the total kills of all players return players.Sum(p => p.Kills); } public static List GetPurgeKillPlayers(string name, Vector3 position) { var purgeKill = GetPurgeKillEntry(name, position); if (purgeKill != null) { // Convert the Players dictionary to a list of PlayerKillInfo return purgeKill.Players.Select(p => new PlayerKillInfo { PlayerID = p.Key, Kills = p.Value.Kills, // Access kills from KillInfo Redeemed = p.Value.Redeemed // Access redeemed status from KillInfo }).ToList(); } return new List(); // Return an empty list if no matching PurgeKill entry is found } public static List GetPurgeKillEntries(int playerID) { return purgeKills.Where(pk => pk.Players.ContainsKey(playerID)).ToList(); } private static PurgeKills GetPurgeKillEntry(string name, Vector3 position) { return purgeKills.FirstOrDefault(pk => pk.Name == name && pk.Position == position); } private static bool PurgeKillExists(string name, Vector3 position) { return purgeKills.Any(pk => pk.Name == name && pk.Position == position); } public static int GetTotalKills(int playerID) { // Sum the total kills for the playerID across all PurgeKills entries int totalKills = purgeKills .Where(purgeKill => purgeKill.Players.ContainsKey(playerID)) // Filter entries that have this playerID .Sum(purgeKill => purgeKill.Players[playerID].Kills); // Sum the kills for this playerID return totalKills; } public static List GetKills(string name, Vector3 position, int playerID) { return purgeKills.Where(pk => pk.Name == name && pk.Position == position && pk.Players.ContainsKey(playerID)).ToList(); } public static bool RemoveKills(string name, Vector3 position, int playerID) { var purgeKill = GetPurgeKillEntry(name, position); if (purgeKill != null && purgeKill.Players.ContainsKey(playerID)) { purgeKill.Players.Remove(playerID); if (purgeKill.Players.Count == 0) { purgeKills.Remove(purgeKill); } SavePurgeKills(); return true; } return false; } public static (int numKills, int numRedeemableKills) AddKill(string name, Vector3 position, int playerID) { var purgeKill = GetPurgeKillEntry(name, position); if (purgeKill == null) { purgeKill = new PurgeKills { Name = name, Position = position }; purgeKills.Add(purgeKill); } if (!purgeKill.Players.ContainsKey(playerID)) { purgeKill.Players[playerID] = new KillInfo { Kills = 0, Redeemed = false }; // Initialize with default values } purgeKill.Players[playerID].Kills++; SavePurgeKills(); int numKills = GetCurrentPurgeKills(playerID); int numRedeemableKills = GetCurrentRedeemablePurgeKills(playerID); //Log.Out("ScenarioManagerRebirth-AddKill numKills: " + numKills); //Log.Out("ScenarioManagerRebirth-AddKill numRedeemableKills: " + numRedeemableKills); return (numKills, numRedeemableKills); } public static bool RedeemKills(string name, Vector3 position, int playerID) { // Find the matching PurgeKill entry var purgeKill = GetPurgeKillEntry(name, position); if (purgeKill != null && purgeKill.Players.ContainsKey(playerID)) { // Check if the player has any kills var killInfo = purgeKill.Players[playerID]; if (killInfo != null && !killInfo.Redeemed) // If not already redeemed { killInfo.Redeemed = true; // Mark as redeemed SavePurgeKills(); // Save the updated kills return true; // Return true if the operation was successful } } return false; // Return false if the operation failed } public static int AutoRedeemKills(int playerID) { int totalKills = 0; // Loop through all purge kills foreach (var purgeKill in purgeKills) { // Check if the player is in the current PurgeKill entry if (purgeKill.Players.ContainsKey(playerID) && IsPrefabCleared(purgeKill.Name, purgeKill.Position)) { var killInfo = purgeKill.Players[playerID]; // If the kill entry was redeemed, reset it and add kills to total if (!killInfo.Redeemed) { killInfo.Redeemed = true; // Reset the redeemed flag totalKills += killInfo.Kills; // Add kills to the total count } } } // Save updated data SavePurgeKills(); return totalKills; // Return total number of kills that were updated } public static int GetCurrentPurgeKills(int playerID) { int totalKills = 0; // Loop through all purge kills foreach (var purgeKill in purgeKills) { // Check if the player is in the current PurgeKill entry if (purgeKill.Players.ContainsKey(playerID)) { var killInfo = purgeKill.Players[playerID]; // If the kill entry was redeemed, reset it and add kills to total if (!killInfo.Redeemed) { totalKills += killInfo.Kills; // Add kills to the total count } } } return totalKills; } public static int GetCurrentRedeemablePurgeKills(int playerID) { int totalKills = 0; // Loop through all purge kills foreach (var purgeKill in purgeKills) { // Check if the player is in the current PurgeKill entry if (purgeKill.Players.ContainsKey(playerID) && !IsPrefabCleared(purgeKill.Name, purgeKill.Position)) { var killInfo = purgeKill.Players[playerID]; // If the kill entry was redeemed, reset it and add kills to total if (!killInfo.Redeemed) { totalKills += killInfo.Kills; // Add kills to the total count } } } return totalKills; } private static void SavePurgeKills() { if (GameManager.Instance.World.IsEditor()) { return; } string scenarioPath = $"{SaveDataPath}/{GamePrefs.GetString(EnumGamePrefs.GameWorld)}/{GamePrefs.GetString(EnumGamePrefs.GameName)}"; string purgeKillsFilePath = $"{scenarioPath}/purgeKillsInfo.xml"; XElement root = new XElement("PurgeKills", from purgeKill in purgeKills select new XElement("PurgeKill", new XElement("Name", purgeKill.Name), new XElement("Position", new XElement("X", purgeKill.Position.x), new XElement("Y", purgeKill.Position.y), new XElement("Z", purgeKill.Position.z)), new XElement("Players", from player in purgeKill.Players select new XElement("Player", new XElement("ID", player.Key), new XElement("Kills", player.Value.Kills), // Save kills new XElement("Redeemed", player.Value.Redeemed) // Save redeemed status )) )); XDocument xmlDoc = new XDocument(root); xmlDoc.Save(purgeKillsFilePath); } // PREFAB INFO HELPERS public static string GetFirstPlayerName(PrefabInfo prefabInfo) { if (prefabInfo.Players != null && prefabInfo.Players.Count > 0) { // Get the first player's name (the first value from the dictionary) return prefabInfo.Players.Values.First(); } return null; // Return null if the dictionary is empty or not set } public static void AddPrefab(string name, Vector3 position, Vector3 size, int difficulty, int entitiesCleared, Dictionary players, string biome) { if (IsPrefabCleared(name, position)) { return; } PrefabInfo newPrefab = new PrefabInfo { Name = name, Position = position, Size = size, Difficulty = difficulty, EntitiesCleared = entitiesCleared, Players = players, Biome = biome }; Vector3 vector = new Vector3(newPrefab.Position.x + newPrefab.Size.x / 2f, newPrefab.Position.y, newPrefab.Position.z + newPrefab.Size.z / 2f); ClearedPrefabs.Add(newPrefab); int clearedPrefabs = GetTotalClearedPrefabCount(biome); int totalPrefabs = RebirthUtilities.GetTotalSpawnedPrefab(biome); //Log.Out($"ScenarioManagerRebirth-AddPrefab biome: {biome} / clearedPrefabs: {clearedPrefabs} of {totalPrefabs}"); SaveScenario("purge"); // Automatically save after adding // remove prefab from discovered RemoveDiscoveredPrefab(name, position, vector); RemoveDynamicPrefab(name, position); //Debug.Log($"Prefab: {newPrefab.Name} / position: {newPrefab.Position} / size: {newPrefab.Size} / Difficulty: {newPrefab.Difficulty} / Entities Cleared: {newPrefab.EntitiesCleared} / Reported By: {players} / Biome: {newPrefab.Biome}"); bool reportLocal = false; string report = string.Format(Localization.Get("PurgePurgedReport"), GetFirstPlayerName(newPrefab), entitiesCleared, Localization.Get(name), Localization.Get("biome_" + biome)); string sound = "purge_discovered"; if (SingletonMonoBehaviour.Instance.IsSinglePlayer) { reportLocal = true; RebirthVariables.purgeDisplayPercentage = ((float)clearedPrefabs / totalPrefabs) * 100; int numPrefabs = clearedPrefabs; if (clearedPrefabs > totalPrefabs) { numPrefabs = totalPrefabs; } RebirthVariables.purgeLabel = numPrefabs + "/" + totalPrefabs; } else { if (!GameManager.IsDedicatedServer) { reportLocal = true; RebirthVariables.purgeDisplayPercentage = ((float)clearedPrefabs / totalPrefabs) * 100; int numPrefabs = clearedPrefabs; if (clearedPrefabs > totalPrefabs) { numPrefabs = totalPrefabs; } RebirthVariables.purgeLabel = numPrefabs + "/" + totalPrefabs; } SingletonMonoBehaviour.Instance.SendPackage(NetPackageManager.GetPackage().Setup("purge_waypoint", vector.x, vector.y, vector.z, report, sound)); SingletonMonoBehaviour.Instance.SendPackage(NetPackageManager.GetPackage().Setup(name, position.x, position.y, position.z, 0, 0, 0, 0, 0, 0, 0), false); } if (reportLocal) { NavObject navPurged = NavObjectManager.Instance.RegisterNavObject("purge_waypoint", vector, "purge_waypoint"); if (navPurged != null) { navPurged.trackedPosition = vector; } EntityPlayerLocal primaryPlayer = GameManager.Instance.World.GetPrimaryPlayer(); if (primaryPlayer != null) { if (sound != "") { Manager.PlayInsidePlayerHead(sound); } //Log.Out("ScenarioManagerRebirth-AddPrefab report: " + report); GameManager.ShowTooltip(primaryPlayer, report, string.Empty); } } } public static void AddDiscoveredPrefab(string name, Vector3 position, Vector3 size, int difficulty, int entitiesCleared, Dictionary players, string biome) { //Log.Out("ScenarioManagerRebirth-AddDiscoveredPrefab START"); PrefabInfo newPrefab = new PrefabInfo { Name = name, Position = position, Size = size, Difficulty = difficulty, EntitiesCleared = entitiesCleared, Players = players, Biome = biome }; //Log.Out($"Prefab: {newPrefab.Name} / position: {newPrefab.Position} / size: {newPrefab.Size} / Difficulty: {newPrefab.Difficulty} / Entities Cleared: {newPrefab.EntitiesCleared} / Reported By: {players} / Biome: {newPrefab.Biome}"); if (IsPrefabDiscovered(name, position)) { //Log.Out("ScenarioManagerRebirth-AddDiscoveredPrefab ALREADY DISCOVERED"); return; } DiscoveredPrefabs.Add(newPrefab); SaveDiscovered(); Vector3 vector = new Vector3(newPrefab.Position.x + newPrefab.Size.x / 2f, newPrefab.Position.y, newPrefab.Position.z + newPrefab.Size.z / 2f); bool reportLocal = false; string report = string.Format(Localization.Get("PurgeDiscoveryReport"), GetFirstPlayerName(newPrefab), Localization.Get(name), Localization.Get("biome_" + biome)); string sound = "purge_cleared"; if (SingletonMonoBehaviour.Instance.IsSinglePlayer) { reportLocal = true; } else { if (!GameManager.IsDedicatedServer) { reportLocal = true; } if (players.ContainsKey(-1)) { report = ""; } SingletonMonoBehaviour.Instance.SendPackage(NetPackageManager.GetPackage().Setup("discovered_waypoint_" + difficulty, vector.x, vector.y, vector.z, report, sound)); } if (reportLocal) { //Log.Out("ScenarioManagerRebirth-AddDiscoveredPrefab navobject: " + "discovered_waypoint_" + difficulty); //Log.Out("ScenarioManagerRebirth-AddDiscoveredPrefab vector: " + vector); NavObject navDiscovered = NavObjectManager.Instance.RegisterNavObject("discovered_waypoint_" + difficulty, vector, "discovered_waypoint_" + difficulty); if (navDiscovered != null) { navDiscovered.trackedPosition = vector; } if (!players.ContainsKey(-1)) { EntityPlayerLocal primaryPlayer = GameManager.Instance.World.GetPrimaryPlayer(); if (primaryPlayer != null) { if (sound != "") { Manager.PlayInsidePlayerHead(sound); } //Log.Out("ScenarioManagerRebirth-AddDiscoveredPrefab report: " + report); GameManager.ShowTooltip(primaryPlayer, report, string.Empty); } } } } // Check if a prefab is cleared by its name and position public static bool IsPrefabCleared(string name, Vector3 position) { return ClearedPrefabs.Any(p => p.Name == name && p.Position == position); } public static bool IsPrefabDiscovered(string name, Vector3 position) { return DiscoveredPrefabs.Any(p => p.Name == name && p.Position == position && p.Difficulty != -1); } // Remove a prefab from the cleared list public static void RemovePrefab(string name, Vector3 position) { var prefabToRemove = ClearedPrefabs.FirstOrDefault(p => p.Name == name && p.Position == position); if (prefabToRemove != null) { ClearedPrefabs.Remove(prefabToRemove); SaveScenario("purge"); } else { Debug.LogWarning("Prefab not found."); } } public static void UpdateDiscoveredPrefab(string name, Vector3 position, int entitiesCleared) { var prefab = DiscoveredPrefabs.FirstOrDefault(p => p.Name == name && p.Position == position); // If the prefab is found, update the EntitiesCleared value if (prefab != null) { prefab.EntitiesCleared = entitiesCleared; } } public static int GetDiscoveredPrefabEntitiesCleared(string name, Vector3 position) { // Find the prefab in the list that matches the name and position var prefab = DiscoveredPrefabs.FirstOrDefault(p => p.Name == name && p.Position == position); // Return the EntitiesCleared value if the prefab is found, otherwise return a default value return prefab != null ? prefab.EntitiesCleared : -1; // Return -1 if not found } public static void RemoveDiscoveredPrefab(string name, Vector3 position, Vector3 location) { //Log.Out("ScenarioManagerRebirth-RemoveDiscoveredPrefab START"); var prefabToRemove = DiscoveredPrefabs.FirstOrDefault(p => p.Name == name && p.Position == position); if (prefabToRemove != null) { //Log.Out("ScenarioManagerRebirth-RemoveDiscoveredPrefab 1"); DiscoveredPrefabs.Remove(prefabToRemove); if (SingletonMonoBehaviour.Instance.IsSinglePlayer) { //Log.Out("ScenarioManagerRebirth-RemoveDiscoveredPrefab UNREGISTER LOCAL NAV OBJECT: " + "discovered_waypoint_" + prefabToRemove.Difficulty); //Log.Out("ScenarioManagerRebirth-RemoveDiscoveredPrefab position: " + position); //Log.Out("ScenarioManagerRebirth-RemoveDiscoveredPrefab location: " + location); NavObjectManager.Instance.UnRegisterNavObjectByPosition(location, "discovered_waypoint_" + prefabToRemove.Difficulty); } else { if (!GameManager.IsDedicatedServer) { NavObjectManager.Instance.UnRegisterNavObjectByPosition(location, "discovered_waypoint_" + prefabToRemove.Difficulty); } //Log.Out("ScenarioManagerRebirth-RemoveDiscoveredPrefab SEND UNREGISTER TO CLIENTS"); SingletonMonoBehaviour.Instance.SendPackage((NetPackage)NetPackageManager.GetPackage().Setup("discovered_waypoint_" + prefabToRemove.Difficulty, location.x, location.y, location.z), false); } SaveDiscovered(); } else { Debug.LogWarning("Prefab not found."); } } // Retrieve prefabs by player name public static List GetPrefabsByPlayer(string playerName) { return ClearedPrefabs.Where(p => p.PlayerName == playerName).ToList(); } // Retrieve prefabs by name public static List GetPrefabsByName(string prefabName) { return ClearedPrefabs.Where(p => p.Name == prefabName).ToList(); } // Retrieve prefabs by position public static List GetPrefabsByPosition(Vector3 position) { return ClearedPrefabs.Where(p => p.Position == position).ToList(); } // Retrieve prefabs by difficulty public static List GetPrefabsByDifficulty(int difficulty) { return ClearedPrefabs.Where(p => p.Difficulty == difficulty).ToList(); } // Retrieve prefabs by biome public static List GetPrefabsByBiome(string biome) { return ClearedPrefabs.Where(p => p.Biome == biome).ToList(); } public static int GetClearedPrefabCount(string biome, int difficulty) { return ClearedPrefabs.Count(p => p.Biome == biome && p.Difficulty == difficulty); } public static int GetTotalClearedPrefabCount(string biome) { return ClearedPrefabs.Count(p => p.Biome == biome); } }