public static new void Load() { orig_Load(); // assign SIDs and CheckpointData.Area for vanilla maps. foreach (AreaData area in Areas) { area.SetSID("Celeste/" + area.Mode[0].Path); for (int modeId = 0; modeId < area.Mode.Length; modeId++) { ModeProperties mode = area.Mode[modeId]; if (mode?.Checkpoints == null) { continue; } foreach (CheckpointData checkpoint in mode.Checkpoints) { checkpoint.SetArea(area.ToKey((AreaMode)modeId)); } } } // Separate array as we sort it afterwards. List <AreaData> modAreas = new List <AreaData>(); lock (Everest.Content.Map) { foreach (ModAsset asset in Everest.Content.Map.Values.Where(asset => asset.Type == typeof(AssetTypeMap))) { string path = asset.PathVirtual.Substring(5); AreaData area = new AreaData(); // Default values. area.SetSID(path); area.Name = path; area.Icon = "areas/" + path.ToLowerInvariant(); if (!GFX.Gui.Has(area.Icon)) { area.Icon = "areas/null"; } area.Interlude = false; area.CanFullClear = true; area.TitleBaseColor = Calc.HexToColor("6c7c81"); area.TitleAccentColor = Calc.HexToColor("2f344b"); area.TitleTextColor = Color.White; area.IntroType = Player.IntroTypes.WakeUp; area.Dreaming = false; area.ColorGrade = null; area.Mode = new ModeProperties[] { new ModeProperties { Inventory = PlayerInventory.Default, AudioState = new AudioState(SFX.music_city, SFX.env_amb_00_main) } }; area.Wipe = (Scene scene, bool wipeIn, Action onComplete) => new AngledWipe(scene, wipeIn, onComplete); area.DarknessAlpha = 0.05f; area.BloomBase = 0f; area.BloomStrength = 1f; area.Jumpthru = "wood"; area.CassseteNoteColor = Calc.HexToColor("33a9ee"); area.CassetteSong = SFX.cas_01_forsaken_city; // Custom values can be set via the MapMeta. MapMeta meta = new MapMeta(); meta.ApplyTo(area); MapMeta metaLoaded = asset.GetMeta <MapMeta>(); if (metaLoaded != null) { area.SetMeta(null); metaLoaded.ApplyTo(area); meta = metaLoaded; } if (string.IsNullOrEmpty(area.Mode[0].Path)) { area.Mode[0].Path = asset.PathVirtual.Substring(5); } // Some of the game's code checks for [1] / [2] explicitly. // Let's just provide null modes to fill any gaps. meta.Modes = meta.Modes ?? new MapMetaModeProperties[3]; if (meta.Modes.Length < 3) { MapMetaModeProperties[] larger = new MapMetaModeProperties[3]; for (int i = 0; i < meta.Modes.Length; i++) { larger[i] = meta.Modes[i]; } meta.Modes = larger; } if (area.Mode.Length < 3) { ModeProperties[] larger = new ModeProperties[3]; for (int i = 0; i < area.Mode.Length; i++) { larger[i] = area.Mode[i]; } area.Mode = larger; } // Celeste levelset always appears first. if (area.GetLevelSet() == "Celeste") { Areas.Add(area); } else { modAreas.Add(area); } // Some special handling. area.OnLevelBegin = (level) => { MapMeta levelMeta = AreaData.Get(level.Session).GetMeta(); MapMetaModeProperties levelMetaMode = level.Session.MapData.GetMeta(); if (levelMetaMode?.SeekerSlowdown ?? false) { level.Add(new SeekerEffectsController()); } }; } } // Merge modAreas into Areas. Areas.AddRange(modAreas); // Find duplicates and remove any earlier copies. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; int otherIndex = Areas.FindIndex(other => other.GetSID() == area.GetSID()); if (otherIndex < i) { Areas[otherIndex] = area; Areas.RemoveAt(i); i--; } } // Sort areas. Areas.Sort(AreaComparison); // Remove AreaDatas which are now a mode of another AreaData. // This can happen late as the map data (.bin) can contain additional metadata. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; string path = area.Mode[0].Path; int otherIndex = Areas.FindIndex(other => other.Mode.Any(otherMode => otherMode?.Path == path)); if (otherIndex != -1 && otherIndex != i) { Areas.RemoveAt(i); i--; continue; } int? order; AreaMode side; string name; ParseName(path, out order, out side, out name); // Also check for .bins possibly belonging to A side .bins by their path and lack of existing modes. for (int ii = 0; ii < Areas.Count; ii++) { AreaData other = Areas[ii]; int? otherOrder; AreaMode otherSide; string otherName; ParseName(other.Mode[0].Path, out otherOrder, out otherSide, out otherName); if (area.GetLevelSet() == other.GetLevelSet() && order == otherOrder && name == otherName && side != otherSide && !other.HasMode(side)) { if (other.Mode[(int)side] == null) { other.Mode[(int)side] = new ModeProperties { Inventory = PlayerInventory.Default, AudioState = new AudioState(SFX.music_city, SFX.env_amb_00_main) } } ; other.Mode[(int)side].Path = path; Areas.RemoveAt(i); i--; break; } } } for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; area.ID = i; // Clean up non-existing modes. int modei = 0; for (; modei < area.Mode.Length; modei++) { ModeProperties mode = area.Mode[modei]; if (mode == null || string.IsNullOrEmpty(mode.Path)) { break; } } Array.Resize(ref area.Mode, modei); Logger.Log("AreaData", $"{i}: {area.GetSID()} - {area.Mode.Length} sides"); // Update old MapData areas and load any new areas. // Add the A side MapData or update its area key. if (area.Mode[0].MapData != null) { area.Mode[0].MapData.Area = area.ToKey(); } else { area.Mode[0].MapData = new MapData(area.ToKey()); } if (area.IsInterludeUnsafe()) { continue; } // A and (some) B sides have PoemIDs. Can be overridden via empty PoemID. if (area.Mode[0].PoemID == null) { area.Mode[0].PoemID = area.GetSID().DialogKeyify() + "_A"; } if (area.Mode.Length > 1 && area.Mode[1] != null && area.Mode[1].PoemID == null) { area.Mode[1].PoemID = area.GetSID().DialogKeyify() + "_B"; } // Update all other existing mode's area keys. for (int mode = 1; mode < area.Mode.Length; mode++) { if (area.Mode[mode] == null) { continue; } if (area.Mode[mode].MapData != null) { area.Mode[mode].MapData.Area = area.ToKey((AreaMode)mode); } else { area.Mode[mode].MapData = new MapData(area.ToKey((AreaMode)mode)); } } } // Load custom mountains // This needs to be done after areas are loaded because it depends on the MapMeta MTNExt.LoadMod(); MTNExt.LoadModData(); }
public static new void Load() { orig_Load(); foreach (AreaData area in Areas) { area.SetSID("Celeste/" + area.Mode[0].Path); } // Separate array as we sort it afterwards. List <AreaData> modAreas = new List <AreaData>(); foreach (ModAsset asset in Everest.Content.ListMaps) { string path = asset.PathMapped.Substring(5); MapMeta meta = asset.GetMeta <MapMeta>(); AreaData area = new AreaData(); // Default values. area.SetSID(path); area.Name = path; area.Icon = "areas/" + path.ToLowerInvariant(); if (!GFX.Gui.Has(area.Icon)) { area.Icon = "areas/null"; } area.TitleBaseColor = Calc.HexToColor("6c7c81"); area.TitleAccentColor = Calc.HexToColor("2f344b"); area.TitleTextColor = Color.White; area.IntroType = Player.IntroTypes.WakeUp; area.Dreaming = false; area.ColorGrade = null; area.Mode = new ModeProperties[] { new ModeProperties { Path = asset.PathMapped.Substring(5), Inventory = PlayerInventory.Default, AudioState = new AudioState(Sfxs.music_city, Sfxs.env_amb_00_main) } }; area.Wipe = (Scene scene, bool wipeIn, Action onComplete) => new AngledWipe(scene, wipeIn, onComplete); area.DarknessAlpha = 0.05f; area.BloomBase = 0f; area.BloomStrength = 1f; area.Jumpthru = "wood"; area.CassseteNoteColor = Calc.HexToColor("33a9ee"); area.CassetteSong = Sfxs.cas_01_forsaken_city; // Custom values. if (meta != null) { if (!string.IsNullOrEmpty(meta.Name)) { area.Name = meta.Name; } if (!string.IsNullOrEmpty(meta.SID)) { area.SetSID(meta.SID); } if (!string.IsNullOrEmpty(meta.Icon) && GFX.Gui.Has(meta.Icon)) { area.Icon = meta.Icon; } area.Interlude = meta.Interlude; if (!string.IsNullOrEmpty(meta.CompleteScreenName)) { area.CompleteScreenName = meta.CompleteScreenName; } area.CassetteCheckpointIndex = meta.CassetteCheckpointIndex; if (!string.IsNullOrEmpty(meta.TitleBaseColor)) { area.TitleBaseColor = Calc.HexToColor(meta.TitleBaseColor); } if (!string.IsNullOrEmpty(meta.TitleAccentColor)) { area.TitleAccentColor = Calc.HexToColor(meta.TitleAccentColor); } if (!string.IsNullOrEmpty(meta.TitleTextColor)) { area.TitleTextColor = Calc.HexToColor(meta.TitleTextColor); } area.IntroType = meta.IntroType; area.Dreaming = meta.Dreaming; if (!string.IsNullOrEmpty(meta.ColorGrade)) { area.ColorGrade = meta.ColorGrade; } area.Mode = MapMeta.Convert(meta.Modes) ?? area.Mode; if (!string.IsNullOrEmpty(meta.Wipe)) { Type type = Assembly.GetEntryAssembly().GetType(meta.Wipe); ConstructorInfo ctor = type?.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) }); if (type != null && ctor != null) { area.Wipe = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete }); } } area.DarknessAlpha = meta.DarknessAlpha; area.BloomBase = meta.BloomBase; area.BloomStrength = meta.BloomStrength; if (!string.IsNullOrEmpty(meta.Jumpthru)) { area.Jumpthru = meta.Jumpthru; } if (!string.IsNullOrEmpty(meta.CassetteNoteColor)) { area.CassseteNoteColor = Calc.HexToColor(meta.CassetteNoteColor); } if (!string.IsNullOrEmpty(meta.CassetteSong)) { area.CassetteSong = meta.CassetteSong; } area.MountainIdle = meta.Mountain?.Idle?.Convert() ?? area.MountainIdle; area.MountainSelect = meta.Mountain?.Select?.Convert() ?? area.MountainSelect; area.MountainZoom = meta.Mountain?.Zoom?.Convert() ?? area.MountainZoom; area.MountainCursor = meta.Mountain?.Cursor?.ToVector3() ?? area.MountainCursor; area.MountainState = meta.Mountain?.State ?? area.MountainState; area.SetCompleteScreenMeta(meta.CompleteScreen); } // Some of the game's code checks for [1] / [2] explicitly. // Let's just provide null modes to fill any gaps. if (area.Mode.Length < 3) { ModeProperties[] larger = new ModeProperties[3]; for (int i = 0; i < area.Mode.Length; i++) { larger[i] = area.Mode[i]; } area.Mode = larger; } // Celeste levelset always appears first. if (area.GetLevelSet() == "Celeste") { Areas.Add(area); } else { modAreas.Add(area); } } // Sort and merge modAreas into Areas. Makes for easier levelset handling. Areas.Sort(AreaComparison); modAreas.Sort(AreaComparison); Areas.AddRange(modAreas); // Find duplicates and remove the earlier copy. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; int otherIndex = Areas.FindIndex(other => other.GetSID() == area.GetSID()); if (otherIndex < i) { Areas[otherIndex] = area; Areas.RemoveAt(i); i--; } } // Remove AreaDatas which are now a mode of another AreaData. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; int otherIndex = Areas.FindIndex(other => other.Mode.Any(otherMode => otherMode?.Path == area.Mode[0].Path)); if (otherIndex != -1 && otherIndex != i) { Areas.RemoveAt(i); i--; } } // Update old MapData areas and load any new areas. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; area.ID = i; if (area.Mode[0].MapData != null) { area.Mode[0].MapData.Area = area.ToKey(); } else { area.Mode[0].MapData = new MapData(area.ToKey()); } if (area.Interlude) { continue; } for (int mode = 1; mode < area.Mode.Length; mode++) { if (area.Mode[mode] == null) { continue; } if (area.Mode[mode].MapData != null) { area.Mode[mode].MapData.Area = area.ToKey((AreaMode)mode); } else { area.Mode[mode].MapData = new MapData(area.ToKey((AreaMode)mode)); } } } }
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 void ApplyTo(AreaData area) { if (!string.IsNullOrEmpty(Name)) { area.Name = Name; } if (!string.IsNullOrEmpty(SID)) { area.SetSID(SID); } if (!string.IsNullOrEmpty(Icon) && GFX.Gui.Has(Icon)) { area.Icon = Icon; } area.Interlude = Interlude; if (!string.IsNullOrEmpty(CompleteScreenName)) { area.CompleteScreenName = CompleteScreenName; } area.CassetteCheckpointIndex = CassetteCheckpointIndex; if (!string.IsNullOrEmpty(TitleBaseColor)) { area.TitleBaseColor = Calc.HexToColor(TitleBaseColor); } if (!string.IsNullOrEmpty(TitleAccentColor)) { area.TitleAccentColor = Calc.HexToColor(TitleAccentColor); } if (!string.IsNullOrEmpty(TitleTextColor)) { area.TitleTextColor = Calc.HexToColor(TitleTextColor); } area.IntroType = IntroType; area.Dreaming = Dreaming; if (!string.IsNullOrEmpty(ColorGrade)) { area.ColorGrade = ColorGrade; } ModeProperties[] modes = area.Mode; area.Mode = Convert(Modes) ?? modes; if (modes != null) { for (int i = 0; i < area.Mode.Length && i < modes.Length; i++) { if (area.Mode[i] == null) { area.Mode[i] = modes[i]; } } } if (!string.IsNullOrEmpty(Wipe)) { Type type = Assembly.GetEntryAssembly().GetType(Wipe); ConstructorInfo ctor = type?.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) }); if (type != null && ctor != null) { area.Wipe = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete }); } } area.DarknessAlpha = DarknessAlpha; area.BloomBase = BloomBase; area.BloomStrength = BloomStrength; if (!string.IsNullOrEmpty(Jumpthru)) { area.Jumpthru = Jumpthru; } area.CoreMode = CoreMode; if (!string.IsNullOrEmpty(CassetteNoteColor)) { area.CassseteNoteColor = Calc.HexToColor(CassetteNoteColor); } if (!string.IsNullOrEmpty(CassetteSong)) { area.CassetteSong = CassetteSong; } area.MountainIdle = Mountain?.Idle?.Convert() ?? area.MountainIdle; area.MountainSelect = Mountain?.Select?.Convert() ?? area.MountainSelect; area.MountainZoom = Mountain?.Zoom?.Convert() ?? area.MountainZoom; area.MountainCursor = Mountain?.Cursor?.ToVector3() ?? area.MountainCursor; area.MountainState = Mountain?.State ?? area.MountainState; area.SetMeta(this); }
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); }
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); }