static bool Prefix(BombGenerator __instance, BombFace selectedFace, BombComponent bombComponentPrefab, GeneratorSetting settings) { var bomb = __instance.GetValue <Bomb>("bomb"); var type = bombComponentPrefab.ComponentType; // Let the timer component spawn normally and record the bomb's information. if (type == ComponentTypeEnum.Timer) { allBombInfo.Add(bomb, new BombInfo(settings, selectedFace, __instance.GetValue <Random>("rand"))); void logCallback(string condition, string _, LogType __) { if (!condition.StartsWith("[BombGenerator] Bomb component list: ")) { return; } // Replace the Random object with a fake one, so that we can make the consistent RNG calls later. __instance.SetValue("rand", new FakeRandom()); Application.logMessageReceived -= logCallback; } Application.logMessageReceived += logCallback; return(true); } // If we don't have information about a bomb, just let it go through. if (!allBombInfo.TryGetValue(bomb, out BombInfo bombInfo)) { return(true); } // Once we're ready to instantiate the components, this allows us to call the original method again. if (bombInfo.EnableOriginal) { return(true); } // If the generator is trying to fill the bomb with empty components, clear the valid faces to skip over it. if (type == ComponentTypeEnum.Empty) { __instance.GetValue <List <BombFace> >("validBombFaces").Clear(); return(false); } // Start loading any fake modules. if (bombComponentPrefab.GetComponent <FakeModule>() != null) { __instance.StartCoroutine(LoadModule(bombComponentPrefab, bombInfo)); } else { bombInfo.Components.Add(bombComponentPrefab); } return(false); }
static bool Prefix(BombGenerator __instance, BombFace selectedFace, BombComponent bombComponentPrefab, GeneratorSetting settings) { var bomb = __instance.GetValue <Bomb>("bomb"); var type = bombComponentPrefab.ComponentType; // Let the timer component spawn normally and record the bomb's information. if (type == ComponentTypeEnum.Timer) { allBombInfo.Add(bomb, new BombInfo(settings, selectedFace, __instance.GetValue <Random>("rand"))); return(true); } // If we don't have information about a bomb, just let it go through. if (!allBombInfo.TryGetValue(bomb, out BombInfo bombInfo)) { return(true); } // Once we're ready to instantiate the components, this allows us to call the original method again. if (bombInfo.EnableOriginal) { return(true); } // If the generator is trying to fill the bomb with empty components, clear the valid faces to skip over it. if (type == ComponentTypeEnum.Empty) { __instance.GetValue <List <BombFace> >("validBombFaces").Clear(); return(false); } // Start loading any fake modules. if (bombComponentPrefab.GetComponent <FakeModule>() != null) { __instance.StartCoroutine(LoadModule(bombComponentPrefab, bombInfo)); } else { bombInfo.Components.Add(bombComponentPrefab); } return(false); }
private static IEnumerator InstantiateComponents(Bomb bomb) { yield return(new WaitUntil(() => modsLoading == 0 && bombQueue.Count != 0 && bombQueue.Peek() == bomb)); var bombGenerator = SceneManager.Instance.GameplayState.GetValue <BombGenerator>("bombGenerator"); bombGenerator.SetValue("bomb", bomb); var logger = bombGenerator.GetValue <ILog>("logger"); // Enable logging again ((log4net.Repository.Hierarchy.Logger)logger.Logger).Level = null; var validBombFaces = new List <BombFace>(bomb.Faces.Where(face => face.ComponentSpawnPoints.Count != 0)); bombGenerator.SetValue("validBombFaces", validBombFaces); List <KMBombInfo> knownBombInfos = new List <KMBombInfo>(); foreach (KMBombInfo item in UnityEngine.Object.FindObjectsOfType <KMBombInfo>()) { knownBombInfos.Add(item); } var bombInfo = allBombInfo[bomb]; var timerFace = bombInfo.TimerFace; var setting = bombInfo.Settings; bombInfo.EnableOriginal = true; bombGenerator.SetValue("rand", bombInfo.Rand); // Bring back the real Random object. UnityEngine.Random.InitState(bomb.Seed); // Loading AudioClips triggers RNG calls so we need to reset the RNG to before that happened. // Emulate logging messages logger.InfoFormat("Generating bomb with seed {0}", bomb.Seed); logger.InfoFormat("Generator settings: {0}", setting.ToString()); foreach (var component in bombInfo.Components) { logger.InfoFormat("Selected {0} ({1})", Modes.GetModuleID(component), component); } var requiresTimer = bombInfo.Components.Where(component => component.RequiresTimerVisibility).Select(Modes.GetModuleID).ToArray(); var anyFace = bombInfo.Components.Where(component => !component.RequiresTimerVisibility).Select(Modes.GetModuleID).ToArray(); logger.DebugFormat("Bomb component list: RequiresTimerVisibility [{0}], AnyFace: [{1}]", string.Join(", ", requiresTimer), string.Join(", ", anyFace)); logger.DebugFormat("Instantiating RequiresTimerVisibility components on {0}", timerFace); // Spawn components bool loggedRemaining = false; foreach (var bombComponent in bombInfo.Components.OrderByDescending(component => component.RequiresTimerVisibility)) { BombFace face = null; if (bombComponent.RequiresTimerVisibility && timerFace.ComponentSpawnPoints.Count != 0) { face = timerFace; } else if (!loggedRemaining) { logger.Debug("Instantiating remaining components on any valid face."); loggedRemaining = true; } if (face == null && validBombFaces.Count != 0) { face = validBombFaces[bombInfo.Rand.Next(0, validBombFaces.Count)]; } if (face == null) { Tweaks.Log("No valid faces remain to instantiate:", bombComponent.name); continue; } bombGenerator.CallMethod("InstantiateComponent", face, bombComponent, setting); } logger.Debug("Filling remaining spaces with empty components."); while (validBombFaces.Count > 0) { bombGenerator.CallMethod("InstantiateComponent", validBombFaces[0], bombGenerator.emptyComponentPrefab, setting); } // We need to re-Init() the bomb face selectables so that the components get their correct X and Y positions for Gamepad support. foreach (Selectable selectable in bomb.Faces.Select(face => face.GetComponent <Selectable>())) { selectable.Init(); } logger.Debug("Generating Widgets"); // To ensure that the widgets get placed in the right position, we need to temporarily revert the bomb's size. bomb.visualTransform.localScale = Vector3.one; WidgetGenerator generator = bombGenerator.GetComponent <WidgetGenerator>(); generator.SetValue("zones", bombInfo.WidgetZones); generator.GenerateWidgets(bomb.WidgetManager, setting.OptionalWidgetCount); bomb.visualTransform.localScale = new Vector3(bomb.Scale, bomb.Scale, bomb.Scale); bombInfo.Loaded = true; HookUpMultipleBombs(bomb, knownBombInfos); bombQueue.Dequeue(); if (BombsLoaded) { SceneManager.Instance.GameplayState.Bombs.AddRange(allBombInfo.Keys); allBombInfo.Clear(); // This fixes the bomb not getting picked up correctly if clicked on before loading is finished. var holdable = KTInputManager.Instance.SelectableManager.GetCurrentFloatingHoldable(); if (holdable) { holdable.Defocus(false, false); } } // Solve any fake modules foreach (BombComponent component in bomb.BombComponents) { if (component.GetComponent <FakeModule>() != null) { component.IsSolved = true; component.Bomb.OnPass(component); } } }
public BombInfo(GeneratorSetting settings, BombFace timerFace, Random rand) { Settings = settings; TimerFace = timerFace; Rand = rand; }
public static BombFace.ComponentSpawnPoint?GetComponentSpawnPoint(Vector3 position, Bomb bomb, out BombFace bombFace) { float minimum = float.MaxValue; BombFace.ComponentSpawnPoint?item = null; bombFace = null; foreach (BombFace face in bomb.Faces) { foreach (var spawnPoint in face.ComponentSpawnPoints.Concat(face.TimerSpawnPoints)) { float distance = (spawnPoint.Transform.position - position).magnitude; if (distance < minimum) { minimum = distance; item = spawnPoint; bombFace = face; } } } return(item); }
private static IEnumerator InstantiateComponents(Bomb bomb) { yield return(new WaitUntil(() => modsLoading == 0)); var bombGenerator = SceneManager.Instance.GameplayState.GetValue <BombGenerator>("bombGenerator"); bombGenerator.SetValue("bomb", bomb); var validBombFaces = new List <BombFace>(bomb.Faces.Where(face => face.ComponentSpawnPoints.Count != 0)); bombGenerator.SetValue("validBombFaces", validBombFaces); List <KMBombInfo> knownBombInfos = new List <KMBombInfo>(); foreach (KMBombInfo item in UnityEngine.Object.FindObjectsOfType <KMBombInfo>()) { knownBombInfos.Add(item); } var bombInfo = allBombInfo[bomb]; var timerFace = bombInfo.TimerFace; var setting = bombInfo.Settings; bombInfo.EnableOriginal = true; bombGenerator.SetValue("rand", bombInfo.Rand); // Bring back the real Random object. UnityEngine.Random.InitState(bomb.Seed); // Loading AudioClips triggers RNG calls so we need to reset the RNG to before that happened. foreach (var bombComponent in bombInfo.Components.OrderByDescending(component => component.RequiresTimerVisibility)) { BombFace face = null; if (bombComponent.RequiresTimerVisibility && timerFace.ComponentSpawnPoints.Count != 0) { face = timerFace; } if (face == null && validBombFaces.Count != 0) { face = validBombFaces[bombInfo.Rand.Next(0, validBombFaces.Count)]; } if (face == null) { Tweaks.Log("No valid faces remain to instantiate:", bombComponent.name); continue; } bombGenerator.CallMethod("InstantiateComponent", face, bombComponent, setting); } while (validBombFaces.Count > 0) { bombGenerator.CallMethod("InstantiateComponent", validBombFaces[0], bombGenerator.emptyComponentPrefab, setting); } // To ensure that the widgets get placed in the right position, we need to temporarily revert the bomb's size. bomb.visualTransform.localScale = Vector3.one; WidgetGenerator generator = bombGenerator.GetComponent <WidgetGenerator>(); generator.SetValue("zones", bombInfo.WidgetZones); generator.GenerateWidgets(bomb.WidgetManager, setting.OptionalWidgetCount); bomb.visualTransform.localScale = new Vector3(bomb.Scale, bomb.Scale, bomb.Scale); bombInfo.Loaded = true; HookUpMultipleBombs(bomb, knownBombInfos); if (BombsLoaded) { SceneManager.Instance.GameplayState.Bombs.AddRange(allBombInfo.Keys); allBombInfo.Clear(); // This fixes the bomb not getting picked up correctly if clicked on before loading is finished. var holdable = KTInputManager.Instance.SelectableManager.GetCurrentFloatingHoldable(); if (holdable) { holdable.Defocus(false, false); } } // Solve any fake modules foreach (BombComponent component in bomb.BombComponents) { if (component.GetComponent <FakeModule>() != null) { component.IsSolved = true; component.Bomb.OnPass(component); } } }