Exemple #1
0
        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);
    }
Exemple #6
0
    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);
            }
        }
    }