public static void Reload() { // ChapterSelect only updates the ID. string lastAreaSID = AreaData.Get(SaveData.Instance.LastArea.ID)?.ToKey().GetSID() ?? AreaKey.Default.GetSID(); // Note: SaveData.Instance.LastArea is reset by AreaData.Interlude_Safe -> SaveData.LevelSetStats realizing that AreaOffset == -1 // Store the "resolved" last selected area in a local variable, then re-set it after reloading. // Reload all maps. Everest.Content.Recrawl(); AreaData.Unload(); AreaData.Load(); AreaData.ReloadMountainViews(); // Fake a save data reload to resync the save data to the new area list. AreaData lastArea = AreaDataExt.Get(lastAreaSID); SaveData.Instance.LastArea = lastArea?.ToKey() ?? AreaKey.Default; SaveData.Instance.BeforeSave(); SaveData.Instance.AfterInitialize(); Overworld overworld = (Engine.Scene.Entities.FindFirst <Oui>())?.Overworld; if (overworld == null) { return; } if (overworld.Mountain.Area >= AreaData.Areas.Count) { overworld.Mountain.EaseCamera(0, AreaData.Areas[0].MountainIdle, null, true); } overworld.ReloadMenus((Overworld.StartMode)(-1)); }
public override IEnumerator Enter(Oui from) { if (Direction == 0) { Overworld.Goto <OuiChapterSelect>(); goto Done; } Direction = Math.Sign(Direction); yield return(0.25f); int startID = SaveData.Instance.LastArea.ID; string startLevelSet = SaveData.Instance.GetLevelSet(); int count = AreaData.Areas.Count; for (int i = (count + startID + Direction) % count; i != startID; i = (count + i + Direction) % count) { AreaData area = AreaData.Areas[i]; if (area.GetLevelSet() != startLevelSet) { SaveData.Instance.LastArea = area.ToKey(); goto Done; } } Done: Audio.Play("event:/ui/world_map/chapter/pane_expand"); Overworld.Goto <OuiChapterSelect>(); yield break; }
public override IEnumerator Enter(Oui from) { // Fix "out of bounds" level selection. int areaOffs = SaveData.Instance.GetLevelSetStats().AreaOffset; int areaMax = Math.Max(areaOffs, SaveData.Instance.UnlockedAreas); area = Calc.Clamp(area, areaOffs, areaMax); Visible = true; EaseCamera(); display = true; journalEnabled = Celeste.PlayMode == Celeste.PlayModes.Debug || (SaveData.Instance?.CheatMode ?? false); for (int i = 0; i <= SaveData.Instance.UnlockedAreas && !journalEnabled; i++) { if (SaveData.Instance.Areas[i].Modes[0].TimePlayed > 0L && !AreaData.Get(i).Interlude) { journalEnabled = true; } } OuiChapterSelectIcon unselected = null; if (from is OuiChapterPanel) { (unselected = icons[area]).Unselect(); } currentLevelSet = SaveData.Instance?.GetLevelSet() ?? "Celeste"; foreach (OuiChapterSelectIcon icon in icons) { AreaData area = AreaData.Areas[icon.Area]; if (area.GetLevelSet() != currentLevelSet) { continue; } int index = area.ToKey().ID; if (index <= Math.Max(1, SaveData.Instance.UnlockedAreas) && icon != unselected) { icon.Position = icon.HiddenPosition; icon.Show(); icon.AssistModeUnlockable = false; } else if (SaveData.Instance.AssistMode && index == SaveData.Instance.UnlockedAreas + 1) { icon.Position = icon.HiddenPosition; icon.Show(); icon.AssistModeUnlockable = true; } // yield return 0.01f; // Originally returns 0.01f } if (from is OuiChapterPanel) { yield return(0.25f); } }
public void Inspect(AreaData area, AreaMode mode = AreaMode.Normal) { Focused = false; Audio.Play(SFX.ui_world_icon_select); SaveData.Instance.LastArea = area.ToKey(mode); if (OuiIcons != null && area.ID < OuiIcons.Count) { OuiIcons[area.ID].Select(); } Overworld.Mountain.Model.EaseState(area.MountainState); Overworld.Goto <OuiChapterPanel>(); }
private static void OnChapterPanelReset(On.Celeste.OuiChapterPanel.orig_Reset orig, OuiChapterPanel self) { resetCrystalHeart(self); AreaData forceArea = self.Overworld == null ? null : new DynData <Overworld>(self.Overworld).Get <AreaData>("collabInGameForcedArea"); if (forceArea == null) { orig(self); customizeCrystalHeart(self); return; } SaveData save = SaveData.Instance; Session session = save.CurrentSession; lastArea = save.LastArea; save.LastArea = forceArea.ToKey(); save.CurrentSession = null; DynData <OuiChapterSelect> ouiChapterSelect = new DynData <OuiChapterSelect>(self.Overworld.GetUI <OuiChapterSelect>()); OuiChapterSelectIcon icon = ouiChapterSelect.Get <List <OuiChapterSelectIcon> >("icons")[save.LastArea.ID]; icon.SnapToSelected(); icon.Add(new Coroutine(UpdateIconRoutine(self, icon))); orig(self); customizeCrystalHeart(self); DynData <OuiChapterPanel> data = new DynData <OuiChapterPanel>(self); data["hasCollabCredits"] = true; if (!isPanelShowingLobby()) { data["chapter"] = (new DynData <Overworld>(self.Overworld).Get <AreaData>("collabInGameForcedArea").Name + "_author").DialogCleanOrNull() ?? ""; } /* * (data.modes as IList).Add( * DynamicData.New(t_OuiChapterPanelOption)(new { * Label = "", * BgColor = Calc.HexToColor("223022"), * Icon = GFX.Gui["areas/null"], * Large = false * }) * ); */ // LastArea is also checked in Render. save.CurrentSession = session; }
public static void Reload(bool recrawl) { SaveData saveData = SaveData.Instance; // ChapterSelect only updates the ID. string lastAreaSID = saveData == null ? null : (AreaData.Get(saveData.LastArea.ID)?.ToKey().GetSID() ?? AreaKey.Default.GetSID()); // Note: SaveData.Instance.LastArea is reset by AreaData.Interlude_Safe -> SaveData.LevelSetStats realizing that AreaOffset == -1 // Store the "resolved" last selected area in a local variable, then re-set it after reloading. // Reload all maps. if (recrawl) { Everest.Content.Recrawl(); } lock (AreaReloadLock) { // prevent anything from calling AreaData.Get during this. AreaData.Unload(); AreaData.Load(); AreaData.ReloadMountainViews(); // Fake a save data reload to resync the save data to the new area list. if (saveData != null) { AreaData lastArea = AreaDataExt.Get(lastAreaSID); saveData.LastArea = lastArea?.ToKey() ?? AreaKey.Default; saveData.BeforeSave(); saveData.AfterInitialize(); } } if (Engine.Scene is Overworld overworld) { if (overworld.Mountain.Area >= AreaData.Areas.Count) { overworld.Mountain.EaseCamera(0, AreaData.Areas[0].MountainIdle, null, true); } OuiChapterSelect chapterSelect = overworld.GetUI <OuiChapterSelect>(); overworld.UIs.Remove(chapterSelect); overworld.Remove(chapterSelect); chapterSelect = new OuiChapterSelect(); chapterSelect.Visible = false; overworld.Add(chapterSelect); overworld.UIs.Add(chapterSelect); chapterSelect.IsStart(overworld, (Overworld.StartMode)(-1)); } }
private IEnumerator StartRoutine(AreaData area, AreaMode mode = AreaMode.Normal, string checkpoint = null) { Overworld.Maddy.Hide(false); area.Wipe(Overworld, false, null); Audio.SetMusic(null, true, true); Audio.SetAmbience(null, true); if ((area.ID == 0 || area.ID == 9) && checkpoint == null && mode == AreaMode.Normal) { Overworld.RendererList.UpdateLists(); Overworld.RendererList.MoveToFront(Overworld.Snow); } yield return(0.5f); LevelEnter.Go(new Session(area.ToKey(mode), checkpoint), false); }
private void ProcessMeta(BinaryPacker.Element meta) { AreaData area = AreaData.Get(Area); AreaMode mode = Area.Mode; if (mode == AreaMode.Normal) { new MapMeta(meta).ApplyTo(area); Area = area.ToKey(); } meta = meta.Children?.FirstOrDefault(el => el.Name == "mode"); if (meta == null) { return; } new MapMetaModeProperties(meta).ApplyTo(area, mode); }
private void ProcessMeta(BinaryPacker.Element meta) { AreaData area = AreaData.Get(Area); AreaMode mode = Area.Mode; if (mode == AreaMode.Normal) { new MapMeta(meta).ApplyTo(area); Area = area.ToKey(); // Backup A-Side's Metadata. Only back up useful data. area.SetASideAreaDataBackup(new AreaData { IntroType = area.IntroType, ColorGrade = area.ColorGrade, DarknessAlpha = area.DarknessAlpha, BloomBase = area.BloomBase, BloomStrength = area.BloomStrength, CoreMode = area.CoreMode, Dreaming = area.Dreaming }); } BinaryPacker.Element modeMeta = meta.Children?.FirstOrDefault(el => el.Name == "mode"); if (modeMeta == null) { return; } new MapMetaModeProperties(modeMeta).ApplyTo(area, mode); // Metadata for B-Side and C-Side are parsed and stored. if (mode != AreaMode.Normal) { MapMeta mapMeta = new MapMeta(meta) { Modes = area.GetMeta().Modes }; area.Mode[(int)mode].SetMapMeta(mapMeta); } }
private static void onLevelExitConstructor(On.Celeste.LevelExit.orig_ctor orig, LevelExit self, LevelExit.Mode mode, Session session, HiresSnow snow) { orig(self, mode, session, snow); if (mode == LevelExit.Mode.Restart || mode == LevelExit.Mode.GoldenBerryRestart) { // be sure to keep the lobby info in the session, even if we are resetting it. temporaryLobbySIDHolder = CollabModule.Instance.Session.LobbySID; temporaryRoomHolder = CollabModule.Instance.Session.LobbyRoom; temporarySpawnPointHolder = new Vector2(CollabModule.Instance.Session.LobbySpawnPointX, CollabModule.Instance.Session.LobbySpawnPointY); temporarySaveAllowedHolder = CollabModule.Instance.Session.SaveAndReturnToLobbyAllowed; } if ((mode == LevelExit.Mode.GiveUp || mode == LevelExit.Mode.Completed) && CollabModule.Instance.Session.LobbySID != null) { // be sure that Return to Map and such from a collab entry returns to the lobby, not to the collab entry... // if the lobby exists, of course. AreaData lobby = AreaData.Get(CollabModule.Instance.Session.LobbySID); if (lobby != null) { SaveData.Instance.LastArea_Safe = lobby.ToKey(); } } }
public override IEnumerator Enter(Oui from) { if (Direction == 0) { Overworld.Goto <OuiChapterSelect>(); goto Done; } Direction = Math.Sign(Direction); yield return(0.25f); int startID = SaveData.Instance.LastArea.ID; string startLevelSet = SaveData.Instance.GetLevelSet(); int count = AreaData.Areas.Count; for (int i = (count + startID + Direction) % count; i != startID; i = (count + i + Direction) % count) { AreaData area = AreaData.Get(i); if (area == null || area.GetLevelSet() != startLevelSet) { SaveData.Instance.LastArea = area.ToKey(); goto Done; } } Done: if (Direction > 0) { Audio.Play(SFX.ui_world_chapter_pane_expand); } else { Audio.Play(SFX.ui_world_chapter_pane_contract); } Overworld.Goto <OuiChapterSelect>(); }
private static void onOuiFileSelectSlotShow(On.Celeste.OuiFileSelectSlot.orig_Show orig, OuiFileSelectSlot self) { // If we are currently in a collab map, display the lobby level set stats instead. AreaKey?savedLastArea = null; string collab = collabNames.FirstOrDefault(collabName => self.SaveData?.LevelSet != null && self.SaveData.LevelSet.StartsWith($"{collabName}/") && self.SaveData.LevelSet != $"{collabName}/0-Lobbies"); if (collab != null) { AreaData firstMapFromCollab = AreaData.Areas.FirstOrDefault(area => area.GetLevelSet() == $"{collab}/0-Lobbies"); if (firstMapFromCollab != null) { savedLastArea = self.SaveData.LastArea_Safe; self.SaveData.LastArea_Safe = firstMapFromCollab.ToKey(); self.Strawberries.CanWiggle = false; // prevent the strawberry collect sound from playing. } } orig(self); string collab2 = collabNames.FirstOrDefault(collabName => self.SaveData?.LevelSet == $"{collabName}/0-Lobbies"); if (collab2 != null) { // recompute the stats for the collab. int totalStrawberries = 0; int totalGoldenStrawberries = 0; int totalHeartGems = 0; int totalCassettes = 0; int maxStrawberryCount = 0; int maxGoldenStrawberryCount = 0; int maxStrawberryCountIncludingUntracked = 0; int maxCassettes = 0; int maxCrystalHearts = 0; int maxCrystalHeartsExcludingCSides = 0; // aggregate all stats for the collab level sets. foreach (LevelSetStats stats in self.SaveData.LevelSets) { if (stats.Name.StartsWith($"{collab2}/")) { totalStrawberries += stats.TotalStrawberries; totalGoldenStrawberries += stats.TotalGoldenStrawberries; totalHeartGems += countTotalHeartGemsForMapsThatHaveHearts(stats); totalCassettes += stats.TotalCassettes; maxStrawberryCount += stats.MaxStrawberries; maxGoldenStrawberryCount += stats.MaxGoldenStrawberries; maxStrawberryCountIncludingUntracked += stats.MaxStrawberriesIncludingUntracked; maxCassettes += stats.MaxCassettes; maxCrystalHearts += stats.MaxHeartGems; maxCrystalHeartsExcludingCSides += stats.MaxHeartGemsExcludingCSides; } } DynData <OuiFileSelectSlot> slotData = new DynData <OuiFileSelectSlot>(self); slotData["totalGoldenStrawberries"] = totalGoldenStrawberries; slotData["totalHeartGems"] = totalHeartGems; slotData["totalCassettes"] = totalCassettes; slotData["maxStrawberryCount"] = maxStrawberryCount; slotData["maxGoldenStrawberryCount"] = maxGoldenStrawberryCount; slotData["maxStrawberryCountIncludingUntracked"] = maxStrawberryCountIncludingUntracked; slotData["maxCassettes"] = maxCassettes; slotData["maxCrystalHearts"] = maxCrystalHearts; slotData["maxCrystalHeartsExcludingCSides"] = maxCrystalHeartsExcludingCSides; slotData["summitStamp"] = false; slotData["farewellStamp"] = false; self.Strawberries.Amount = totalStrawberries; self.Strawberries.OutOf = maxStrawberryCount; } // Restore the last area if it was replaced at the beginning of this method. if (savedLastArea != null) { self.SaveData.LastArea_Safe = savedLastArea.Value; } }
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(); }
private static void AddExtraModes(OuiChapterPanel self) { // check map meta for extra sides or side overrides //AltSidesHelperMeta meta = new DynData<AreaData>(self.Data).Get<AltSidesHelperMeta>("AltSidesHelperMeta"); AltSidesHelperMeta meta = GetMetaForAreaData(self.Data); //AltSidesMetadata[self.Data]; if (meta?.Sides != null) { Logger.Log("AltSidesHelper", $"Customising panel UI for \"{self.Data.SID}\"."); bool[] unlockedSides = new bool[meta.Sides.Count()]; int siblings = ((IList)modesField.GetValue(self)).Count; int oldModes = siblings; bool bsidesunlocked = !self.Data.Interlude_Safe && self.Data.HasMode(AreaMode.BSide) && (self.DisplayedStats.Cassette || ((SaveData.Instance.DebugMode || SaveData.Instance.CheatMode) && self.DisplayedStats.Cassette == self.RealStats.Cassette)); bool csidesunlocked = !self.Data.Interlude_Safe && self.Data.HasMode(AreaMode.CSide) && SaveData.Instance.UnlockedModes >= 3 && Celeste.Celeste.PlayMode != Celeste.Celeste.PlayModes.Event; // find the new total number of unlocked modes int unlockedModeCount = 0; // if this map has a C-Side, this is whether they have C-sides unlocked. else, if this map has a B-Sides, its whether they have a cassette. else, true. bool prevUnlocked = self.Data.HasMode(AreaMode.CSide) ? csidesunlocked : self.Data.HasMode(AreaMode.BSide) ? bsidesunlocked : true; // if this map has a C-Side, this is whether they've beaten it; else, if this map has a B-Side, its whether they've completed it; else, its whether they've completed the level. bool prevCompleted = self.Data.HasMode(AreaMode.CSide) ? SaveData.Instance.GetAreaStatsFor(self.Data.ToKey()).Modes[(int)AreaMode.CSide].Completed : self.Data.HasMode(AreaMode.BSide) ? SaveData.Instance.GetAreaStatsFor(self.Data.ToKey()).Modes[(int)AreaMode.BSide].Completed : SaveData.Instance.GetAreaStatsFor(self.Data.ToKey()).Modes[(int)AreaMode.Normal].Completed; for (int i1 = 0; i1 < meta.Sides.Length; i1++) { AltSidesHelperMode mode = meta.Sides[i1]; if (!mode.OverrideVanillaSideData) { if ((mode.UnlockMode.Equals("consecutive") && prevCompleted) || (mode.UnlockMode.Equals("with_previous") && prevUnlocked) || (mode.UnlockMode.Equals("triggered") && AltSidesSaveData.UnlockedAltSideIDs.Contains(mode.Map)) || (mode.UnlockMode.Equals("c_sides_unlocked") && csidesunlocked) || mode.UnlockMode.Equals("always") || SaveData.Instance.DebugMode || SaveData.Instance.CheatMode) { unlockedModeCount++; siblings++; prevUnlocked = true; prevCompleted = SaveData.Instance.GetAreaStatsFor(AreaData.Get(mode.Map).ToKey()).Modes[(int)AreaMode.Normal].Completed; unlockedSides[i1] = true; } else { prevUnlocked = prevCompleted = false; unlockedSides[i1] = false; } } else { unlockedSides[i1] = true; } } // adjust the original options to fit, and attach the map path & mode to the original options int origMode = 0; foreach (var vmode in (IList)modesField.GetValue(self)) { DynamicData data = new DynamicData(vmode); if (siblings > 5) { data.Set("Siblings", siblings); data.Set("Large", false); } data.Set("AreaKey", self.Data.ToKey((AreaMode)origMode)); origMode++; } // apply mode settings int newSides = 0; for (int i = 0; i < meta.Sides.Length /*&& newSides < unlockedModes*/; i++) { AltSidesHelperMode mode = meta.Sides[i]; // only add if its unlocked if (unlockedSides[i]) { if (!mode.OverrideVanillaSideData) { object newOptn; ((IList)modesField.GetValue(self)).Add( newOptn = DynamicData.New(t_OuiChapterPanelOption)(new { Label = Dialog.Clean(mode.Label), Icon = GFX.Gui[mode.Icon], ID = "AltSidesHelperMode_" + i.ToString(), Siblings = siblings > 5 ? siblings : 0 }) ); DynamicData data = new DynamicData(newOptn); AreaData map = null; foreach (var area in AreaData.Areas) { if (area.SID.Equals(mode.Map)) { map = area; } } data.Set("AreaKey", map.ToKey()); newSides++; Logger.Log("AltSidesHelper", $"Added new side for \"{self.Data.SID}\"."); } else { // find the a-side and modify it DynamicData data = new DynamicData(((IList)modesField.GetValue(self))[0]); data.Set("Label", Dialog.Clean(mode.Label)); data.Set("Icon", GFX.Gui[mode.Icon]); Logger.Log("AltSidesHelper", $"Modifying A-Side data for \"{self.Data.SID}\"."); } } } } int count = ((IList)modesField.GetValue(self)).Count; for (int i = 0; i < count; i++) { //DynamicData data = new DynamicData(((IList)modesField.GetValue(self))[i]); //data.Invoke("SlideTowards", count, i); } }
public static List <OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal journal, string levelSet, bool showOnlyDiscovered) { bool displaySpeedBerryColumn = shouldDisplaySpeedBerryColumn(levelSet); List <OuiJournalCollabProgressInLobby> pages = new List <OuiJournalCollabProgressInLobby>(); int rowCount = 0; int totalStrawberries = 0; int totalDeaths = 0; int sumOfBestDeaths = 0; int sumOfBestDashes = 0; long totalTime = 0; long sumOfBestTimes = 0; bool allMapsDone = true; bool allLevelsDone = true; bool allSpeedBerriesDone = true; string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + levelSet) ? "CollabUtils2Hearts/" + levelSet : "heartgem0"; int mapsPerPage = 12; int mapAmount = SaveData.Instance.Areas_Safe.Where(item => !AreaData.Get(item.ID_Safe).Interlude_Safe && (!showOnlyDiscovered || item.TotalTimePlayed > 0)).Count(); // we want to display the map icons if they're not actually all the same. ^^' bool displayIcons = AreaData.Areas .Where(area => !area.Interlude_Safe) .Select(area => area.Icon) .Distinct() .Count() > 1; OuiJournalCollabProgressInLobby currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet, displayIcons); pages.Add(currentPage); if (mapAmount >= mapsPerPage) { // we want the last page to contain at least 2 maps. while (mapAmount % mapsPerPage < 2) { mapsPerPage--; } } List <AreaStats> sortedMaps = new List <AreaStats>(SaveData.Instance.Areas_Safe) .Where(map => !AreaData.Get(map).Interlude_Safe) .ToList(); // sort maps by icon name if all of their icons start with [number]-... // because then we know the ordering is intentional (and not accidental like easy > hard > medium) Regex startsWithNumber = new Regex(".*/[0-9]+-.*"); if (sortedMaps.Select(map => AreaData.Get(map).Icon ?? "").All(icon => startsWithNumber.IsMatch(icon))) { sortedMaps.Sort((a, b) => { AreaData adata = AreaData.Get(a); AreaData bdata = AreaData.Get(b); bool aHeartSide = LobbyHelper.IsHeartSide(a.GetSID()); bool bHeartSide = LobbyHelper.IsHeartSide(b.GetSID()); // heart sides should appear last. if (aHeartSide && !bHeartSide) { return(1); } if (!aHeartSide && bHeartSide) { return(-1); } // sort by icon name, then by map bin name. return(adata.Icon == bdata.Icon ? adata.Name.CompareTo(bdata.Name) : adata.Icon.CompareTo(bdata.Icon)); }); } foreach (AreaStats item in sortedMaps) { AreaData areaData = AreaData.Get(item.ID_Safe); if (LobbyHelper.IsHeartSide(areaData.GetSID())) { if (allMapsDone || item.TotalTimePlayed > 0) { // add a separator, like the one between regular maps and Farewell currentPage.table.AddRow(); } else { // all maps weren't complete yet, and the heart side was never accessed: hide the heart side for now. continue; } } if (showOnlyDiscovered && item.TotalTimePlayed <= 0) { // skip the map, because it was not discovered yet. // since it wasn't discovered, we can already say all maps weren't done though. allMapsDone = false; allLevelsDone = false; allSpeedBerriesDone = false; continue; } string strawberryText = null; if (areaData.Mode[0].TotalStrawberries > 0 || item.TotalStrawberries > 0) { strawberryText = item.TotalStrawberries.ToString(); if (item.Modes[0].Completed) { strawberryText = strawberryText + "/" + areaData.Mode[0].TotalStrawberries; } } else { strawberryText = "-"; } Row row = currentPage.table.AddRow() .Add(new TextCell(Dialog.Clean(areaData.Name), new Vector2(1f, 0.5f), 0.6f, currentPage.TextColor)); if (displayIcons) { row.Add(null).Add(new IconCellFromGui(GFX.Gui.Has(areaData.Icon) ? areaData.Icon : "areas/null", 60f, 50f)); } row.Add(null) .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot")) .Add(new TextCell(strawberryText, currentPage.TextJustify, 0.5f, currentPage.TextColor)); if (item.TotalTimePlayed > 0) { row.Add(new TextCell(Dialog.Deaths(item.Modes[0].Deaths), currentPage.TextJustify, 0.5f, currentPage.TextColor)); } else { row.Add(new IconCell("dot")); } AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey()); if (CollabMapDataProcessor.SilverBerries.TryGetValue(areaData.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) && levelSetBerries.TryGetValue(areaData.GetSID(), out EntityID berryID) && stats.Modes[0].Strawberries.Contains(berryID)) { // silver berry was obtained! row.Add(new IconCell("CollabUtils2/silver_strawberry")); } else if (stats.Modes[0].Strawberries.Any(berry => areaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level))) { // golden berry was obtained! row.Add(new IconCell("CollabUtils2/golden_strawberry")); } else if (item.Modes[0].SingleRunCompleted) { row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), currentPage.TextJustify, 0.5f, currentPage.TextColor)); sumOfBestDeaths += item.Modes[0].BestDeaths; } else { // the player didn't ever do a single run. row.Add(new IconCell("dot")); allLevelsDone = false; } if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled()) { if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || item.Modes[0].SingleRunCompleted) { row.Add(new TextCell(Dialog.Deaths(OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item)), currentPage.TextJustify, 0.5f, currentPage.TextColor)); sumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item); } else { row.Add(new IconCell("dot")); } } if (item.TotalTimePlayed > 0) { row.Add(new TextCell(Dialog.Time(item.TotalTimePlayed), currentPage.TextJustify, 0.5f, currentPage.TextColor)); } else { row.Add(new IconCell("dot")); } if (displaySpeedBerryColumn) { if (CollabMapDataProcessor.SpeedBerries.TryGetValue(item.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo speedBerryInfo) && CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(item.GetSID(), out long speedBerryPB)) { row.Add(new TextCell(Dialog.Time(speedBerryPB), currentPage.TextJustify, 0.5f, getRankColor(speedBerryInfo, speedBerryPB))); row.Add(new IconCell(getRankIcon(speedBerryInfo, speedBerryPB))); sumOfBestTimes += speedBerryPB; } else { row.Add(new IconCell("dot")).Add(null); allSpeedBerriesDone = false; } } else { if (item.Modes[0].BestTime > 0f) { row.Add(new TextCell(Dialog.Time(item.Modes[0].BestTime), currentPage.TextJustify, 0.5f, currentPage.TextColor)).Add(null); sumOfBestTimes += item.Modes[0].BestTime; } else { row.Add(new IconCell("dot")).Add(null); allSpeedBerriesDone = false; } } totalStrawberries += item.TotalStrawberries; totalDeaths += item.Modes[0].Deaths; totalTime += item.TotalTimePlayed; if (!item.Modes[0].HeartGem) { allMapsDone = false; } rowCount++; if (rowCount >= mapsPerPage) { // split the next zones into another page. rowCount = 0; currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet, displayIcons); pages.Add(currentPage); } } if (currentPage.table.Rows > 1) { currentPage.table.AddRow(); Row totalsRow = currentPage.table.AddRow() .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, currentPage.TextColor)).Add(null); if (displayIcons) { totalsRow.Add(null).Add(null); } totalsRow.Add(null) .Add(new TextCell(totalStrawberries.ToString(), currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(Dialog.Deaths(totalDeaths), currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(allLevelsDone ? Dialog.Deaths(sumOfBestDeaths) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)); if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled()) { totalsRow.Add(new TextCell(OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() || allLevelsDone ? Dialog.Deaths(sumOfBestDashes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)); } totalsRow .Add(new TextCell(Dialog.Time(totalTime), currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)).Add(null); for (int l = 1; l < SaveData.Instance.UnlockedModes; l++) { totalsRow.Add(null); } totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), currentPage.TextJustify, 0.6f, currentPage.TextColor)); currentPage.table.AddRow(); } return(pages); }
private static void onOuiFileSelectSlotShow(On.Celeste.OuiFileSelectSlot.orig_Show orig, OuiFileSelectSlot self) { // If we are currently in a collab map, display the lobby level set stats instead. AreaKey?savedLastArea = null; string collab = collabNames.FirstOrDefault(collabName => self.SaveData?.LevelSet != null && self.SaveData.LevelSet.StartsWith($"{collabName}/") && self.SaveData.LevelSet != $"{collabName}/0-Lobbies"); if (collab != null) { AreaData firstMapFromCollab = AreaData.Areas.FirstOrDefault(area => area.GetLevelSet() == $"{collab}/0-Lobbies"); if (firstMapFromCollab != null) { savedLastArea = self.SaveData.LastArea_Safe; self.SaveData.LastArea_Safe = firstMapFromCollab.ToKey(); self.Strawberries.CanWiggle = false; // prevent the strawberry collect sound from playing. } } orig(self); string collab2 = collabNames.FirstOrDefault(collabName => self.SaveData?.LevelSet == $"{collabName}/0-Lobbies"); if (collab2 != null) { // recompute the stats for the collab. int totalStrawberries = 0; int totalGoldenStrawberries = 0; int totalHeartGems = 0; int totalCassettes = 0; int maxStrawberryCount = 0; int maxGoldenStrawberryCount = 0; int maxStrawberryCountIncludingUntracked = 0; int maxCassettes = 0; int maxCrystalHearts = 0; int maxCrystalHeartsExcludingCSides = 0; // aggregate all stats for the collab level sets. foreach (LevelSetStats stats in self.SaveData.LevelSets) { if (stats.Name.StartsWith($"{collab2}/")) { totalStrawberries += stats.TotalStrawberries; totalGoldenStrawberries += stats.TotalGoldenStrawberries; totalHeartGems += countTotalHeartGemsForMapsThatHaveHearts(stats); totalCassettes += stats.TotalCassettes; maxStrawberryCount += stats.MaxStrawberries; maxGoldenStrawberryCount += stats.MaxGoldenStrawberries; maxStrawberryCountIncludingUntracked += stats.MaxStrawberriesIncludingUntracked; maxCassettes += stats.MaxCassettes; maxCrystalHearts += stats.MaxHeartGems; maxCrystalHeartsExcludingCSides += stats.MaxHeartGemsExcludingCSides; } } DynData <OuiFileSelectSlot> slotData = new DynData <OuiFileSelectSlot>(self); slotData["totalGoldenStrawberries"] = totalGoldenStrawberries; slotData["totalHeartGems"] = totalHeartGems; slotData["totalCassettes"] = totalCassettes; slotData["maxStrawberryCount"] = maxStrawberryCount; slotData["maxGoldenStrawberryCount"] = maxGoldenStrawberryCount; slotData["maxStrawberryCountIncludingUntracked"] = maxStrawberryCountIncludingUntracked; slotData["maxCassettes"] = maxCassettes; slotData["maxCrystalHearts"] = maxCrystalHearts; slotData["maxCrystalHeartsExcludingCSides"] = maxCrystalHeartsExcludingCSides; slotData["summitStamp"] = false; slotData["farewellStamp"] = false; self.Strawberries.Amount = totalStrawberries; self.Strawberries.OutOf = maxStrawberryCount; } // figure out if some hearts are customized, and store it in DynData so that a IL hook can access it later. SaveData oldInstance = SaveData.Instance; SaveData.Instance = self.SaveData; List <string> customJournalHearts = new List <string>(); if (self.SaveData != null) { foreach (AreaStats item in self.SaveData.Areas_Safe) { if (item.ID_Safe > self.SaveData.UnlockedAreas_Safe) { break; } if (!AreaData.Areas[item.ID_Safe].Interlude_Safe && AreaData.Areas[item.ID_Safe].CanFullClear) { string lobbyLevelSetName = GetLobbyLevelSet(item.GetSID()); if (lobbyLevelSetName != null && MTN.Journal.Has("CollabUtils2Hearts/" + lobbyLevelSetName)) { customJournalHearts.Add("CollabUtils2Hearts/" + lobbyLevelSetName); } else { customJournalHearts.Add(null); } } } } new DynData <OuiFileSelectSlot>(self)["collabutils2_customhearts"] = customJournalHearts; SaveData.Instance = oldInstance; // Restore the last area if it was replaced at the beginning of this method. if (savedLastArea != null) { self.SaveData.LastArea_Safe = savedLastArea.Value; } }
public OuiJournalCollabProgressInOverworld(OuiJournal journal) : base(journal) { bool displaySpeedBerryColumn = shouldDisplaySpeedBerryColumn(); PageTexture = "page"; table = new Table() .AddColumn(new TextCell(Dialog.Clean("journal_progress"), new Vector2(0f, 0.5f), 1f, Color.Black * 0.7f, 420f)) .AddColumn(new EmptyCell(20f)) .AddColumn(new EmptyCell(64f)) .AddColumn(new IconCell("strawberry", 150f)) .AddColumn(new IconCell("skullblue", 100f)) .AddColumn(new IconCell("CollabUtils2MinDeaths/SpringCollab2020/1-Beginner", 100f)); if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled()) { table.AddColumn(new IconCell("max480/DashCountMod/dashes", 80f)); } table .AddColumn(new IconCell("time", 220f)) .AddColumn(new IconCell("CollabUtils2/speed_berry_pbs_heading", 220f)) .AddColumn(new EmptyCell(30f)); int totalStrawberries = 0; int totalDeaths = 0; int sumOfBestDeaths = 0; int sumOfBestDashes = 0; long totalTime = 0; long sumOfBestTimes = 0; bool allLevelsDone = true; bool allSpeedBerriesDone = true; bool allMapsCompletedInSingleRun = true; foreach (AreaStats item in SaveData.Instance.Areas_Safe) { AreaData areaData = AreaData.Get(item.ID_Safe); if (areaData.GetLevelSet() == SaveData.Instance.LevelSet) { string lobbyMapLevelSetName = LobbyHelper.GetLobbyLevelSet(areaData.GetSID()); LevelSetStats lobbyMapLevelSet = null; if (lobbyMapLevelSetName != null) { lobbyMapLevelSet = SaveData.Instance.GetLevelSetStatsFor(lobbyMapLevelSetName); } int lobbyStrawberries = item.TotalStrawberries; int lobbyTotalStrawberries = areaData.Mode[0].TotalStrawberries; int lobbyDeaths = item.Modes[0].Deaths; int lobbySumOfBestDeaths = 0; int lobbySumOfBestDashes = OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() ? OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item) : 0; long lobbyTotalTime = item.TotalTimePlayed; long lobbySumOfBestTimes = 0; bool lobbyLevelsDone = true; int lobbySpeedBerryLevel = 1; bool lobbySilverBerriesObtained = true; bool lobbyAllMapsCompletedInSingleRun = true; foreach (AreaStats lobbyMap in lobbyMapLevelSet?.Areas ?? new List <AreaStats>()) { AreaData lobbyAreaData = AreaData.Get(lobbyMap.ID_Safe); lobbyStrawberries += lobbyMap.TotalStrawberries; lobbyTotalStrawberries += lobbyAreaData.Mode[0].TotalStrawberries; lobbyDeaths += lobbyMap.Modes[0].Deaths; lobbyTotalTime += lobbyMap.TotalTimePlayed; lobbyAllMapsCompletedInSingleRun &= lobbyMap.Modes[0].SingleRunCompleted; if (displaySpeedBerryColumn) { if (CollabMapDataProcessor.SpeedBerries.TryGetValue(lobbyMap.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo mapSpeedBerryInfo) && CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(lobbyMap.GetSID(), out long mapSpeedBerryPB)) { lobbySpeedBerryLevel = Math.Max(getRankLevel(mapSpeedBerryInfo, mapSpeedBerryPB), lobbySpeedBerryLevel); lobbySumOfBestTimes += mapSpeedBerryPB; } else { lobbySpeedBerryLevel = 4; } } else { if (lobbyMap.Modes[0].BestTime > 0f) { lobbySumOfBestTimes += lobbyMap.Modes[0].BestTime; } else { lobbySpeedBerryLevel = 4; } } bool goldenBerryNotObtained = !lobbyMap.Modes[0].Strawberries.Any(berry => lobbyAreaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level)); bool silverBerryNotObtained = !CollabMapDataProcessor.SilverBerries.TryGetValue(lobbyMap.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) || !levelSetBerries.TryGetValue(lobbyMap.GetSID(), out EntityID berryID) || !lobbyMap.Modes[0].Strawberries.Contains(berryID); if (goldenBerryNotObtained && silverBerryNotObtained) { lobbySilverBerriesObtained = false; lobbySumOfBestDeaths += lobbyMap.Modes[0].BestDeaths; } lobbySumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(lobbyMap); if (!lobbyMap.Modes[0].HeartGem) { lobbyLevelsDone = false; } } string strawberryText = null; if (lobbyStrawberries > 0 || lobbyTotalStrawberries > 0) { strawberryText = lobbyStrawberries.ToString(); if (lobbyLevelsDone) { strawberryText = strawberryText + "/" + lobbyTotalStrawberries; } } else { strawberryText = "-"; } string heartTexturePath = lobbyMapLevelSetName ?? areaData.GetSID(); string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + heartTexturePath) ? "CollabUtils2Hearts/" + heartTexturePath : "heartgem0"; string areaName = Dialog.Clean(areaData.Name); if (Dialog.Has(areaData.Name + "_journal")) { areaName = Dialog.Clean(areaData.Name + "_journal"); } Row row = table.AddRow() .Add(new TextCell(areaName, new Vector2(1f, 0.5f), 0.6f, TextColor)) .Add(null) .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot")) .Add(new TextCell(strawberryText, TextJustify, 0.5f, TextColor)); if (lobbyTotalTime > 0) { row.Add(new TextCell(Dialog.Deaths(lobbyDeaths), TextJustify, 0.5f, TextColor)); } else { row.Add(new IconCell("dot")); } if (lobbyLevelsDone) { AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey()); if (lobbyMapLevelSet == null) { row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), TextJustify, 0.5f, TextColor)); sumOfBestDeaths += item.Modes[0].BestDeaths; } else if (lobbySilverBerriesObtained) { row.Add(new IconCell("CollabUtils2/golden_strawberry")); } else if (lobbyAllMapsCompletedInSingleRun) { row.Add(new TextCell(Dialog.Deaths(lobbySumOfBestDeaths), TextJustify, 0.5f, TextColor)); } else { row.Add(new IconCell("dot")); } } else { row.Add(new IconCell("dot")); allLevelsDone = false; } if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled()) { if (lobbyMapLevelSet == null) { if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || item.Modes[0].SingleRunCompleted) { row.Add(new TextCell(Dialog.Deaths(OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item)), TextJustify, 0.5f, TextColor)); sumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item); } else { row.Add(new IconCell("dot")); } } else if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || lobbyAllMapsCompletedInSingleRun) { row.Add(new TextCell(Dialog.Deaths(lobbySumOfBestDashes), TextJustify, 0.5f, TextColor)); } else { row.Add(new IconCell("dot")); } } if (lobbyTotalTime > 0) { row.Add(new TextCell(Dialog.Time(lobbyTotalTime), TextJustify, 0.5f, TextColor)); } else { row.Add(new IconCell("dot")); } if (lobbyMapLevelSet == null) { row.Add(new TextCell("-", TextJustify, 0.5f, TextColor)).Add(null); } else if (lobbySpeedBerryLevel < 4) { if (displaySpeedBerryColumn) { row.Add(new TextCell(Dialog.Time(lobbySumOfBestTimes), TextJustify, 0.5f, getRankColor(lobbySpeedBerryLevel))); row.Add(new IconCell(getRankIcon(lobbySpeedBerryLevel))); sumOfBestTimes += lobbySumOfBestTimes; } else { row.Add(new TextCell(Dialog.Time(lobbySumOfBestTimes), TextJustify, 0.5f, TextColor)).Add(null); sumOfBestTimes += lobbySumOfBestTimes; } } else { row.Add(new IconCell("dot")).Add(null); allSpeedBerriesDone = false; } totalStrawberries += lobbyStrawberries; totalDeaths += lobbyDeaths; sumOfBestDeaths += lobbySumOfBestDeaths; sumOfBestDashes += lobbySumOfBestDashes; totalTime += lobbyTotalTime; allMapsCompletedInSingleRun &= lobbyAllMapsCompletedInSingleRun; if (!lobbyLevelsDone) { allLevelsDone = false; } } } table.AddRow(); Row totalsRow = table.AddRow() .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, TextColor)).Add(null) .Add(null) .Add(new TextCell(totalStrawberries.ToString(), TextJustify, 0.6f, TextColor)) .Add(new TextCell(Dialog.Deaths(totalDeaths), TextJustify, 0.6f, TextColor)) .Add(new TextCell(allLevelsDone && allMapsCompletedInSingleRun ? Dialog.Deaths(sumOfBestDeaths) : "-", TextJustify, 0.6f, TextColor)); if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled()) { totalsRow.Add(new TextCell(OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() || (allLevelsDone && allMapsCompletedInSingleRun) ? Dialog.Deaths(sumOfBestDashes) : "-", TextJustify, 0.6f, TextColor)); } totalsRow .Add(new TextCell(Dialog.Time(totalTime), TextJustify, 0.6f, TextColor)) .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", TextJustify, 0.6f, TextColor)).Add(null); for (int l = 1; l < SaveData.Instance.UnlockedModes; l++) { totalsRow.Add(null); } totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), TextJustify, 0.6f, TextColor)); table.AddRow(); }
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 List <OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal journal, string levelSet) { List <OuiJournalCollabProgressInLobby> pages = new List <OuiJournalCollabProgressInLobby>(); int rowCount = 0; OuiJournalCollabProgressInLobby currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet); pages.Add(currentPage); int totalStrawberries = 0; int totalDeaths = 0; int sumOfBestDeaths = 0; long totalTime = 0; long sumOfBestTimes = 0; bool allMapsDone = true; bool allLevelsDone = true; bool allSpeedBerriesDone = true; string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + levelSet) ? "CollabUtils2Hearts/" + levelSet : "heartgem0"; foreach (AreaStats item in SaveData.Instance.Areas_Safe) { AreaData areaData = AreaData.Get(item.ID_Safe); if (!areaData.Interlude_Safe) { if (LobbyHelper.IsHeartSide(areaData.GetSID())) { if (allMapsDone || item.TotalTimePlayed > 0) { // add a separator, like the one between regular maps and Farewell currentPage.table.AddRow(); } else { // all maps weren't complete yet, and the heart side was never accessed: hide the heart side for now. continue; } } string strawberryText = null; if (areaData.Mode[0].TotalStrawberries > 0 || item.TotalStrawberries > 0) { strawberryText = item.TotalStrawberries.ToString(); if (item.Modes[0].Completed) { strawberryText = strawberryText + "/" + areaData.Mode[0].TotalStrawberries; } } else { strawberryText = "-"; } Row row = currentPage.table.AddRow() .Add(new TextCell(Dialog.Clean(areaData.Name), new Vector2(1f, 0.5f), 0.6f, currentPage.TextColor)) .Add(null) .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot")) .Add(new TextCell(strawberryText, currentPage.TextJustify, 0.5f, currentPage.TextColor)); if (item.TotalTimePlayed > 0) { row.Add(new TextCell(Dialog.Deaths(item.Modes[0].Deaths), currentPage.TextJustify, 0.5f, currentPage.TextColor)); } else { row.Add(new IconCell("dot")); } AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey()); if (CollabMapDataProcessor.SilverBerries.TryGetValue(areaData.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) && levelSetBerries.TryGetValue(areaData.GetSID(), out EntityID berryID) && stats.Modes[0].Strawberries.Contains(berryID)) { // silver berry was obtained! row.Add(new IconCell("CollabUtils2/silver_strawberry")); } else if (stats.Modes[0].Strawberries.Any(berry => areaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level))) { // golden berry was obtained! row.Add(new IconCell("CollabUtils2/golden_strawberry")); } else if (item.Modes[0].SingleRunCompleted) { row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), currentPage.TextJustify, 0.5f, currentPage.TextColor)); sumOfBestDeaths += item.Modes[0].BestDeaths; } else { // the player didn't ever do a single run. row.Add(new IconCell("dot")); allLevelsDone = false; } if (item.TotalTimePlayed > 0) { row.Add(new TextCell(Dialog.Time(item.TotalTimePlayed), currentPage.TextJustify, 0.5f, currentPage.TextColor)); } else { row.Add(new IconCell("dot")); } if (CollabModule.Instance.Settings.BestTimeToDisplayInJournal == CollabSettings.BestTimeInJournal.SpeedBerry) { if (CollabMapDataProcessor.SpeedBerries.TryGetValue(item.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo speedBerryInfo) && CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(item.GetSID(), out long speedBerryPB)) { row.Add(new TextCell(Dialog.Time(speedBerryPB), currentPage.TextJustify, 0.5f, getRankColor(speedBerryInfo, speedBerryPB))); row.Add(new IconCell(getRankIcon(speedBerryInfo, speedBerryPB))); sumOfBestTimes += speedBerryPB; } else { row.Add(new IconCell("dot")).Add(null); allSpeedBerriesDone = false; } } else { if (item.Modes[0].BestTime > 0f) { row.Add(new TextCell(Dialog.Time(item.Modes[0].BestTime), currentPage.TextJustify, 0.5f, currentPage.TextColor)).Add(null); sumOfBestTimes += item.Modes[0].BestTime; } else { row.Add(new IconCell("dot")).Add(null); allSpeedBerriesDone = false; } } totalStrawberries += item.TotalStrawberries; totalDeaths += item.Modes[0].Deaths; totalTime += item.TotalTimePlayed; if (!item.Modes[0].HeartGem) { allMapsDone = false; } rowCount++; if (rowCount > 11) { // split the next zones into another page. rowCount = 0; currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet); pages.Add(currentPage); } } } if (currentPage.table.Rows > 1) { currentPage.table.AddRow(); Row totalsRow = currentPage.table.AddRow() .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, currentPage.TextColor)).Add(null) .Add(null) .Add(new TextCell(totalStrawberries.ToString(), currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(Dialog.Deaths(totalDeaths), currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(allLevelsDone ? Dialog.Deaths(sumOfBestDeaths) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(Dialog.Time(totalTime), currentPage.TextJustify, 0.6f, currentPage.TextColor)) .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)).Add(null); for (int l = 1; l < SaveData.Instance.UnlockedModes; l++) { totalsRow.Add(null); } totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), currentPage.TextJustify, 0.6f, currentPage.TextColor)); currentPage.table.AddRow(); } return(pages); }
public override IEnumerator Enter(Oui from) { // Fix "out of bounds" level selection. GetMinMaxArea(out int areaOffs, out int areaMax); int areaUnclamp = area; area = Calc.Clamp(area, areaOffs, areaMax); Visible = true; EaseCamera(); display = true; currentLevelSet = SaveData.Instance?.GetLevelSet() ?? "Celeste"; journalEnabled = string.IsNullOrEmpty(currentLevelSet) || Celeste.PlayMode == Celeste.PlayModes.Debug || (SaveData.Instance?.CheatMode ?? false); for (int i = 0; i <= SaveData.Instance.UnlockedAreas && !journalEnabled; i++) { if (SaveData.Instance.Areas[i].Modes[0].TimePlayed > 0L && !AreaData.Get(i).Interlude) { journalEnabled = true; } } OuiChapterSelectIcon unselected = null; if (from is OuiChapterPanel) { (unselected = icons[areaUnclamp]).Unselect(); if (areaUnclamp != area) { unselected.Hide(); } } bool isVanilla = currentLevelSet == "Celeste"; foreach (OuiChapterSelectIcon icon in icons) { AreaData area = AreaData.Get(icon.Area); if (area == null || area.GetLevelSet() != currentLevelSet) { continue; } int index = area.ToKey().ID; if ((string.IsNullOrEmpty(currentLevelSet) || index <= Math.Max(1, SaveData.Instance.UnlockedAreas)) && icon != unselected) { icon.Position = icon.HiddenPosition; icon.Show(); icon.AssistModeUnlockable = false; } else if (SaveData.Instance.AssistMode && index == SaveData.Instance.UnlockedAreas + 1) { icon.Position = icon.HiddenPosition; icon.Show(); icon.AssistModeUnlockable = true; } if (isVanilla) { yield return(0.01f); } } if (from is OuiChapterPanel) { yield return(0.25f); } }