public new void CleanCheckpoints() { if (string.IsNullOrEmpty(SID) && (ID_Unsafe < 0 || AreaData.Areas.Count <= ID_Unsafe)) { throw new Exception($"SaveData contains invalid AreaStats with no SID and out-of-range ID of {ID_Unsafe} / {AreaData.Areas.Count}"); } AreaData area = AreaData.Get(ID); for (int i = 0; i < Modes.Length; i++) { AreaMode areaMode = (AreaMode)i; AreaModeStats areaModeStats = Modes[i]; ModeProperties modeProperties = null; if (area.HasMode(areaMode)) { modeProperties = area.Mode[i]; } HashSet <string> checkpoints = new HashSet <string>(areaModeStats.Checkpoints); areaModeStats.Checkpoints.Clear(); if (modeProperties != null && modeProperties.Checkpoints != null) { foreach (CheckpointData checkpointData in modeProperties.Checkpoints) { if (checkpoints.Contains(checkpointData.Level)) { areaModeStats.Checkpoints.Add(checkpointData.Level); } } } } }
public new void CleanCheckpoints() { AreaData area = AreaData.Get(ID); for (int i = 0; i < Modes.Length; i++) { AreaMode areaMode = (AreaMode)i; AreaModeStats areaModeStats = Modes[i]; ModeProperties modeProperties = null; if (area.HasMode(areaMode)) { modeProperties = area.Mode[i]; } HashSet <string> checkpoints = new HashSet <string>(areaModeStats.Checkpoints); areaModeStats.Checkpoints.Clear(); if (modeProperties != null && modeProperties.Checkpoints != null) { foreach (CheckpointData checkpointData in modeProperties.Checkpoints) { if (checkpoints.Contains(checkpointData.Level)) { areaModeStats.Checkpoints.Add(checkpointData.Level); } } } } }
private static HashSet <string> _GetCheckpoints(SaveData save, AreaKey area) { // TODO: Maybe switch back to using SaveData.GetCheckpoints in the future? if (Celeste.PlayMode == Celeste.PlayModes.Event) { return(new HashSet <string>()); } HashSet <string> set; AreaData areaData = AreaData.Areas[area.ID]; ModeProperties mode = areaData.Mode[(int)area.Mode]; if (save.DebugMode || save.CheatMode) { set = new HashSet <string>(); if (mode.Checkpoints != null) { foreach (CheckpointData cp in mode.Checkpoints) { set.Add($"{(AreaData.Get(cp.GetArea()) ?? areaData).GetSID()}|{cp.Level}"); } } return(set); } AreaModeStats areaModeStats = save.Areas[area.ID].Modes[(int)area.Mode]; set = areaModeStats.Checkpoints; // Perform the same "cleanup" as SaveData.GetCheckpoints, but copy the set when adding area SIDs. if (mode == null) { set.Clear(); return(set); } set.RemoveWhere((string a) => !mode.Checkpoints.Any((CheckpointData b) => b.Level == a)); AreaData[] subs = AreaData.Areas.Where(other => other.GetMeta()?.Parent == areaData.GetSID() && other.HasMode(area.Mode) ).ToArray(); return(new HashSet <string>(set.Select(s => { foreach (AreaData sub in subs) { foreach (CheckpointData cp in sub.Mode[(int)area.Mode].Checkpoints) { if (cp.Level == s) { return $"{sub.GetSID()}|{s}"; } } } return s; }))); }
public MapDataFixup(MapData map) { MapData = map; AreaKey = map.Area; AreaData = AreaData.Get(AreaKey); Mode = AreaData.Mode[(int)AreaKey.Mode]; foreach (EverestModule module in Everest._Modules) { module.PrepareMapDataProcessors(this); } }
private void SetButtons(ModeProperties mp) { GameObject btnGo = Instantiate(_buttonPrefab); btnGo.transform.SetParent(_buttonsLayout.transform); btnGo.name = mp.mode.ToString(); Button btn = btnGo.GetComponent <Button>(); Image img = btnGo.GetComponent <Image>(); btn.onClick.AddListener(() => CurrentEditMode = mp.mode); img.sprite = mp.buttonSprite; }
public static ModeProperties[] Convert(MapMetaModeProperties[] meta) { if (meta == null || meta.Length == 0) { return(null); } ModeProperties[] data = new ModeProperties[meta.Length]; for (int i = 0; i < meta.Length; i++) { data[i] = meta[i]?.Convert(); } return(data); }
public MapDataFixup(MapData map) { MapData = map; AreaKey = map.Area; AreaData = AreaData.Get(AreaKey); ParentAreaData = AreaDataExt.Get(AreaData.GetMeta()?.Parent) ?? AreaData; Mode = AreaData.Mode[(int)AreaKey.Mode]; ParentMode = ParentAreaData.Mode.ElementAtOrDefault((int)AreaKey.Mode) ?? Mode; ParentMapData = ParentMode?.MapData ?? map; foreach (EverestModule module in Everest._Modules) { module.PrepareMapDataProcessors(this); } }
public void ApplyTo(AreaData area, AreaMode mode) { area.GetMeta().Modes[(int)mode] = this; ModeProperties props = area.Mode[(int)mode]; if (props != null) { props.AudioState = AudioState?.Convert() ?? props.AudioState; props.Checkpoints = MapMeta.Convert(Checkpoints) ?? props.Checkpoints; props.IgnoreLevelAudioLayerData = IgnoreLevelAudioLayerData ?? props.IgnoreLevelAudioLayerData; props.Inventory = MapMeta.GetInventory(Inventory) ?? props.Inventory; props.Path = Path ?? props.Path; props.PoemID = PoemID ?? props.PoemID; } else { props = Convert(); } area.Mode[(int)mode] = props; }
private static int countTotalHeartGemsForMapsThatHaveHearts(LevelSetStats levelSetStats) { return(levelSetStats.AreasIncludingCeleste.Sum((AreaStats area) => { int totalHeartGems = 0; ModeProperties[] propertiesOfAllModes = AreaData.Get(area.GetSID())?.Mode ?? new ModeProperties[0]; for (int i = 0; i < propertiesOfAllModes.Length && i < area.Modes.Length; i++) { if (area.Modes[i].HeartGem) { // the crystal heart of this map/mode was collected, so check if it has one before counting it in. ModeProperties modeProperties = propertiesOfAllModes[i]; if (modeProperties?.MapData != null && modeProperties.MapData.Area.Mode <= AreaMode.CSide) { totalHeartGems += (modeProperties.MapData.DetectedHeartGem ? 1 : 0); } } } return totalHeartGems; })); }
public new void Added(Scene scene) { orig_Added(scene); Session session = (scene as Level)?.Session; if (checkpoint != null || session == null || session.Area.GetLevelSet() == "Celeste") { return; } AreaKey area = session.Area; ModeProperties mode = AreaData.Areas[area.ID].Mode[(int)area.Mode]; if (mode.Checkpoints == null) { return; } HashSet <string> cps = SaveData.Instance.GetCheckpoints(area); CheckpointData cp = null; foreach (CheckpointData checkpointData2 in mode.Checkpoints) { bool flag2 = session.LevelFlags.Contains(checkpointData2.Level) && cps.Contains(checkpointData2.Level); if (flag2) { cp = checkpointData2; } } string id = GetCheckpointPreviewName(area, cp?.Level); if (MTN.Checkpoints.Has(id)) { checkpoint = MTN.Checkpoints[id]; } }
public new HashSet <string> GetCheckpoints(AreaKey area) { HashSet <string> checkpoints = orig_GetCheckpoints(area); if (Celeste.PlayMode == Celeste.PlayModes.Event || DebugMode || CheatMode) { return(checkpoints); } // Remove any checkpoints which don't exist in the level. ModeProperties mode = AreaData.Get(area).Mode[(int)area.Mode]; if (mode == null) { checkpoints.Clear(); } else { checkpoints.RemoveWhere(a => !mode.Checkpoints.Any(b => b.Level == a)); } return(checkpoints); }
private void ProcessLevels(BinaryPacker.Element levels) { AreaData area = AreaData.Get(Area); ModeProperties mode = area.Mode[(int)Area.Mode]; // Mod levels are... different. // levels.Children.Sort((a, b) => a.Attr("name").Replace("lvl_", "").CompareTo(b.Attr("name").Replace("lvl_", ""))); int checkpoint = 0; List <CheckpointData> checkpointsAuto = null; if (mode.Checkpoints == null) { checkpointsAuto = new List <CheckpointData>(); } int strawberry = 0; int strawberryInCheckpoint = 0; if (levels.Children != null) { foreach (BinaryPacker.Element level in levels.Children) { string[] levelTags = level.Attr("name").Split(':'); string levelName = levelTags[0]; if (levelName.StartsWith("lvl_")) { levelName = levelName.Substring(4); } level.SetAttr("name", "lvl_" + levelName); // lvl_ was optional before Celeste 1.2.5.0 made it mandatory. BinaryPacker.Element entities = level.Children.FirstOrDefault(el => el.Name == "entities"); BinaryPacker.Element triggers = level.Children.FirstOrDefault(el => el.Name == "triggers"); // Celeste 1.2.5.0 optimizes BinaryPacker, which causes some issues. // Let's "unoptimize" entities and triggers. if (entities == null) { level.Children.Add(entities = new BinaryPacker.Element { Name = "entities" }); } if (entities.Children == null) { entities.Children = new List <BinaryPacker.Element>(); } if (triggers == null) { level.Children.Add(triggers = new BinaryPacker.Element { Name = "triggers" }); } if (triggers.Children == null) { triggers.Children = new List <BinaryPacker.Element>(); } if (levelTags.Contains("checkpoint") || levelTags.Contains("cp")) { entities.Children.Add(new BinaryPacker.Element { Name = "checkpoint", Attributes = new Dictionary <string, object>() { { "x", "0" }, { "y", "0" } } }); } if (level.AttrBool("space")) { if (level.AttrBool("spaceSkipWrap") || levelTags.Contains("nospacewrap") || levelTags.Contains("nsw")) { entities.Children.Add(new BinaryPacker.Element { Name = "everest/spaceControllerBlocker" }); } if (level.AttrBool("spaceSkipGravity") || levelTags.Contains("nospacegravity") || levelTags.Contains("nsg")) { entities.Children.Add(new BinaryPacker.Element { Name = "everest/spaceController" }); level.SetAttr("space", false); } if (!levelTags.Contains("nospacefix") && !levelTags.Contains("nsf") && !triggers.Children.Any(el => el.Name == "cameraTargetTrigger") && !entities.Children.Any(el => el.Name == "everest/spaceControllerBlocker")) { // Camera centers tile-perfectly on uneven heights. int heightForCenter = level.AttrInt("height"); heightForCenter /= 8; if (heightForCenter % 2 == 0) { heightForCenter--; } heightForCenter *= 8; triggers.Children.Add(new BinaryPacker.Element { Name = "cameraTargetTrigger", Attributes = new Dictionary <string, object>() { { "x", 0f }, { "y", 0f }, { "width", level.AttrInt("width") }, { "height", level.AttrInt("height") }, { "yOnly", true }, { "lerpStrength", 1f } }, Children = new List <BinaryPacker.Element>() { new BinaryPacker.Element { Attributes = new Dictionary <string, object>() { { "x", 160f }, { "y", heightForCenter / 2f } } } } }); } } foreach (BinaryPacker.Element levelChild in level.Children) { switch (levelChild.Name) { case "entities": foreach (BinaryPacker.Element entity in levelChild.Children) { switch (entity.Name) { case "checkpoint": if (checkpointsAuto != null) { CheckpointData c = new CheckpointData( levelName, (area.GetSID() + "_" + levelName).DialogKeyify(), MapMeta.GetInventory(entity.Attr("inventory")), entity.Attr("dreaming") == "" ? area.Dreaming : entity.AttrBool("dreaming"), null ); int id = entity.AttrInt("checkpointID", -1); if (id == -1) { checkpointsAuto.Add(c); } else { while (checkpointsAuto.Count <= id) { checkpointsAuto.Add(null); } checkpointsAuto[id] = c; } } checkpoint++; strawberryInCheckpoint = 0; break; case "cassette": if (area.CassetteCheckpointIndex == 0) { area.CassetteCheckpointIndex = checkpoint; } break; case "strawberry": if (entity.AttrInt("checkpointID", -1) == -1) { entity.SetAttr("checkpointID", checkpoint); } if (entity.AttrInt("order", -1) == -1) { entity.SetAttr("order", strawberryInCheckpoint); } strawberry++; strawberryInCheckpoint++; break; } } break; } } } } if (mode.Checkpoints == null) { mode.Checkpoints = checkpointsAuto.Where(c => c != null).ToArray(); } }
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(); }
// Mods can't access patch_ classes directly. // We thus expose any new members through extensions. public static MapMeta GetMapMeta(this ModeProperties self) => ((patch_ModeProperties)self).MapMeta;
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)); } } } }
[PatchMapDataLoader] // Manually manipulate the method via MonoModRules private void Load() { // reset those fields to prevent them from stacking up when reloading the map. DetectedStrawberries = 0; DetectedHeartGem = false; DetectedRemixNotes = false; Goldenberries = new List <EntityData>(); DashlessGoldenberries = new List <EntityData>(); DetectedCassette = false; DetectedStrawberriesIncludingUntracked = 0; try { orig_Load(); foreach (LevelData level in Levels) { foreach (EntityData entity in level.Entities) { if (entity.Name == "memorialTextController") // aka "dashless golden" { DashlessGoldenberries.Add(entity); } } } AreaData area = AreaData.Get(Area); AreaData parentArea = AreaDataExt.Get(area.GetMeta()?.Parent); ModeProperties parentMode = parentArea?.Mode?.ElementAtOrDefault((int)Area.Mode); if (parentMode != null) { MapData parentMapData = parentMode.MapData; if (parentMapData == null) { Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed auto-assigning data from {Area} to its unloaded parent"); return; } parentMapData.Strawberries.AddRange(Strawberries); // Recount everything berry-related for the parent map data, just like in orig_Load. parentMode.TotalStrawberries = 0; parentMode.StartStrawberries = 0; parentMode.StrawberriesByCheckpoint = new EntityData[10, 25]; for (int i = 0; parentMode.Checkpoints != null && i < parentMode.Checkpoints.Length; i++) { if (parentMode.Checkpoints[i] != null) { parentMode.Checkpoints[i].Strawberries = 0; } } foreach (EntityData entity in parentMapData.Strawberries) { if (!entity.Bool("moon")) { int checkpointID = entity.Int("checkpointIDParented", entity.Int("checkpointID")); int order = entity.Int("order"); if (_GrowAndGet(ref parentMode.StrawberriesByCheckpoint, checkpointID, order) == null) { parentMode.StrawberriesByCheckpoint[checkpointID, order] = entity; } if (checkpointID == 0) { parentMode.StartStrawberries++; } else if (parentMode.Checkpoints != null) { parentMode.Checkpoints[checkpointID - 1].Strawberries++; } parentMode.TotalStrawberries++; } } } } catch (Exception e) { Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed loading MapData {Area}"); e.LogDetailed(); } }
public static void SetMapMeta(this ModeProperties self, MapMeta value) => ((patch_ModeProperties)self).MapMeta = value;