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); } }); }
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); }
public override void Load() { Settings = this.SavedData.SavedSettings?.Copy() ?? new RandoSettings(); LoadQol(); LoadMechanics(); LoadSessionLifecycle(); LoadMenuLifecycle(); }
public override void Load() { Settings = this.SavedData.SavedSettings?.Copy() ?? new RandoSettings(); On.Celeste.GameLoader.Begin += LateInitialize; LoadQol(); LoadMechanics(); LoadSessionLifecycle(); LoadMenuLifecycle(); Entities.LifeBerry.Load(); }
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, }; }
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); }
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, }; }
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, }; }
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]; } } } }
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); }
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); }
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); } } } } }
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); }
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); }
public RandoModule() { Instance = this; Settings = new RandoSettings(); }
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); }
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; } } } }