示例#1
0
        private static string MadlibBlank(string description, string hash, string seed)
        {
            if (description.Contains(":"))
            {
                var split = description.Split(':');
                description = split[0];
                seed        = split[1];
            }

            int count;

            try {
                count = int.Parse(Dialog.Get("RANDOHEART_" + description + "_COUNT"));
            } catch (FormatException) {
                throw new Exception("Bad key: RANDOHEART_" + description + "_COUNT");
            }
            var r      = new Random((int)RandoSettings.djb2(hash + seed));
            var picked = r.Next(count);
            var result = Dialog.Get("RANDOHEART_" + description + "_" + picked.ToString());

            if (char.IsLower(description[0]))
            {
                result = result.ToLower();
            }
            return(result);
        }
        private void HijackExitBegin(On.Celeste.LevelExit.orig_Begin orig, LevelExit self)
        {
            orig(self);
            var settings = this.InRandomizerSettings;

            this.endingSettings = settings;
            if (settings == null || settings.Algorithm != LogicType.Endless)
            {
                return;
            }

            LevelExit.Mode mode = (LevelExit.Mode) typeof(LevelExit).GetField("mode", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(self);
            if (mode != LevelExit.Mode.Completed)
            {
                return;
            }

            var newSettings = settings.Copy();

            newSettings.EndlessLevel++;
            Audio.SetMusic(SFX.music_complete_bside);
            Audio.SetAmbience(null);

            this.genTask = Task.Run(() => {
                try {
                    return(RandoLogic.GenerateMap(newSettings));
                } catch (GenerationError e) {
                    LevelEnterExt.ErrorMessage = e.Message ?? "Failed to generate area";
                    LevelEnter.Go(new Session(new AreaKey(1).SetSID("")), false);
                    return(AreaKey.None);
                }
            });
        }
示例#3
0
        private void MakeFrankenTilesets(RandoSettings settings)
        {
            var fgPaths = new List <string>();
            var bgPaths = new List <string>();
            var atPaths = new List <string>();

            foreach (var map in settings.EnabledMaps)
            {
                var meta   = AreaData.Get(map).GetMeta();
                var fgPath = meta?.ForegroundTiles;
                var bgPath = meta?.BackgroundTiles;
                var atPath = meta?.AnimatedTiles;
                if (!string.IsNullOrEmpty(fgPath) && !fgPaths.Contains(fgPath))
                {
                    fgPaths.Add(fgPath);
                }
                if (!string.IsNullOrEmpty(bgPath) && !bgPaths.Contains(bgPath))
                {
                    bgPaths.Add(bgPath);
                }
                if (!string.IsNullOrEmpty(atPath) && !atPaths.Contains(atPath))
                {
                    atPaths.Add(atPath);
                }
            }

            CombineAutotilers(GFX.FGAutotiler, fgPaths, settings);
            CombineAutotilers(GFX.BGAutotiler, bgPaths, settings);
            CombineAnimatedTiles(GFX.AnimatedTilesBank, atPaths, settings);
        }
        public static int ComputeScore(RandoSettings settings)
        {
            float time    = (float)TimeSpan.FromTicks(SaveData.Instance.CurrentSession.Time).TotalSeconds;
            float berries = Entities.LifeBerry.GrabbedLifeBerries.Carrying;
            float levels  = settings.EndlessLevel + 1;
            float score;

            // scoring consts
            // formatted like this in case we want to have per-settings scores
            float levelBonus, berryBonus, timeDecay;

            if (settings.SeedType == SeedType.Custom)
            {
                levelBonus = 150f;
                berryBonus = 40f;
                timeDecay  = 0.1f / (60f * 10f);
            }
            else
            {
                levelBonus = 450f;
                berryBonus = 60f;
                timeDecay  = 0.1f / (60f * 15f);
            }
            score = levels * levelBonus + berries * berryBonus - time - timeDecay / 2f * time * time;

            return((int)score);
        }
示例#5
0
 public override void Load()
 {
     Settings = this.SavedData.SavedSettings?.Copy() ?? new RandoSettings();
     LoadQol();
     LoadMechanics();
     LoadSessionLifecycle();
     LoadMenuLifecycle();
 }
示例#6
0
 public override void Load()
 {
     Settings = this.SavedData.SavedSettings?.Copy() ?? new RandoSettings();
     On.Celeste.GameLoader.Begin += LateInitialize;
     LoadQol();
     LoadMechanics();
     LoadSessionLifecycle();
     LoadMenuLifecycle();
     Entities.LifeBerry.Load();
 }
示例#7
0
 private RandoLogic(RandoSettings settings, AreaKey key)
 {
     this.Random   = new Random((int)settings.IntSeed);
     this.Settings = settings;
     this.Key      = key;
     this.ResetRooms();
     this.Caps = new Capabilities {
         Dashes      = settings.Dashes,
         PlayerSkill = settings.Difficulty,
         HasKey      = true,
     };
 }
示例#8
0
        public void FillMap(MapData map, RandoSettings settings, Random random)
        {
            foreach (var room in this.Rooms)
            {
                map.Levels.Add(room.Bake(this.nonce++, settings, random));
            }

            // set warp targets
            foreach (var room in this.Rooms)
            {
                foreach (var node in room.Nodes.Values)
                {
                    foreach (var edge in node.Edges)
                    {
                        var s = edge.CorrespondingEdge(node);
                        if (s.CustomWarp)
                        {
                            var baked = room.BakedRoom;
                            var dyn   = new DynData <LevelData>(baked);
                            dyn.Set <string>("CustomWarp", edge.OtherNode(node).Room.BakedRoom.Name);
                        }
                    }
                }

                if (room.WarpMap != null)
                {
                    var newMap = new Dictionary <string, string>();
                    foreach (var kv in room.WarpMap)
                    {
                        newMap[kv.Key] = kv.Value.BakedRoom.Name;
                    }
                    var dyn = new DynData <LevelData>(room.BakedRoom);
                    dyn.Set <Dictionary <string, string> >("WarpMapping", newMap);
                }

                room.Bake2();
            }

            var hasExtendedVariantTriggers = map.Levels.Exists(levelData =>
                                                               levelData.Triggers.Exists(entityData => extendedVariantsEntities.Contains(entityData.Name)) ||
                                                               levelData.Entities.Exists(entityData => extendedVariantsEntities.Contains(entityData.Name)));
            var hasIsaVariantTriggers = map.Levels.Exists(levelData =>
                                                          levelData.Triggers.Exists(entityData => entityData.Name == "ForceVariantTrigger"));
            var dyn2 = new DynData <MapData>(map);

            dyn2.Set <bool?>("HasExtendedVariantTriggers", hasExtendedVariantTriggers);
            dyn2.Set <bool?>("HasIsaVariantTriggers", hasIsaVariantTriggers);
        }
示例#9
0
        private RandoLogic(RandoSettings settings, AreaKey key)
        {
            this.Random         = new Random((int)settings.IntSeed);
            this.Settings       = settings;
            this.RemainingRooms = new List <StaticRoom>();
            foreach (var room in RandoLogic.AllRooms)
            {
                if (settings.MapIncluded(room.Area))
                {
                    this.RemainingRooms.Add(room);
                }
            }
            this.Key = key;

            this.Caps = new Capabilities {
                Dashes      = settings.Dashes,
                PlayerSkill = settings.Difficulty,
                HasKey      = true,
            };
        }
示例#10
0
 private RandoLogic(RandoSettings settings, AreaKey key, int tryNum)
 {
     this.Random = new Random((int)settings.IntSeed);
     for (int i = 0; i < settings.EndlessLevel; i++)
     {
         this.Random = new Random(this.Random.Next());
     }
     for (int i = 0; i < tryNum; i++)
     {
         this.Random.Next();
         this.Random = new Random(this.Random.Next());
     }
     this.Settings = settings;
     this.Key      = key;
     this.ResetRooms();
     this.Caps = new Capabilities {
         Dashes      = settings.Dashes,
         PlayerSkill = settings.Difficulty,
         HasKey      = true,
     };
 }
示例#11
0
        private static void CombineAutotilers(Autotiler basic, List <string> additions, RandoSettings settings)
        {
            var counts = new Dictionary <char, int>();
            var r      = new Random((int)settings.IntSeed);

            // uhhhhhhh this is intensely sketchy
            var lookup = (IDictionary)typeof(Autotiler).GetField("lookup", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(basic);

            foreach (char k in lookup.Keys)
            {
                counts[k] = 1;
            }

            foreach (var path in additions)
            {
                var advanced = new Autotiler(path);
                var lookup2  = (IDictionary)typeof(Autotiler).GetField("lookup", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(advanced);
                foreach (char k in lookup2.Keys)
                {
                    if (counts.ContainsKey(k))
                    {
                        counts[k]++;
                        if (r.Next(counts[k]) == 0)
                        {
                            lookup[k] = lookup2[k];
                        }
                    }
                    else
                    {
                        counts[k] = 1;
                        lookup[k] = lookup2[k];
                    }
                }
            }
        }
示例#12
0
        public static AreaKey GenerateMap(RandoSettings settings)
        {
            var lastarea = AreaData.Areas[AreaData.Areas.Count - 1];
            var newID    = AreaData.Areas.Count;

            if (lastarea.GetLevelSet() == "randomizer")
            {
                newID--;
            }

            var newArea = new AreaData {
                IntroType = Player.IntroTypes.WakeUp,
                Interlude = false,
                Dreaming  = false,
                ID        = newID,
                Name      = $"{settings.Seed}_{settings.Hash}",
                Mode      = new ModeProperties[3] {
                    new ModeProperties {
                        Inventory = settings.Dashes == NumDashes.Zero ? new PlayerInventory(0, true, true, false) :
                                    settings.Dashes == NumDashes.One ? PlayerInventory.Default :
                                    PlayerInventory.CH6End,
                    }, null, null
                },
                Icon                = AreaData.Areas[0].Icon,
                MountainIdle        = AreaData.Areas[0].MountainIdle,
                MountainZoom        = AreaData.Areas[0].MountainZoom,
                MountainState       = AreaData.Areas[0].MountainState,
                MountainCursor      = new Vector3(0, 100000, 0), // ???
                MountainSelect      = AreaData.Areas[0].MountainSelect,
                MountainCursorScale = AreaData.Areas[0].MountainCursorScale,
            };

            newArea.SetMeta(new Meta.MapMeta {
                Modes = new Meta.MapMetaModeProperties[] {
                    new Meta.MapMetaModeProperties {
                        HeartIsEnd = true,
                        //SeekerSlowdown = true,  // this doesn't do anything
                    },
                    null, null
                }
            });
            newArea.OnLevelBegin = (level) => {
                level.Add(new SeekerEffectsController());
            };

            newArea.SetSID($"randomizer/{newArea.Name}");
            if (lastarea.GetSID().StartsWith("randomizer/"))
            {
                AreaData.Areas[AreaData.Areas.Count - 1] = newArea;
            }
            else
            {
                // avert race condition
                RandoModule.AreaHandoff = newArea;
                while (RandoModule.AreaHandoff != null)
                {
                    Thread.Sleep(10);
                }
            }

            var key = new AreaKey(newArea.ID);

            var r = new RandoLogic(settings, key);

            newArea.Wipe = r.PickWipe();
            newArea.CompleteScreenName = r.PickCompleteScreen();
            newArea.CassetteSong       = r.PickCassetteAudio();
            newArea.Mode[0].AudioState = new AudioState(r.PickMusicAudio(), r.PickAmbienceAudio());
            newArea.Mode[0].MapData    = r.MakeMap();
            r.RandomizeDialog();

            Logger.Log("randomizer", $"new area {newArea.GetSID()}");

            return(key);
        }
示例#13
0
            public override LevelData Bake(int?nonce, RandoSettings settings, Random random)
            {
                var result = base.Bake(nonce, settings, random);

                int        maxID  = 0;
                EntityData granny = null;

                foreach (var e in result.Entities)
                {
                    maxID = Math.Max(maxID, e.ID);
                    if (e.Name == "npc")
                    {
                        granny = e;
                    }
                }
                result.Entities.Remove(granny);

                result.Spawns.Insert(0, new Vector2(336, 144));

                result.Entities.Add(new EntityData {
                    Name     = "summitGemManager",
                    ID       = ++maxID,
                    Level    = result,
                    Position = new Vector2(384, 136),
                    Nodes    = new Vector2[] {
                        new Vector2(330, 168),
                        new Vector2(346 + 4, 156),
                        new Vector2(362 + 8, 168),
                        new Vector2(378 + 12, 156),
                        new Vector2(394 + 16, 168),
                        new Vector2(410 + 20, 156)
                    }
                });

                result.Entities.Add(new EntityData {
                    Name     = "invisibleBarrier",
                    ID       = ++maxID,
                    Level    = result,
                    Position = new Vector2(392, 32),
                    Width    = 48,
                    Height   = 32
                });

                result.Entities.Add(new EntityData {
                    Name     = "blackGem",
                    ID       = ++maxID,
                    Level    = result,
                    Position = new Vector2(416, 48),
                });

                foreach (var pos in new Vector2[] {
                    new Vector2(400, 56),
                    new Vector2(408, 64),
                    new Vector2(416, 72),
                    new Vector2(424, 64),
                    new Vector2(432, 64),
                    new Vector2(440, 56),
                    new Vector2(440, 48),
                    new Vector2(432, 40),
                    new Vector2(432, 32),
                })
                {
                    result.Entities.Add(new EntityData {
                        Name     = "spinner",
                        ID       = ++maxID,
                        Level    = result,
                        Position = pos
                    });
                }

                return(result);
            }
示例#14
0
        private static void CombineAnimatedTiles(AnimatedTilesBank basic, List <string> additions, RandoSettings settings)
        {
            var counts = new Dictionary <string, int>();

            foreach (var key in basic.AnimationsByName.Keys)
            {
                counts[key] = 1;
            }
            var r = new Random((int)settings.IntSeed);

            foreach (var path in additions)
            {
                XmlElement animatedData = Calc.LoadContentXML(path)["Data"];
                foreach (XmlElement el in animatedData)
                {
                    if (el == null)
                    {
                        continue;
                    }

                    var  name = el.Attr("name");
                    bool insert;
                    if (counts.TryGetValue(name, out int count))
                    {
                        count++;
                        counts[name] = count;
                        insert       = r.Next(count) == 0;
                    }
                    else
                    {
                        counts[name] = 1;
                        insert       = true;
                    }

                    if (insert)
                    {
                        int idx = -1;
                        if (basic.AnimationsByName.ContainsKey(name))
                        {
                            idx = basic.Animations.IndexOf(basic.AnimationsByName[name]);
                            basic.AnimationsByName.Remove(name);
                        }
                        basic.Add(
                            name,
                            el.AttrFloat("delay", 0f),
                            el.AttrVector2("posX", "posY", Vector2.Zero),
                            el.AttrVector2("origX", "origY", Vector2.Zero),
                            GFX.Game.GetAtlasSubtextures(el.Attr("path"))
                            );
                        if (idx != -1)
                        {
                            basic.Animations[idx] = basic.AnimationsByName[name];
                            basic.Animations.RemoveAt(basic.Animations.Count - 1);
                        }
                    }
                }
            }
        }
示例#15
0
        public virtual LevelData Bake(int?nonce, RandoSettings settings, Random random)
        {
            var result = this.Static.MakeLevelData(new Vector2(this.Bounds.Left, this.Bounds.Top), nonce);

            this.BakedRoom = result;

            bool hasCassetteBlocks = false;

            foreach (var e in result.Entities)
            {
                if (e.Name == "cassetteBlock")
                {
                    hasCassetteBlocks = true;
                    break;
                }
            }

            bool ohgodwhat = random.Next(100) == 0; // :)

            string pickCrystalColor()
            {
tryagain:
                switch (random.Next(14))
                {
                case 0:
                case 1:
                case 2:
                    return("blue");

                case 3:
                case 4:
                case 5:
                    return("red");

                case 6:
                case 7:
                case 8:
                    return("purple");

                case 9:
                    return("rainbow");

                default:
                    // dust bunnies can't be shattered, lmao
                    if (this.Static.SpinnersShatter)
                    {
                        goto tryagain;
                    }
                    return("dust");
                }
            }

            string crystalcolor      = pickCrystalColor();
            string canonCrystalcolor = this.Static.Area.ID == 3 || this.Static.Area.ID == 7 && this.Static.Level.Name.StartsWith("d-") ? "dust" :
                                       this.Static.Area.ID == 5 ? "red" :
                                       this.Static.Area.ID == 6 ? "purple" :
                                       this.Static.Area.ID == 10 ? "rainbow" : "blue";

            string pickSpinnerColor()
            {
                return(new string[] { "dust", "spike", "star" }[random.Next(3)]);
            }

            string spinnercolor      = pickSpinnerColor();
            string canonSpinnerColor = this.Static.Area.ID == 10 ? "star" :
                                       this.Static.Area.ID == 3 || this.Static.Area.ID == 7 && this.Static.Level.Name.StartsWith("d-") ? "dust" : "spike";

            string pickSpikeColor()
            {
tryagain:
                var result2 = new string[] { "outline", "reflection", "tentacles", "cliffside" }[random.Next(4)];

                if (hasCassetteBlocks && result2 == "tentacles")
                {
                    goto tryagain;
                }
                return(result2);
            }

            string spikecolor      = pickSpikeColor();
            string canonSpikecolor = AreaData.Get(this.Static.Area).Spike;

            int maxID    = 0;
            var toRemove = new List <EntityData>();

            foreach (var e in result.Entities)
            {
                maxID = Math.Max(maxID, e.ID);

                switch (e.Name)
                {
                case "spinner":
                    if (ohgodwhat)
                    {
                        crystalcolor = pickCrystalColor();
                    }
                    if (e.Values == null)
                    {
                        e.Values = new Dictionary <string, object>();
                    }
                    if (settings.RandomDecorations)
                    {
                        if (crystalcolor == "dust")
                        {
                            e.Values["dust"] = true;
                        }
                        else
                        {
                            e.Values.Remove("dust");
                            e.Values["color"] = crystalcolor;
                        }
                    }
                    else if (!e.Has("dust") && !e.Has("color"))
                    {
                        if (canonCrystalcolor == "dust")
                        {
                            e.Values["dust"] = true;
                        }
                        else
                        {
                            e.Values["color"] = canonCrystalcolor;
                        }
                    }
                    break;

                case "trackSpinner":
                case "rotateSpinner":
                    if (ohgodwhat)
                    {
                        spinnercolor = pickSpinnerColor();
                    }
                    if (e.Values == null)
                    {
                        e.Values = new Dictionary <string, object>();
                    }
                    if (settings.RandomDecorations)
                    {
                        if (spinnercolor == "star")
                        {
                            e.Values["star"] = true;
                        }
                        else if (spinnercolor == "dust")
                        {
                            e.Values["dust"] = true;
                        }
                        else
                        {
                            e.Values.Remove("star");
                            e.Values.Remove("dust");
                        }
                    }
                    else if (!e.Has("star") && !e.Has("dust"))
                    {
                        if (canonSpinnerColor == "star")
                        {
                            e.Values["star"] = true;
                        }
                        else if (canonSpinnerColor == "dust")
                        {
                            e.Values["dust"] = true;
                        }
                        else
                        {
                            // no action
                        }
                    }
                    break;

                case "spikesUp":
                case "spikesDown":
                case "spikesLeft":
                case "spikesRight":
                    if (ohgodwhat)
                    {
                        spikecolor = pickSpikeColor();
                    }
                    if (e.Values == null)
                    {
                        e.Values = new Dictionary <string, object>();
                    }
                    if (settings.RandomDecorations)
                    {
                        bool single = ((e.Name == "spikesUp" || e.Name == "spikesDown") && e.Width == 8) || ((e.Name == "spikesLeft" || e.Name == "spikesRight") && e.Height == 8);
                        e.Values["type"] = single && spikecolor == "tentacles" ? "outline" : spikecolor;
                    }
                    else if (e.Attr("type", "default") == "default")
                    {
                        e.Values["type"] = canonSpikecolor;
                    }
                    break;

                case "lockBlock":
                    if (!this.UsedKeyholes.Contains(e.ID))
                    {
                        toRemove.Add(e);
                    }
                    break;
                }
            }
            foreach (var e in toRemove)
            {
                result.Entities.Remove(e);
            }

            void blockHole(Hole hole)
            {
                var topbottom = hole.Side == ScreenDirection.Up || hole.Side == ScreenDirection.Down;
                var farside   = hole.Side == ScreenDirection.Down || hole.Side == ScreenDirection.Right;

                Vector2 corner;

                switch (hole.Side)
                {
                case ScreenDirection.Up:
                    corner = new Vector2(0, -8);
                    break;

                case ScreenDirection.Left:
                    corner = new Vector2(-8, 0);
                    break;

                case ScreenDirection.Down:
                    corner = new Vector2(0, this.Bounds.Height);
                    break;

                case ScreenDirection.Right:
                default:
                    corner = new Vector2(this.Bounds.Width, 0);
                    break;
                }
                corner += hole.AlongDir.Unit() * hole.LowBound * 8;
                var e = new EntityData {
                    ID       = ++maxID,
                    Name     = "invisibleBarrier",
                    Width    = !topbottom ? 8 : hole.Size * 8,
                    Height   = topbottom ? 8 : hole.Size * 8,
                    Position = corner,
                    Level    = result,
                };

                result.Entities.Add(e);
            }

            void partiallyBlockHole(Hole hole, Hole hole2)
            {
                int diff         = hole.Compatible(hole2);
                var newHighBound = diff + hole2.LowBound - 1;
                var newHole      = new Hole(hole.Side, hole.LowBound, newHighBound, false);

                blockHole(newHole);
            }

            Hole neuterHole(Hole hole1, Hole hole2)
            {
                if (hole1.Size <= hole2.Size)
                {
                    return(hole1);
                }

                if ((hole1.Side == ScreenDirection.Up && hole1.Launch != null) || (hole2.Side == ScreenDirection.Up && hole2.Launch != null))
                {
                    return(hole1);
                }

                var  align     = hole1.Compatible(hole2);
                bool alignLeft = hole1.LowBound - align == hole2.LowBound;

                if (alignLeft)
                {
                    return(new Hole(hole1.Side, hole1.LowBound, hole1.LowBound + hole2.Size, false));
                }
                else
                {
                    return(new Hole(hole1.Side, hole1.HighBound - hole2.Size, hole1.HighBound, false));
                }
            }

            void beamHole(Hole hole)
            {
                Vector2 center;
                int     rotation;

                switch (hole.Side)
                {
                case ScreenDirection.Up:
                    center   = new Vector2(0, 0);
                    rotation = 0;
                    break;

                case ScreenDirection.Left:
                    center   = new Vector2(0, 0);
                    rotation = 270;
                    break;

                case ScreenDirection.Down:
                    center   = new Vector2(0, this.Bounds.Height);
                    rotation = 180;
                    break;

                case ScreenDirection.Right:
                default:
                    center   = new Vector2(this.Bounds.Width, 0);
                    rotation = 90;
                    break;
                }
                center += hole.AlongDir.Unit() * (hole.LowBound * 8 + hole.Size * 4);
                var e = new EntityData {
                    ID       = ++maxID,
                    Name     = "lightbeam",
                    Width    = hole.Size * 8,
                    Height   = 24,
                    Position = center,
                    Level    = result,
                    Values   = new Dictionary <string, object> {
                        { "rotation", (object)rotation },
                    }
                };

                result.Entities.Add(e);
            }

            void gateTopHole(Hole hole)
            {
                if (!hole.LowOpen)
                {
                    var coord = (hole.LowBound - 1) * 8;
                    var e     = new EntityData {
                        Name     = "invisibleBarrier",
                        Position = new Vector2(coord, -80),
                        Width    = 8,
                        Height   = 80,
                        Level    = result,
                        ID       = ++maxID,
                    };
                    result.Entities.Add(e);
                }
                if (!hole.HighOpen)
                {
                    var coord = (hole.HighBound + 1) * 8;
                    var e     = new EntityData {
                        Name     = "invisibleBarrier",
                        Position = new Vector2(coord, -80),
                        Width    = 8,
                        Height   = 80,
                        Level    = result,
                        ID       = ++maxID,
                    };
                    result.Entities.Add(e);
                }
            }

            var unusedHorizontalHoles = new HashSet <Hole>();
            var unusedTopHoles        = new HashSet <Hole>();
            var usedVerticalHoles     = new List <Hole>();

            foreach (var hole in this.Static.Holes)
            {
                if (hole.Side == ScreenDirection.Left || hole.Side == ScreenDirection.Right)
                {
                    unusedHorizontalHoles.Add(hole);
                }
                if (hole.Side == ScreenDirection.Up)
                {
                    unusedTopHoles.Add(hole);
                }
            }
            foreach (var node in this.Nodes.Values)
            {
                foreach (var edge in node.Edges)
                {
                    var edgeStatic = edge.CorrespondingEdge(node);
                    var hole       = edgeStatic.HoleTarget;
                    var hole2      = edge.OtherEdge(node).HoleTarget;
                    if (hole != null && hole2 != null && (hole.Side == ScreenDirection.Down || hole.Side == ScreenDirection.Up))
                    {
                        usedVerticalHoles.Add(neuterHole(hole, hole2));
                    }
                    if (hole != null && (hole.Side == ScreenDirection.Left || hole.Side == ScreenDirection.Right))
                    {
                        unusedHorizontalHoles.Remove(hole);
                    }
                    if (hole != null && hole.Side == ScreenDirection.Up)
                    {
                        unusedTopHoles.Remove(hole);
                    }

                    // Block off holes connected to edges which should not be re-entered
                    if (hole != null && hole2 != null && hole2.Kind == HoleKind.Out)
                    {
                        blockHole(hole);
                    }
                    // Block off pieces of holes which are partially unused
                    if (hole != null && hole2 != null && hole.Size > hole2.Size)
                    {
                        partiallyBlockHole(hole, hole2);
                    }
                    // Add beams
                    var shine = RandoModule.Instance.Settings.Lights;
                    if ((shine == ShineLights.On || (shine == ShineLights.Hubs && this.Static.Hub)) &&
                        hole != null && hole2 != null &&
                        (hole.Kind == HoleKind.Out || hole.Kind == HoleKind.InOut) &&
                        (hole2.Kind == HoleKind.In || hole2.Kind == HoleKind.Unknown || hole2.Kind == HoleKind.InOut))
                    {
                        beamHole(hole);
                    }
                }

                foreach (var kv in node.Collectables)
                {
                    string name = null;
                    var    col  = kv.Value.Item1;
                    switch (col)
                    {
                    case LinkedCollectable.Key:
                        name = "key";
                        break;

                    case LinkedCollectable.Strawberry:
                    case LinkedCollectable.WingedStrawberry:
                        name = "strawberry";
                        break;

                    case LinkedCollectable.Gem1:
                    case LinkedCollectable.Gem2:
                    case LinkedCollectable.Gem3:
                    case LinkedCollectable.Gem4:
                    case LinkedCollectable.Gem5:
                    case LinkedCollectable.Gem6:
                        name = "summitgem";
                        break;

                    case LinkedCollectable.LifeBerry:
                        name = "randomizer/LifeBerry";
                        break;
                    }

                    var e = new EntityData {
                        ID       = ++maxID,
                        Name     = name,
                        Level    = result,
                        Position = kv.Key.Position,
                        Values   = new Dictionary <string, object>(),
                    };
                    if (kv.Value.Item2)
                    {
                        e.Values["AutoBubble"] = true;
                    }
                    if (col == LinkedCollectable.WingedStrawberry)
                    {
                        e.Values["winged"] = "true";
                    }
                    if (col >= LinkedCollectable.Gem1 && col <= LinkedCollectable.Gem6)
                    {
                        e.Values["gem"] = (col - LinkedCollectable.Gem1).ToString();
                    }
                    result.Entities.Add(e);
                }
            }

            //result.DisableDownTransition = false; // overridden in hook, doesn't matter
            new DynData <LevelData>(result).Set("UsedVerticalHoles", usedVerticalHoles);
            foreach (var hole in unusedHorizontalHoles)
            {
                blockHole(hole);
            }
            foreach (var hole in unusedTopHoles)
            {
                gateTopHole(hole);
            }
            return(result);
        }
示例#16
0
        public static AreaKey GenerateMap(RandoSettings settings)
        {
            var  lastarea = AreaData.Areas[AreaData.Areas.Count - 1];
            var  newID    = AreaData.Areas.Count;
            bool secondVerseSameAsTheFirst = lastarea.GetSID().StartsWith("randomizer/");

            if (secondVerseSameAsTheFirst)
            {
                newID--;
            }

            var newArea = new AreaData {
                IntroType = Player.IntroTypes.WakeUp,
                Interlude = false,
                Dreaming  = false,
                ID        = newID,
                Name      = $"{settings.Seed}_{settings.Hash}",
                Mode      = new ModeProperties[3] {
                    new ModeProperties {
                        Inventory = settings.Dashes == NumDashes.Zero ? new PlayerInventory(0, true, true, false) :
                                    settings.Dashes == NumDashes.One ? PlayerInventory.Default :
                                    PlayerInventory.CH6End,
                    }, null, null
                },
                Icon                = AreaData.Areas[0].Icon,
                MountainIdle        = AreaData.Areas[0].MountainIdle,
                MountainZoom        = AreaData.Areas[0].MountainZoom,
                MountainState       = AreaData.Areas[0].MountainState,
                MountainCursor      = new Vector3(0, 100000, 0), // ???
                MountainSelect      = AreaData.Areas[0].MountainSelect,
                MountainCursorScale = AreaData.Areas[0].MountainCursorScale,
            };

            newArea.SetMeta(new Meta.MapMeta {
                Modes = new Meta.MapMetaModeProperties[] {
                    new Meta.MapMetaModeProperties {
                        HeartIsEnd = true,
                        //SeekerSlowdown = true,  // this doesn't do anything
                    },
                    null, null
                }
            });
            newArea.OnLevelBegin = (level) => {
                level.Add(new SeekerEffectsController());
            };
            var dyn = new DynData <AreaData>(newArea);

            dyn.Set <RandoSettings>("RandoSettings", settings.Copy());

            newArea.SetSID($"randomizer/{newArea.Name}");
            if (secondVerseSameAsTheFirst)
            {
                AreaData.Areas[AreaData.Areas.Count - 1] = newArea;
                // invalidate the MapEditor area key cache, as it will erroniously see a cache hit
                typeof(Editor.MapEditor).GetField("area", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, AreaKey.None);
            }
            else
            {
                // avert race condition
                RandoModule.AreaHandoff = newArea;
                while (RandoModule.AreaHandoff != null)
                {
                    Thread.Sleep(10);
                }
            }

            var key = new AreaKey(newArea.ID);

            var r = new RandoLogic(settings, key);

            newArea.Mode[0].MapData    = r.MakeMap();
            newArea.Wipe               = r.PickWipe();
            newArea.CompleteScreenName = r.PickCompleteScreen();
            newArea.CassetteSong       = r.PickCassetteAudio();
            newArea.Mode[0].AudioState = new AudioState(r.PickMusicAudio(), r.PickAmbienceAudio());
            r.RandomizeDialog();

            Logger.Log("randomizer", $"new area {newArea.GetSID()}");

            return(key);
        }
示例#17
0
 public RandoModule()
 {
     Instance = this;
     Settings = new RandoSettings();
 }
示例#18
0
        public static AreaKey GenerateMap(RandoSettings settings)
        {
            StringWriter builder = new StringWriter();

            builder.Write("Generating map with settings:\n");
            YamlHelper.Serializer.Serialize(builder, settings);
            Logger.Log("randomizer", builder.ToString());
            var newID = AreaData.Areas.Count;

            if (AreaData.Areas.Last().GetSID().StartsWith("randomizer/"))
            {
                newID--;
            }

            var newArea = new AreaData {
                IntroType = Player.IntroTypes.WakeUp,
                Interlude = false,
                Dreaming  = false,
                ID        = newID,
                Name      = $"{settings.Seed}_{settings.EndlessLevel}_{settings.Hash}",
                Mode      = new ModeProperties[3] {
                    new ModeProperties {
                        Inventory = settings.Dashes == NumDashes.Zero ? new PlayerInventory(0, true, false, false) :
                                    settings.Dashes == NumDashes.One ?  new PlayerInventory(1, true, false, false) :
                                    new PlayerInventory(2, true, false, false),
                    }, null, null
                },
                Icon                = AreaData.Areas[0].Icon,
                MountainIdle        = AreaData.Areas[0].MountainIdle,
                MountainZoom        = AreaData.Areas[0].MountainZoom,
                MountainState       = AreaData.Areas[0].MountainState,
                MountainCursor      = new Vector3(0, 100000, 0), // ???
                MountainSelect      = AreaData.Areas[0].MountainSelect,
                MountainCursorScale = AreaData.Areas[0].MountainCursorScale,
            };

            newArea.SetMeta(new Meta.MapMeta {
                Modes = new Meta.MapMetaModeProperties[] {
                    new Meta.MapMetaModeProperties {
                        HeartIsEnd = true,
                        //SeekerSlowdown = true,  // this doesn't do anything
                    },
                    null, null
                }
            });
            newArea.OnLevelBegin = (level) => {
                level.Add(new SeekerEffectsController());
            };
            var dyn = new DynData <AreaData>(newArea);

            dyn.Set <RandoSettings>("RandoSettings", settings.Copy());

            newArea.SetSID($"randomizer/{newArea.Name}");

            // avert race condition
            RandoModule.AreaHandoff = newArea;
            while (RandoModule.AreaHandoff != null)
            {
                Thread.Sleep(10);
            }

            // invalidate the MapEditor area key cache, as it will erroniously see a cache hit
            typeof(Editor.MapEditor).GetField("area", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, AreaKey.None);

            var key = new AreaKey(newArea.ID);

            int tryNum = 0;

            while (true)
            {
                try {
                    var r = new RandoLogic(settings, key, tryNum);

                    newArea.Mode[0].MapData    = r.MakeMap();
                    newArea.Wipe               = r.PickWipe();
                    newArea.CompleteScreenName = r.PickCompleteScreen();
                    newArea.CassetteSong       = r.PickCassetteAudio();
                    newArea.Mode[0].AudioState = new AudioState(r.PickMusicAudio(), r.PickAmbienceAudio());
                    if (settings.RandomColors)
                    {
                        newArea.BloomBase     = (float)Math.Pow(r.Random.NextFloat(), 5) * r.Random.NextFloat();
                        newArea.DarknessAlpha = r.Random.NextFloat() * (float)Math.Pow(r.Random.NextFloat(), 0.5) * (float)Math.Pow(r.Random.NextFloat(), 2) * 0.35f;
                        newArea.ColorGrade    = r.PickColorGrade();
                    }
                    r.RandomizeDialog();
                    break;
                } catch (RetryException) {
                    tryNum++;
                    if (tryNum >= 500)
                    {
                        throw new GenerationError("Cannot create map with these settings");
                    }
                    Logger.Log("randomizer", $"Too many retries ({tryNum}), starting again");
                }
            }

            Logger.Log("randomizer", $"new area {newArea.GetSID()}");

            return(key);
        }
示例#19
0
        private static void CombineSprites(SpriteBank bankOrig, List <string> additions, RandoSettings settings)
        {
            var counts = new Dictionary <string, int>();

            foreach (var key in bankOrig.SpriteData.Keys)
            {
                counts[key] = 1;
            }
            var r = new Random((int)settings.IntSeed);

            foreach (var addition in additions)
            {
                var bankMod = new SpriteBank(GFX.Game, addition);

                foreach (KeyValuePair <string, SpriteData> kvpBank in bankMod.SpriteData)
                {
                    string     key      = kvpBank.Key;
                    SpriteData valueMod = kvpBank.Value;

                    if (bankOrig.SpriteData.TryGetValue(key, out SpriteData valueOrig))
                    {
                        IDictionary animsOrig = valueOrig.Sprite.GetAnimations();
                        IDictionary animsMod  = valueMod.Sprite.GetAnimations();
                        foreach (DictionaryEntry kvpAnim in animsMod)
                        {
                            animsOrig[kvpAnim.Key] = kvpAnim.Value;
                        }

                        valueOrig.Sources.AddRange(valueMod.Sources);

                        // replay the starting animation to be sure it is referring to the new sprite.
                        valueOrig.Sprite.Stop();
                        if (valueMod.Sprite.CurrentAnimationID != "")
                        {
                            valueOrig.Sprite.Play(valueMod.Sprite.CurrentAnimationID);
                        }
                    }
                    else
                    {
                        bankOrig.SpriteData[key] = valueMod;
                    }
                }
            }
        }