using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using EntityStates; using HG; using RoR2.EntitlementManagement; using RoR2.ExpansionManagement; using RoR2.Stats; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; namespace RoR2.UI.LogBook; public class LogBookController : MonoBehaviour { private class NavigationPageInfo { public CategoryDef categoryDef; public Entry[] entries; public int index; public int indexInCategory; } private class LogBookState : EntityState { protected LogBookController logBookController; protected float unscaledAge; public override void OnEnter() { base.OnEnter(); logBookController = GetComponent(); } public override void Update() { base.Update(); unscaledAge += Time.unscaledDeltaTime; } } private class FadeState : LogBookState { private CanvasGroup canvasGroup; public float duration = 0.5f; public float endValue; public override void OnEnter() { base.OnEnter(); canvasGroup = GetComponent(); if ((bool)canvasGroup) { canvasGroup.alpha = 0f; } } public override void OnExit() { if ((bool)canvasGroup) { canvasGroup.alpha = endValue; } base.OnExit(); } public override void Update() { if ((bool)canvasGroup) { canvasGroup.alpha = unscaledAge / duration; if (canvasGroup.alpha >= 1f) { outer.SetNextState(new Idle()); } } base.Update(); } } private class ChangeEntriesPageState : LogBookState { private int oldPageIndex; public NavigationPageInfo newNavigationPageInfo; public float duration = 0.1f; public Vector2 moveDirection; private GameObject oldPage; private GameObject newPage; private Vector2 oldPageTargetPosition; private Vector2 newPageTargetPosition; private Vector2 containerSize = Vector2.zero; public override void OnEnter() { base.OnEnter(); if ((bool)logBookController) { oldPageIndex = logBookController.currentPageIndex; oldPage = logBookController.currentEntriesPageObject; newPage = logBookController.BuildEntriesPage(newNavigationPageInfo); containerSize = logBookController.entryPageContainer.rect.size; } SetPagePositions(0f); } public override void OnExit() { base.OnExit(); EntityState.Destroy(oldPage); if ((bool)logBookController) { logBookController.currentEntriesPageObject = newPage; logBookController.currentPageIndex = newNavigationPageInfo.indexInCategory; } } private void SetPagePositions(float t) { Vector2 vector = new Vector2(containerSize.x * (0f - moveDirection.x), containerSize.y * moveDirection.y); Vector2 vector2 = vector * t; if ((bool)oldPage) { oldPage.transform.localPosition = vector2; } if ((bool)newPage) { newPage.transform.localPosition = vector2 - vector; } } public override void Update() { base.Update(); float num = Mathf.Clamp01(unscaledAge / duration); SetPagePositions(num); if (num == 1f) { outer.SetNextState(new Idle()); } } } private class ChangeCategoryState : LogBookState { private int oldCategoryIndex; public int newCategoryIndex; public bool goToLastPage; public float duration = 0.1f; private GameObject oldPage; private GameObject newPage; private Vector2 oldPageTargetPosition; private Vector2 newPageTargetPosition; private Vector2 moveDirection; private Vector2 containerSize = Vector2.zero; private NavigationPageInfo[] newNavigationPages; private int destinationPageIndex; private NavigationPageInfo newNavigationPageInfo; private int frame; public override void OnEnter() { base.OnEnter(); if ((bool)logBookController) { oldCategoryIndex = logBookController.currentCategoryIndex; oldPage = logBookController.currentEntriesPageObject; newNavigationPages = logBookController.GetCategoryPages(newCategoryIndex); destinationPageIndex = newNavigationPages[0].index; if (goToLastPage) { destinationPageIndex = newNavigationPages[newNavigationPages.Length - 1].index; Debug.LogFormat("goToLastPage=true destinationPageIndex={0}", destinationPageIndex); } newNavigationPageInfo = logBookController.allNavigationPages[destinationPageIndex]; newPage = logBookController.BuildEntriesPage(newNavigationPageInfo); containerSize = logBookController.entryPageContainer.rect.size; moveDirection = new Vector2(Mathf.Sign(newCategoryIndex - oldCategoryIndex), 0f); } SetPagePositions(0f); } public override void OnExit() { EntityState.Destroy(oldPage); if ((bool)logBookController) { logBookController.currentEntriesPageObject = newPage; logBookController.currentPageIndex = newNavigationPageInfo.indexInCategory; logBookController.desiredPageIndex = newNavigationPageInfo.indexInCategory; logBookController.currentCategoryIndex = newCategoryIndex; logBookController.availableNavigationPages = newNavigationPages; logBookController.currentCategoryLabel.token = categories[newCategoryIndex].nameToken; logBookController.categoryHightlightRect.SetParent(logBookController.navigationCategoryButtonAllocator.elements[newCategoryIndex].transform, worldPositionStays: false); logBookController.categoryHightlightRect.gameObject.SetActive(value: false); logBookController.categoryHightlightRect.gameObject.SetActive(value: true); if (logBookController.moveNavigationPageIndicatorContainerToCategoryButton) { logBookController.navigationPageIndicatorContainer.SetParent(logBookController.navigationCategoryButtonAllocator.elements[newCategoryIndex].transform, worldPositionStays: false); } } base.OnExit(); } private void SetPagePositions(float t) { Vector2 vector = new Vector2(containerSize.x * (0f - moveDirection.x), containerSize.y * moveDirection.y); Vector2 vector2 = vector * t; if ((bool)oldPage) { oldPage.transform.localPosition = vector2; } if ((bool)newPage) { newPage.transform.localPosition = vector2 - vector; if (frame == 4) { newPage.GetComponent().enabled = false; } } } public override void Update() { base.Update(); frame++; float num = Mathf.Clamp01(unscaledAge / duration); SetPagePositions(num); if (num == 1f) { outer.SetNextState(new Idle()); } } } private class EnterLogViewState : LogBookState { public Texture iconTexture; public RectTransform startRectTransform; public RectTransform endRectTransform; public Entry entry; private GameObject flyingIcon; private RectTransform flyingIconTransform; private RawImage flyingIconImage; private float duration = 0.75f; private Rect startRect; private Rect midRect; private Rect endRect; private bool submittedViewEntry; public override void OnEnter() { base.OnEnter(); flyingIcon = new GameObject("FlyingIcon", typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage)); flyingIconTransform = (RectTransform)flyingIcon.transform; flyingIconTransform.SetParent(logBookController.transform, worldPositionStays: false); flyingIconTransform.localScale = Vector3.one; flyingIconImage = flyingIconTransform.GetComponent(); flyingIconImage.texture = iconTexture; Vector3[] array = new Vector3[4]; startRectTransform.GetWorldCorners(array); startRect = GetRectRelativeToParent(array); midRect = new Rect(((RectTransform)logBookController.transform).rect.center, startRect.size); endRectTransform.GetWorldCorners(array); endRect = GetRectRelativeToParent(array); SetIconRect(startRect); } private void SetIconRect(Rect rect) { flyingIconTransform.position = rect.position; flyingIconTransform.offsetMin = rect.min; flyingIconTransform.offsetMax = rect.max; } private Rect GetRectRelativeToParent(Vector3[] corners) { for (int i = 0; i < 4; i++) { corners[i] = logBookController.transform.InverseTransformPoint(corners[i]); } Rect result = default(Rect); result.xMin = corners[0].x; result.xMax = corners[2].x; result.yMin = corners[0].y; result.yMax = corners[2].y; return result; } private static Rect RectFromWorldCorners(Vector3[] corners) { Rect result = default(Rect); result.xMin = corners[0].x; result.xMax = corners[2].x; result.yMin = corners[0].y; result.yMax = corners[2].y; return result; } private static Rect LerpRect(Rect a, Rect b, float t) { Rect result = default(Rect); result.min = Vector2.LerpUnclamped(a.min, b.min, t); result.max = Vector2.LerpUnclamped(a.max, b.max, t); return result; } public override void OnExit() { EntityState.Destroy(flyingIcon); base.OnExit(); } public override void Update() { base.Update(); float num = Mathf.Min(unscaledAge / duration, 1f); if (num < 0.1f) { Util.Remap(num, 0f, 0.1f, 0f, 1f); SetIconRect(startRect); } if (num < 0.2f) { float t = Util.Remap(num, 0.1f, 0.2f, 0f, 1f); SetIconRect(LerpRect(startRect, midRect, t)); } else if (num < 0.4f) { Util.Remap(num, 0.2f, 0.4f, 0f, 1f); SetIconRect(midRect); } else if (num < 0.6f) { float t2 = Util.Remap(num, 0.4f, 0.6f, 0f, 1f); SetIconRect(LerpRect(midRect, endRect, t2)); } else if (num < 1f) { float num2 = Util.Remap(num, 0.6f, 1f, 0f, 1f); flyingIconImage.color = new Color(1f, 1f, 1f, 1f - num2); SetIconRect(endRect); if (!submittedViewEntry) { submittedViewEntry = true; logBookController.ViewEntry(entry); } } else { outer.SetNextState(new Idle()); } } } [Header("Navigation")] public GameObject navigationPanel; public RectTransform categoryContainer; public GameObject categorySpaceFiller; public int categorySpaceFillerCount; public Color spaceFillerColor; private UIElementAllocator navigationCategoryButtonAllocator; public RectTransform entryPageContainer; public GameObject entryPagePrefab; public RectTransform navigationPageIndicatorContainer; public GameObject navigationPageIndicatorPrefab; public bool moveNavigationPageIndicatorContainerToCategoryButton; private UIElementAllocator navigationPageIndicatorAllocator; public MPButton previousPageButton; public MPButton nextPageButton; public LanguageTextMeshController currentCategoryLabel; private RectTransform categoryHightlightRect; [Header("PageViewer")] public UnityEvent OnViewEntry; public GameObject pageViewerPanel; public MPButton pageViewerBackButton; [Header("Misc")] public GameObject categoryButtonPrefab; public GameObject headerHighlightPrefab; public LanguageTextMeshController hoverLanguageTextMeshController; public string hoverDescriptionFormatString; private EntityStateMachine stateMachine; private UILayerKey uiLayerKey; public static CategoryDef[] categories; public static ResourceAvailability availability; private static bool IsInitialized; public static bool IsStaticDataReady; private NavigationPageInfo[] _availableNavigationPages = Array.Empty(); private GameObject currentEntriesPageObject; private int currentCategoryIndex; private int desiredCategoryIndex; private int currentPageIndex; private int desiredPageIndex; private bool goToEndOfNextCategory; private NavigationPageInfo[] allNavigationPages; private NavigationPageInfo[][] navigationPagesByCategory; private static List lastBuiltEntitlements; private NavigationPageInfo[] availableNavigationPages { get { return _availableNavigationPages; } set { int num = _availableNavigationPages.Length; _availableNavigationPages = value; if (num != availableNavigationPages.Length) { navigationPageIndicatorAllocator.AllocateElements(availableNavigationPages.Length); } } } public static event Action OnViewablesRegistered; private void Awake() { EntitlementManager.UpdateLocalUsersEntitlements(); navigationCategoryButtonAllocator = new UIElementAllocator(categoryContainer, categoryButtonPrefab); navigationPageIndicatorAllocator = new UIElementAllocator(navigationPageIndicatorContainer, navigationPageIndicatorPrefab); navigationPageIndicatorAllocator.onCreateElement = delegate(int index, MPButton button) { button.onClick.AddListener(delegate { desiredPageIndex = index; }); }; previousPageButton.onClick.AddListener(OnLeftButton); nextPageButton.onClick.AddListener(OnRightButton); pageViewerBackButton.onClick.AddListener(ReturnToNavigation); stateMachine = base.gameObject.AddComponent(); uiLayerKey = base.gameObject.GetComponent(); stateMachine.initialStateType = default(SerializableEntityStateType); stateMachine.AllowStartWithoutNetworker = true; stateMachine.ShouldStateTransitionOnUpdate = true; categoryHightlightRect = (RectTransform)UnityEngine.Object.Instantiate(headerHighlightPrefab, base.transform.parent).transform; categoryHightlightRect.gameObject.SetActive(value: false); } private void Start() { GeneratePages(LocalUserManager.GetFirstLocalUser()?.userProfile); BuildCategoriesButtons(); stateMachine.SetNextState(new ChangeCategoryState { newCategoryIndex = 0 }); } private void BuildCategoriesButtons() { navigationCategoryButtonAllocator.AllocateElements(categories.Length); ReadOnlyCollection elements = navigationCategoryButtonAllocator.elements; for (int i = 0; i < categories.Length; i++) { int categoryIndex = i; MPButton mPButton = elements[i]; CategoryDef categoryDef = categories[i]; mPButton.GetComponentInChildren().text = Language.GetString(categoryDef.nameToken); mPButton.onClick.RemoveAllListeners(); mPButton.onClick.AddListener(delegate { OnCategoryClicked(categoryIndex); }); mPButton.requiredTopLayer = uiLayerKey; ViewableTag viewableTag = mPButton.gameObject.GetComponent(); if (!viewableTag) { viewableTag = mPButton.gameObject.AddComponent(); } viewableTag.viewableName = categoryDef.viewableNode.fullName; } if ((bool)categorySpaceFiller) { for (int j = 0; j < categorySpaceFillerCount; j++) { UnityEngine.Object.Instantiate(categorySpaceFiller, categoryContainer).gameObject.SetActive(value: true); } } } public static IEnumerator Init() { if (LocalUserManager.isAnyUserSignedIn) { BuildStaticData(); } yield return null; LocalUserManager.onUserSignIn += OnUserSignIn; BaseUserEntitlementTracker.OnUserEntitlementsUpdated = (Action)Delegate.Combine(BaseUserEntitlementTracker.OnUserEntitlementsUpdated, new Action(BuildStaticData)); IsInitialized = true; } private static void OnUserSignIn(LocalUser obj) { BuildStaticData(); } private static EntryStatus GetPickupStatus(in Entry entry, UserProfile viewerProfile) { UnlockableDef unlockableDef = null; PickupIndex pickupIndex = (PickupIndex)entry.extraData; PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); ItemIndex itemIndex = pickupDef?.itemIndex ?? ItemIndex.None; EquipmentIndex equipmentIndex = pickupDef?.equipmentIndex ?? EquipmentIndex.None; if (itemIndex != ItemIndex.None) { unlockableDef = ItemCatalog.GetItemDef(itemIndex).unlockableDef; } else { if (equipmentIndex == EquipmentIndex.None) { return EntryStatus.Unimplemented; } unlockableDef = EquipmentCatalog.GetEquipmentDef(equipmentIndex).unlockableDef; } if (!viewerProfile.HasUnlockable(unlockableDef)) { return EntryStatus.Locked; } if (!viewerProfile.HasDiscoveredPickup(pickupIndex)) { return EntryStatus.Unencountered; } return EntryStatus.Available; } private static TooltipContent GetPickupTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status) { UnlockableDef unlockableDef = PickupCatalog.GetPickupDef((PickupIndex)entry.extraData).unlockableDef; TooltipContent result = default(TooltipContent); if (status >= EntryStatus.Available) { result.overrideTitleText = entry.GetDisplayName(userProfile); result.titleColor = entry.color; if (unlockableDef != null) { result.overrideBodyText = unlockableDef.getUnlockedString(); } result.bodyToken = ""; result.bodyColor = ColorCatalog.GetColor(ColorCatalog.ColorIndex.Unlockable); } else { result.titleToken = "UNIDENTIFIED"; result.titleColor = Color.gray; result.bodyToken = ""; switch (status) { case EntryStatus.Unimplemented: result.titleToken = "TOOLTIP_WIP_CONTENT_NAME"; result.bodyToken = "TOOLTIP_WIP_CONTENT_DESCRIPTION"; break; case EntryStatus.Unencountered: result.overrideBodyText = Language.GetString("LOGBOOK_UNLOCK_ITEM_LOG"); break; case EntryStatus.Locked: result.overrideBodyText = unlockableDef.getHowToUnlockString(); break; } result.bodyColor = Color.white; } return result; } private static TooltipContent GetMonsterTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status) { TooltipContent result = default(TooltipContent); result.titleColor = entry.color; if (status >= EntryStatus.Available) { result.overrideTitleText = entry.GetDisplayName(userProfile); result.titleColor = entry.color; result.bodyToken = ""; } else { result.titleToken = "UNIDENTIFIED"; result.titleColor = Color.gray; result.bodyToken = "LOGBOOK_UNLOCK_ITEM_MONSTER"; } return result; } private static TooltipContent GetStageTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status) { TooltipContent result = default(TooltipContent); result.titleColor = entry.color; if (status >= EntryStatus.Available) { result.overrideTitleText = entry.GetDisplayName(userProfile); result.titleColor = entry.color; result.bodyToken = ""; } else { result.titleToken = "UNIDENTIFIED"; result.titleColor = Color.gray; result.bodyToken = "LOGBOOK_UNLOCK_ITEM_STAGE"; } return result; } private static TooltipContent GetSurvivorTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status) { TooltipContent result = default(TooltipContent); UnlockableDef unlockableDef = SurvivorCatalog.FindSurvivorDefFromBody(((CharacterBody)entry.extraData).gameObject).unlockableDef; if (status >= EntryStatus.Available) { result.overrideTitleText = entry.GetDisplayName(userProfile); result.titleColor = entry.color; result.bodyToken = ""; } else { result.titleToken = "UNIDENTIFIED"; result.bodyToken = ""; result.titleColor = Color.gray; switch (status) { case EntryStatus.Unencountered: result.overrideBodyText = Language.GetString("LOGBOOK_UNLOCK_ITEM_SURVIVOR"); break; case EntryStatus.Locked: result.overrideBodyText = unlockableDef.getHowToUnlockString(); break; } } return result; } private static TooltipContent GetAchievementTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status) { TooltipContent result = default(TooltipContent); UnlockableDef unlockableDef = UnlockableCatalog.GetUnlockableDef(((AchievementDef)entry.extraData).unlockableRewardIdentifier); result.titleColor = entry.color; result.bodyToken = ""; if (unlockableDef == null) { result.overrideTitleText = entry.GetDisplayName(userProfile); result.titleColor = Color.gray; result.overrideBodyText = "ACHIEVEMENT HAS NO UNLOCKABLE DEFINED"; result.bodyColor = Color.white; return result; } if (status >= EntryStatus.Available) { result.titleToken = entry.GetDisplayName(userProfile); result.titleColor = entry.color; result.overrideBodyText = unlockableDef.getUnlockedString(); } else { result.titleToken = "UNIDENTIFIED"; result.titleColor = Color.gray; if (status == EntryStatus.Locked) { result.overrideBodyText = Language.GetString("UNIDENTIFIED_DESCRIPTION"); } else { result.overrideBodyText = unlockableDef.getHowToUnlockString(); } } return result; } private static TooltipContent GetWIPTooltipContent(in Entry entry, UserProfile userProfile, EntryStatus status) { TooltipContent result = default(TooltipContent); result.titleColor = ColorCatalog.GetColor(ColorCatalog.ColorIndex.WIP); result.titleToken = "TOOLTIP_WIP_CONTENT_NAME"; result.bodyToken = "TOOLTIP_WIP_CONTENT_DESCRIPTION"; return result; } private static EntryStatus GetAlwaysAvailable(UserProfile userProfile, Entry entry) { return EntryStatus.Available; } private static EntryStatus GetUnimplemented(in Entry entry, UserProfile viewerProfile) { return EntryStatus.Unimplemented; } private static EntryStatus GetStageStatus(in Entry entry, UserProfile viewerProfile) { UnlockableDef unlockableLogFromBaseSceneName = SceneCatalog.GetUnlockableLogFromBaseSceneName((entry.extraData as SceneDef).baseSceneName); if (unlockableLogFromBaseSceneName != null && viewerProfile.HasUnlockable(unlockableLogFromBaseSceneName)) { return EntryStatus.Available; } return EntryStatus.Unencountered; } private static EntryStatus GetMonsterStatus(in Entry entry, UserProfile viewerProfile) { CharacterBody characterBody = (CharacterBody)entry.extraData; UnlockableDef unlockableDef = characterBody.GetComponent()?.logUnlockableDef; if (!unlockableDef) { return EntryStatus.None; } if (viewerProfile.HasUnlockable(unlockableDef)) { return EntryStatus.Available; } if (viewerProfile.statSheet.GetStatValueULong(PerBodyStatDef.killsAgainst, characterBody.gameObject.name) != 0) { return EntryStatus.Unencountered; } return EntryStatus.Locked; } private static EntryStatus GetSurvivorStatus(in Entry entry, UserProfile viewerProfile) { CharacterBody characterBody = (CharacterBody)entry.extraData; SurvivorDef survivorDef = SurvivorCatalog.FindSurvivorDefFromBody(characterBody.gameObject); if (!viewerProfile.HasUnlockable(survivorDef.unlockableDef)) { return EntryStatus.Locked; } if (viewerProfile.statSheet.GetStatValueULong(PerBodyStatDef.totalWins, characterBody.gameObject.name) == 0L) { return EntryStatus.Unencountered; } return EntryStatus.Available; } private static EntryStatus GetAchievementStatus(in Entry entry, UserProfile viewerProfile) { string identifier = ((AchievementDef)entry.extraData).identifier; bool flag = viewerProfile.HasAchievement(identifier); if (!viewerProfile.CanSeeAchievement(identifier)) { return EntryStatus.Locked; } if (!flag) { return EntryStatus.Unencountered; } return EntryStatus.Available; } private static void BuildStaticData() { RoR2Application.instance.StartCoroutine(WaitForBuildingStaticData()); } static LogBookController() { categories = Array.Empty(); availability = default(ResourceAvailability); IsInitialized = false; IsStaticDataReady = false; lastBuiltEntitlements = null; } private NavigationPageInfo[] GetCategoryPages(int categoryIndex) { if (navigationPagesByCategory.GetLength(0) <= categoryIndex) { return new NavigationPageInfo[0]; } return navigationPagesByCategory[categoryIndex]; } public void OnLeftButton() { desiredPageIndex--; } public void OnRightButton() { desiredPageIndex++; } private void OnCategoryClicked(int categoryIndex) { desiredCategoryIndex = categoryIndex; goToEndOfNextCategory = false; } public void OnCategoryLeftButton() { desiredCategoryIndex--; goToEndOfNextCategory = false; } public void OnCategoryRightButton() { desiredCategoryIndex++; goToEndOfNextCategory = false; } private void GeneratePages(UserProfile viewerProfile) { if (!IsInitialized) { Init(); } navigationPagesByCategory = new NavigationPageInfo[categories.Length][]; IEnumerable enumerable = Array.Empty(); int num = 0; for (int i = 0; i < categories.Length; i++) { CategoryDef categoryDef = categories[i]; Entry[] array = categoryDef.BuildEntries(viewerProfile); bool fullWidth = categoryDef.fullWidth; Vector2 size = entryPageContainer.rect.size; if (fullWidth) { categoryDef.iconSize.x = size.x; } int num2 = Mathf.FloorToInt(Mathf.Max(size.x / categoryDef.iconSize.x, 1f)); int num3 = Mathf.FloorToInt(Mathf.Max(size.y / categoryDef.iconSize.y, 1f)); int num4 = num2 * num3; int num5 = Mathf.CeilToInt((float)array.Length / (float)num4); if (num5 <= 0) { num5 = 1; } NavigationPageInfo[] array2 = new NavigationPageInfo[num5]; for (int j = 0; j < num5; j++) { int num6 = j * num4; int num7 = array.Length - num6; int num8 = num4; if (num8 > num7) { num8 = num7; } Entry[] array3 = new Entry[num4]; Array.Copy(array, num6, array3, 0, num8); NavigationPageInfo navigationPageInfo = new NavigationPageInfo(); navigationPageInfo.categoryDef = categoryDef; navigationPageInfo.entries = array3; navigationPageInfo.index = num++; navigationPageInfo.indexInCategory = j; array2[j] = navigationPageInfo; } navigationPagesByCategory[i] = array2; enumerable = enumerable.Concat(array2); } allNavigationPages = enumerable.ToArray(); } private void Update() { if (desiredPageIndex > availableNavigationPages.Length - 1) { desiredPageIndex = availableNavigationPages.Length - 1; desiredCategoryIndex++; goToEndOfNextCategory = false; } if (desiredPageIndex < 0) { desiredCategoryIndex--; desiredPageIndex = 0; goToEndOfNextCategory = true; } if (desiredCategoryIndex > categories.Length - 1) { desiredCategoryIndex = 0; goToEndOfNextCategory = false; } if (desiredCategoryIndex < 0) { desiredCategoryIndex = categories.Length - 1; goToEndOfNextCategory = true; } foreach (MPButton element in navigationPageIndicatorAllocator.elements) { ColorBlock colors = element.colors; colors.colorMultiplier = 1f; element.colors = colors; } if (currentPageIndex < navigationPageIndicatorAllocator.elements.Count) { MPButton mPButton = navigationPageIndicatorAllocator.elements[currentPageIndex]; ColorBlock colors2 = mPButton.colors; colors2.colorMultiplier = 2f; mPButton.colors = colors2; } if (desiredCategoryIndex != currentCategoryIndex) { if (stateMachine.state is Idle) { int num = ((desiredCategoryIndex > currentCategoryIndex) ? 1 : (-1)); stateMachine.SetNextState(new ChangeCategoryState { newCategoryIndex = currentCategoryIndex + num, goToLastPage = goToEndOfNextCategory }); } } else if (desiredPageIndex != currentPageIndex && stateMachine.state is Idle) { int num2 = ((desiredPageIndex > currentPageIndex) ? 1 : (-1)); stateMachine.SetNextState(new ChangeEntriesPageState { newNavigationPageInfo = GetCategoryPages(currentCategoryIndex)[currentPageIndex + num2], moveDirection = new Vector2(num2, 0f) }); } } private UserProfile LookUpUserProfile() { return LocalUserManager.readOnlyLocalUsersList.FirstOrDefault((LocalUser v) => v != null)?.userProfile; } private GameObject BuildEntriesPage(NavigationPageInfo navigationPageInfo) { Entry[] entries = navigationPageInfo.entries; CategoryDef categoryDef = navigationPageInfo.categoryDef; GameObject gameObject = UnityEngine.Object.Instantiate(entryPagePrefab, entryPageContainer); GridLayoutGroup component = gameObject.GetComponent(); component.cellSize = categoryDef.iconSize; component.constraint = GridLayoutGroup.Constraint.FixedColumnCount; float num = entryPageContainer.rect.width - (float)component.padding.left - (float)component.padding.right; component.constraintCount = (int)(num / (component.cellSize.x + component.spacing.x)); UIElementAllocator uIElementAllocator = new UIElementAllocator((RectTransform)gameObject.transform, categoryDef.iconPrefab); uIElementAllocator.AllocateElements(entries.Length); UserProfile userProfile = LookUpUserProfile(); ReadOnlyCollection elements = uIElementAllocator.elements; for (int i = 0; i < elements.Count; i++) { RectTransform rectTransform = elements[i]; HGButton component2 = rectTransform.GetComponent(); Entry entry = ((i < entries.Length) ? entries[i] : null); EntryStatus entryStatus = entry?.GetStatus(userProfile) ?? EntryStatus.None; if (entryStatus != 0) { TooltipContent tooltipContent = entry.GetTooltipContent(userProfile, entryStatus); categoryDef.initializeElementGraphics?.Invoke(rectTransform.gameObject, entry, entryStatus, userProfile); if ((bool)component2) { UnityEngine.UI.Navigation navigation = component2.navigation; navigation.mode = UnityEngine.UI.Navigation.Mode.Explicit; int index = (i + elements.Count - 1) % elements.Count; navigation.selectOnLeft = elements[index].GetComponent(); int index2 = (i + elements.Count + 1) % elements.Count; navigation.selectOnRight = elements[index2].GetComponent(); if (i - component.constraintCount >= 0) { navigation.selectOnUp = elements[i - component.constraintCount].GetComponent(); } if (i + component.constraintCount < elements.Count) { navigation.selectOnDown = elements[i + component.constraintCount].GetComponent(); } component2.navigation = navigation; bool flag = entryStatus >= EntryStatus.Available; component2.interactable = true; component2.disableGamepadClick = component2.disableGamepadClick || !flag; component2.disablePointerClick = component2.disablePointerClick || !flag; component2.imageOnInteractable = (flag ? component2.imageOnInteractable : null); component2.requiredTopLayer = uiLayerKey; component2.updateTextOnHover = true; component2.hoverLanguageTextMeshController = hoverLanguageTextMeshController; string titleText = tooltipContent.GetTitleText(); string bodyText = tooltipContent.GetBodyText(); Color titleColor = tooltipContent.titleColor; titleColor.a = 0.2f; component2.hoverToken = Language.GetStringFormatted("LOGBOOK_HOVER_DESCRIPTION_FORMAT", titleText, bodyText, ColorUtility.ToHtmlStringRGBA(titleColor)); } if (entry.viewableNode != null) { ViewableTag viewableTag = rectTransform.gameObject.GetComponent(); if (!viewableTag) { viewableTag = rectTransform.gameObject.AddComponent(); viewableTag.viewableVisualStyle = ViewableTag.ViewableVisualStyle.Icon; } viewableTag.viewableName = entry.viewableNode.fullName; } } if (entryStatus >= EntryStatus.Available && (bool)component2) { component2.onClick.AddListener(delegate { ViewEntry(entry); }); } if (entryStatus == EntryStatus.None) { if ((bool)component2) { component2.enabled = false; component2.targetGraphic.color = spaceFillerColor; } else { rectTransform.GetComponent().color = spaceFillerColor; } for (int num2 = rectTransform.childCount - 1; num2 >= 0; num2--) { UnityEngine.Object.Destroy(rectTransform.GetChild(num2).gameObject); } } if ((bool)component2 && i == 0) { component2.defaultFallbackButton = true; } } gameObject.gameObject.SetActive(value: true); GridLayoutGroup gridLayoutGroup = gameObject.GetComponent(); Action destroyLayoutGroup = null; int frameTimer = 2; destroyLayoutGroup = delegate { int num3 = frameTimer - 1; frameTimer = num3; if (frameTimer <= 0) { gridLayoutGroup.enabled = false; RoR2Application.onLateUpdate -= destroyLayoutGroup; } }; RoR2Application.onLateUpdate += destroyLayoutGroup; return gameObject; } private void ViewEntry(Entry entry) { OnViewEntry.Invoke(); LogBookPage component = pageViewerPanel.GetComponent(); component.SetEntry(LookUpUserProfile(), entry); component.modelPanel.SetAnglesForCharacterThumbnailForSeconds(0.5f); ViewableTrigger.TriggerView(entry.viewableNode?.fullName); } private void ReturnToNavigation() { navigationPanel.SetActive(value: true); pageViewerPanel.SetActive(value: false); } private static bool UnlockableExists(UnlockableDef unlockableDef) { return unlockableDef; } private static bool IsEntryBodyWithoutLore(in Entry entry) { CharacterBody obj = (CharacterBody)entry.extraData; bool flag = false; string text = ""; string baseNameToken = obj.baseNameToken; if (!string.IsNullOrEmpty(baseNameToken)) { text = baseNameToken.Replace("_NAME", "_LORE"); if (Language.english.TokenIsRegistered(text)) { flag = true; } } return !flag; } private static bool IsEntryPickupItemWithoutLore(in Entry entry) { ItemDef itemDef = ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef((PickupIndex)entry.extraData).itemIndex); return !Language.english.TokenIsRegistered(itemDef.loreToken); } private static bool IsEntryPickupEquipmentWithoutLore(in Entry entry) { EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(PickupCatalog.GetPickupDef((PickupIndex)entry.extraData).equipmentIndex); return !Language.english.TokenIsRegistered(equipmentDef.loreToken); } private static bool CanSelectItemEntry(ItemDef itemDef, Dictionary expansionAvailability) { if (itemDef != null) { ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(itemDef.tier); if ((bool)itemTierDef && itemTierDef.isDroppable) { if (!(itemDef.requiredExpansion == null) && expansionAvailability.ContainsKey(itemDef.requiredExpansion)) { return expansionAvailability[itemDef.requiredExpansion]; } return true; } return false; } return false; } private static bool CanSelectEquipmentEntry(EquipmentDef equipmentDef, Dictionary expansionAvailability) { if (equipmentDef != null) { if (equipmentDef.canDrop) { if (!(equipmentDef.requiredExpansion == null) && expansionAvailability.ContainsKey(equipmentDef.requiredExpansion)) { return expansionAvailability[equipmentDef.requiredExpansion]; } return true; } return false; } return false; } private static Entry[] BuildPickupEntries(Dictionary expansionAvailability) { new Entry { nameToken = "TOOLTIP_WIP_CONTENT_NAME", color = Color.white, iconTexture = LegacyResourcesAPI.Load("Textures/MiscIcons/texWIPIcon"), getStatusImplementation = GetUnimplemented, getTooltipContentImplementation = GetWIPTooltipContent }; IEnumerable first = from pickupDef in PickupCatalog.allPickups select ItemCatalog.GetItemDef(pickupDef.itemIndex) into itemDef where CanSelectItemEntry(itemDef, expansionAvailability) orderby (int)(itemDef.tier + ((itemDef.tier == ItemTier.Lunar) ? 100 : 0)) select new Entry { nameToken = itemDef.nameToken, color = ColorCatalog.GetColor(itemDef.darkColorIndex), iconTexture = itemDef.pickupIconTexture, bgTexture = itemDef.bgIconTexture, extraData = PickupCatalog.FindPickupIndex(itemDef.itemIndex), modelPrefab = itemDef.pickupModelPrefab, getStatusImplementation = GetPickupStatus, getTooltipContentImplementation = GetPickupTooltipContent, pageBuilderMethod = PageBuilder.SimplePickup, isWIPImplementation = IsEntryPickupItemWithoutLore }; IEnumerable second = from pickupDef in PickupCatalog.allPickups select EquipmentCatalog.GetEquipmentDef(pickupDef.equipmentIndex) into equipmentDef where CanSelectEquipmentEntry(equipmentDef, expansionAvailability) orderby !equipmentDef.isLunar select new Entry { nameToken = equipmentDef.nameToken, color = ColorCatalog.GetColor(equipmentDef.colorIndex), iconTexture = equipmentDef.pickupIconTexture, bgTexture = equipmentDef.bgIconTexture, extraData = PickupCatalog.FindPickupIndex(equipmentDef.equipmentIndex), modelPrefab = equipmentDef.pickupModelPrefab, getStatusImplementation = GetPickupStatus, getTooltipContentImplementation = GetPickupTooltipContent, pageBuilderMethod = PageBuilder.SimplePickup, isWIPImplementation = IsEntryPickupEquipmentWithoutLore }; return first.Concat(second).ToArray(); } private static bool CanSelectMonsterEntry(CharacterBody characterBody, Dictionary expansionAvailability) { if ((bool)characterBody) { ExpansionRequirementComponent component = characterBody.GetComponent(); if (!component || !component.requiredExpansion || !expansionAvailability.ContainsKey(component.requiredExpansion) || expansionAvailability[component.requiredExpansion]) { return UnlockableExists(characterBody.GetComponent()?.logUnlockableDef); } return false; } return false; } private static Entry[] BuildMonsterEntries(Dictionary expansionAvailability) { Entry[] array = (from characterBody in BodyCatalog.allBodyPrefabBodyBodyComponents where CanSelectMonsterEntry(characterBody, expansionAvailability) orderby characterBody.baseMaxHealth select new Entry { nameToken = characterBody.baseNameToken, color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.HardDifficulty), iconTexture = characterBody.portraitIcon, extraData = characterBody, modelPrefab = characterBody.GetComponent()?.modelTransform?.gameObject, getStatusImplementation = GetMonsterStatus, getTooltipContentImplementation = GetMonsterTooltipContent, pageBuilderMethod = PageBuilder.MonsterBody, bgTexture = (characterBody.isChampion ? LegacyResourcesAPI.Load("Textures/ItemIcons/BG/texTier3BGIcon") : LegacyResourcesAPI.Load("Textures/ItemIcons/BG/texTier1BGIcon")), isWIPImplementation = IsEntryBodyWithoutLore }).ToArray(); Dictionary dictionary = new Dictionary(); Entry[] array2 = array; foreach (Entry entry in array2) { if (!dictionary.ContainsKey(entry.nameToken)) { dictionary.Add(entry.nameToken, entry); } } return dictionary.Values.ToArray(); } private static bool CanSelectStageEntry(SceneDef sceneDef, Dictionary expansionAvailability) { if ((bool)sceneDef) { ExpansionDef requiredExpansion = sceneDef.requiredExpansion; if (!requiredExpansion || !expansionAvailability.ContainsKey(requiredExpansion) || expansionAvailability[requiredExpansion]) { return sceneDef.shouldIncludeInLogbook; } return false; } return false; } private static Entry[] BuildStageEntries(Dictionary expansionAvailability) { return (from sceneDef in SceneCatalog.allSceneDefs where CanSelectStageEntry(sceneDef, expansionAvailability) orderby sceneDef.stageOrder select new Entry { nameToken = sceneDef.nameToken, iconTexture = sceneDef.previewTexture, color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.Tier2ItemDark), getStatusImplementation = GetStageStatus, modelPrefab = sceneDef.dioramaPrefab, getTooltipContentImplementation = GetStageTooltipContent, pageBuilderMethod = PageBuilder.Stage, extraData = sceneDef, isWIPImplementation = delegate(in Entry entry) { return !Language.english.TokenIsRegistered(((SceneDef)entry.extraData).loreToken); } }).ToArray(); } private static bool CanSelectSurvivorBodyEntry(CharacterBody body, Dictionary expansionAvailability) { if ((bool)body) { ExpansionRequirementComponent component = body.GetComponent(); if ((bool)component && (bool)component.requiredExpansion && expansionAvailability.ContainsKey(component.requiredExpansion)) { return expansionAvailability[component.requiredExpansion]; } return true; } return false; } private static Entry[] BuildSurvivorEntries(Dictionary expansionAvailability) { return (from survivorDef in SurvivorCatalog.orderedSurvivorDefs select BodyCatalog.GetBodyPrefabBodyComponent(SurvivorCatalog.GetBodyIndexFromSurvivorIndex(survivorDef.survivorIndex)) into body where CanSelectSurvivorBodyEntry(body, expansionAvailability) select body into characterBody select new Entry { nameToken = characterBody.baseNameToken, color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.NormalDifficulty), iconTexture = characterBody.portraitIcon, bgTexture = LegacyResourcesAPI.Load("Textures/ItemIcons/BG/texSurvivorBGIcon"), extraData = characterBody, modelPrefab = characterBody.GetComponent()?.modelTransform?.gameObject, getStatusImplementation = GetSurvivorStatus, getTooltipContentImplementation = GetSurvivorTooltipContent, pageBuilderMethod = PageBuilder.SurvivorBody, isWIPImplementation = IsEntryBodyWithoutLore }).ToArray(); } private static bool CanSelectAchievementEntry(AchievementDef achievementDef, Dictionary expansionAvailability) { if (achievementDef != null) { ExpansionDef expansionDefForUnlockable = UnlockableCatalog.GetExpansionDefForUnlockable(UnlockableCatalog.GetUnlockableDef(achievementDef.unlockableRewardIdentifier)?.index ?? UnlockableIndex.None); if ((bool)expansionDefForUnlockable && expansionAvailability.ContainsKey(expansionDefForUnlockable)) { return expansionAvailability[expansionDefForUnlockable]; } return true; } return false; } private static Entry[] BuildAchievementEntries(Dictionary expansionAvailability) { return (from achievementDef in AchievementManager.allAchievementDefs where CanSelectAchievementEntry(achievementDef, expansionAvailability) select new Entry { nameToken = achievementDef.nameToken, color = ColorCatalog.GetColor(ColorCatalog.ColorIndex.NormalDifficulty), iconTexture = achievementDef.GetAchievedIcon()?.texture, extraData = achievementDef, modelPrefab = null, getStatusImplementation = GetAchievementStatus, getTooltipContentImplementation = GetAchievementTooltipContent }).ToArray(); } private static Entry[] BuildProfileEntries(UserProfile viewerProfile) { List entries = new List(); if (true) { foreach (string availableProfileName in PlatformSystems.saveSystem.GetAvailableProfileNames()) { AddProfileStatsEntry(PlatformSystems.saveSystem.GetProfile(availableProfileName)); } } else if (viewerProfile != null) { AddProfileStatsEntry(viewerProfile); } return entries.ToArray(); void AddProfileStatsEntry(UserProfile userProfile) { Entry item = new Entry { pageBuilderMethod = PageBuilder.StatsPanel, getStatusImplementation = delegate { return EntryStatus.Available; }, extraData = userProfile, getDisplayNameImplementation = delegate(in Entry entry, UserProfile _viewerProfile) { return ((UserProfile)entry.extraData).name; }, iconTexture = userProfile.portraitTexture }; entries.Add(item); } } private static Entry[] BuildMorgueEntries(UserProfile viewerProfile) { List entries = CollectionPool>.RentCollection(); List list = CollectionPool>.RentCollection(); MorgueManager.LoadHistoryRunReports(list); foreach (RunReport item2 in list) { AddRunReportEntry(item2); } CollectionPool>.ReturnCollection(list); Entry[] result = entries.ToArray(); CollectionPool>.ReturnCollection(entries); return result; void AddRunReportEntry(RunReport runReport) { GetPrimaryPlayerInfo(runReport, out var _, out var primaryPlayerBody3); Entry item = new Entry { pageBuilderMethod = PageBuilder.RunReportPanel, getStatusImplementation = GetEntryStatus, extraData = runReport, getDisplayNameImplementation = GetDisplayName, getTooltipContentImplementation = GetTooltipContent, iconTexture = primaryPlayerBody3?.portraitIcon }; entries.Add(item); } static string GetDisplayName(in Entry entry, UserProfile _viewerProfile) { return ((RunReport)entry.extraData).snapshotTimeUtc.ToLocalTime().ToString("G", CultureInfo.CurrentCulture); } static EntryStatus GetEntryStatus(in Entry entry, UserProfile _viewerProfile) { return EntryStatus.Available; } void GetPrimaryPlayerInfo(RunReport _runReport, out RunReport.PlayerInfo primaryPlayerInfo, out CharacterBody primaryPlayerBody) { primaryPlayerInfo = _runReport.FindPlayerInfo(viewerProfile) ?? _runReport.FindFirstPlayerInfo(); primaryPlayerBody = BodyCatalog.GetBodyPrefabBodyComponent(primaryPlayerInfo?.bodyIndex ?? BodyIndex.None); } TooltipContent GetTooltipContent(in Entry entry, UserProfile _viewerProfile, EntryStatus entryStatus) { RunReport runReport2 = (RunReport)entry.extraData; GetPrimaryPlayerInfo(runReport2, out var _, out var primaryPlayerBody2); TooltipContent result2 = default(TooltipContent); result2.overrideTitleText = Language.GetStringFormatted("LOGBOOK_ENTRY_RUNREPORT_TOOLTIP_TITLE_FORMAT", runReport2.snapshotTimeUtc.ToLocalTime().ToString("G", CultureInfo.CurrentCulture)); result2.overrideBodyText = Language.GetStringFormatted("LOGBOOK_ENTRY_RUNREPORT_TOOLTIP_BODY_FORMAT", Language.GetString(primaryPlayerBody2?.baseNameToken ?? string.Empty), Language.GetString(runReport2.gameMode?.nameToken ?? string.Empty), Language.GetString(runReport2.gameEnding?.endingTextToken ?? string.Empty)); return result2; } } private static IEnumerator WaitForBuildingStaticData() { IsStaticDataReady = false; Dictionary expansionAvailability = new Dictionary(); List list = new List(); foreach (ExpansionDef expansionDef in ExpansionCatalog.expansionDefs) { expansionAvailability.Add(expansionDef, !expansionDef.requiredEntitlement || EntitlementManager.localUserEntitlementTracker.AnyUserHasEntitlement(expansionDef.requiredEntitlement)); if (expansionAvailability[expansionDef]) { list.Add(expansionDef.requiredEntitlement); } } if (lastBuiltEntitlements != null && lastBuiltEntitlements.SequenceEqual(list)) { lastBuiltEntitlements = list; IsStaticDataReady = true; yield break; } lastBuiltEntitlements = list; yield return null; Entry[] pickupEntries = BuildPickupEntries(expansionAvailability); yield return null; Entry[] monsterEntries = BuildMonsterEntries(expansionAvailability); yield return null; Entry[] stageEntries = BuildStageEntries(expansionAvailability); yield return null; Entry[] survivorEntries = BuildSurvivorEntries(expansionAvailability); yield return null; Entry[] achievementEntries = BuildAchievementEntries(expansionAvailability); yield return null; categories = new CategoryDef[7] { new CategoryDef { nameToken = "LOGBOOK_CATEGORY_ITEMANDEQUIPMENT", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/ItemEntryIcon"), buildEntries = (UserProfile viewerProfile) => pickupEntries, navButtons = new CategoryDef.NavButtons { navigate = "LOGBOOK_ZOOMINOUT" } }, new CategoryDef { nameToken = "LOGBOOK_CATEGORY_MONSTER", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/MonsterEntryIcon"), buildEntries = (UserProfile viewerProfile) => monsterEntries, navButtons = new CategoryDef.NavButtons { navigate = "LOGBOOK_ZOOMINOUT" } }, new CategoryDef { nameToken = "LOGBOOK_CATEGORY_STAGE", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/StageEntryIcon"), buildEntries = (UserProfile viewerProfile) => stageEntries }, new CategoryDef { nameToken = "LOGBOOK_CATEGORY_SURVIVOR", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/SurvivorEntryIcon"), buildEntries = (UserProfile viewerProfile) => survivorEntries }, new CategoryDef { nameToken = "LOGBOOK_CATEGORY_ACHIEVEMENTS", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/AchievementEntryIcon"), buildEntries = (UserProfile viewerProfile) => achievementEntries, initializeElementGraphics = CategoryDef.InitializeChallenge }, new CategoryDef { nameToken = "LOGBOOK_CATEGORY_STATS", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/StatsEntryIcon"), buildEntries = BuildProfileEntries, initializeElementGraphics = CategoryDef.InitializeStats, navButtons = new CategoryDef.NavButtons { scroll = "", rotate = "LOGBOOK_PAN" } }, new CategoryDef { nameToken = "LOGBOOK_CATEGORY_MORGUE", iconPrefab = LegacyResourcesAPI.Load("Prefabs/UI/Logbook/MorgueEntryIcon"), buildEntries = BuildMorgueEntries, initializeElementGraphics = CategoryDef.InitializeMorgue, navButtons = new CategoryDef.NavButtons { rotate = "", navigate = "" } } }; RegisterViewables(categories); availability.MakeAvailable(); IsStaticDataReady = true; EntitlementManager.UpdateLocalUsersEntitlements(); } [ConCommand(commandName = "logbook_list_unfinished_lore", flags = ConVarFlags.None, helpText = "Prints all logbook entries which still have undefined lore.")] private static void CCLogbookListUnfinishedLore(ConCommandArgs args) { List list = new List(); CategoryDef[] array = categories; for (int i = 0; i < array.Length; i++) { Entry[] array2 = array[i].BuildEntries(args.GetSenderLocalUser()?.userProfile); foreach (Entry entry in array2) { string text = ""; if (entry.extraData is UnityEngine.Object @object) { text = @object.name; } if (entry.isWip) { list.Add(entry.extraData?.GetType()?.Name + " \"" + text + "\" \"" + Language.GetString(entry.nameToken) + "\""); } } } args.Log(string.Join("\n", list)); } private static void RegisterViewables(CategoryDef[] categoriesToGenerateFrom) { ViewablesCatalog.Node node = new ViewablesCatalog.Node("Logbook", isFolder: true); CategoryDef[] array = categories; foreach (CategoryDef obj in array) { ViewablesCatalog.Node parent = (obj.viewableNode = new ViewablesCatalog.Node(obj.nameToken, isFolder: true, node)); Entry[] array2 = obj.BuildEntries(null); foreach (Entry entry in array2) { string nameToken = entry.nameToken; ViewablesCatalog.Node entryNode; AchievementDef achievementDef; bool hasPrereq; if (!entry.isWip && !(nameToken == "TOOLTIP_WIP_CONTENT_NAME") && !string.IsNullOrEmpty(nameToken)) { entryNode = new ViewablesCatalog.Node(nameToken, isFolder: false, parent); object extraData = entry.extraData; achievementDef = extraData as AchievementDef; if (achievementDef != null) { hasPrereq = !string.IsNullOrEmpty(achievementDef.prerequisiteAchievementIdentifier); entryNode.shouldShowUnviewed = Check; } else { entryNode.shouldShowUnviewed = Check; } entry.viewableNode = entryNode; } bool Check(UserProfile userProfile) { if (userProfile.HasViewedViewable(entryNode.fullName)) { return false; } if (userProfile.HasAchievement(achievementDef.identifier)) { return false; } if (hasPrereq) { return userProfile.HasAchievement(achievementDef.prerequisiteAchievementIdentifier); } return true; } bool Check(UserProfile viewerProfile) { if (viewerProfile.HasViewedViewable(entryNode.fullName)) { return false; } return entry.GetStatus(viewerProfile) == EntryStatus.Available; } } } ViewablesCatalog.AddNodeToRoot(node); LogBookController.OnViewablesRegistered?.Invoke(); } }