diff --git a/Assets/DownloadsIcon.png b/Assets/DownloadsIcon.png new file mode 100644 index 0000000..85deb70 Binary files /dev/null and b/Assets/DownloadsIcon.png differ diff --git a/Assets/FullLikeIcon.png b/Assets/FullLikeIcon.png new file mode 100644 index 0000000..01690a1 Binary files /dev/null and b/Assets/FullLikeIcon.png differ diff --git a/BloonFactory.cs b/BloonFactory.cs index 243ed04..1d6de4a 100644 --- a/BloonFactory.cs +++ b/BloonFactory.cs @@ -26,13 +26,6 @@ public override void OnApplicationStart() ValueColors.ColorByLinkType[typeof(Trigger)] = Color.magenta; ValueColors.ColorByLinkType[typeof(Visuals)] = Color.cyan; } - public override void OnUpdate() - { - if (Input.GetKeyDown(KeyCode.K)) - { - ModGameMenu.Open(); - } - } public override void OnNewGameModel(GameModel result) { foreach (var bloon in CustomBloon.Bloons) diff --git a/BloonFactory.csproj b/BloonFactory.csproj index 5b7b1dd..849c1c8 100644 --- a/BloonFactory.csproj +++ b/BloonFactory.csproj @@ -22,6 +22,9 @@ ..\..\SteamLibrary\steamapps\common\BloonsTD6\Mods\FactoryCore.dll + + ..\..\SteamLibrary\steamapps\common\BloonsTD6\MelonLoader\Il2CppAssemblies\Il2CppNinjaKiwi.LiNK.dll + diff --git a/CustomBloon.cs b/CustomBloon.cs index 513615b..6d7aa76 100644 --- a/CustomBloon.cs +++ b/CustomBloon.cs @@ -73,6 +73,7 @@ public void ModifyExistingBloonModel(BloonModel model, RoundSetModel roundset) } DamageStateDisplayModule.DamageStateFix(model); + } public override IEnumerable Load() { diff --git a/ModuleProperties/BloonTextureModuleProperty.cs b/ModuleProperties/BloonTextureModuleProperty.cs index b90fd37..f1baa5c 100644 --- a/ModuleProperties/BloonTextureModuleProperty.cs +++ b/ModuleProperties/BloonTextureModuleProperty.cs @@ -36,7 +36,7 @@ public override ModHelperPanel GetVisual(ModHelperPanel root) TaskScheduler.ScheduleTask(() => { UpdateImage(image); - }, ScheduleType.WaitForFrames, 5); + }); return panel; } diff --git a/Modules/Display/BloonDisplay.cs b/Modules/Display/BloonDisplay.cs index e5b4a7f..2a74da6 100644 --- a/Modules/Display/BloonDisplay.cs +++ b/Modules/Display/BloonDisplay.cs @@ -5,12 +5,14 @@ using Il2CppAssets.Scripts.Models.GenericBehaviors; using Il2CppAssets.Scripts.Unity.Display; using System; +using System.Collections.Generic; using UnityEngine; namespace BloonFactory.Modules.Display { internal class BloonDisplay : ModDisplay2D { + private static Dictionary Cache = new Dictionary(); protected override string TextureName => "BaseBloon"; public override DisplayCategory DisplayCategory => DisplayCategory.Bloon; public override string BaseDisplay => "9d3c0064c3ace7448bf8fefa4a97a70f"; @@ -36,7 +38,13 @@ public BloonDisplay(Func getTexture, BloonTemplate template, string i } public override void ModifyDisplayNode(UnityDisplayNode node) { - var sprite = Sprite.Create(GenerateTexture.Invoke(), new Rect(0, 0, Width, Height), new Vector2(0.5f, 0.5f), PixelsPerUnit); + if (!Cache.TryGetValue(Guid, out var cachedTexture) || cachedTexture == null) + { + var texture = GenerateTexture?.Invoke(); + Cache.Add(Guid, texture); + cachedTexture = texture; + } + var sprite = Sprite.Create(cachedTexture, new Rect(0, 0, Width, Height), new Vector2(0.5f, 0.5f), PixelsPerUnit); node.GetRenderer().sprite = sprite; node.isSprite = true; } diff --git a/Patches/BloonMenu_CreateBloonButtons_Patch.cs b/Patches/BloonMenu_CreateBloonButtons_Patch.cs index 3bd9407..507987f 100644 --- a/Patches/BloonMenu_CreateBloonButtons_Patch.cs +++ b/Patches/BloonMenu_CreateBloonButtons_Patch.cs @@ -19,7 +19,7 @@ public static void Prefix(BloonMenu __instance, Il2CppSystem.Collections.Generic { foreach (var bloon in CustomBloon.Bloons) { - if (!sortedBloons.Any(a => a.id == bloon.BloonTemplate.TemplateId)) + if (!sortedBloons.Any(a => a.id == bloon.BloonTemplate.TemplateId) && !bloon.BloonTemplate.IsQueueForDeletion) { sortedBloons.Add(Game.instance.model.GetBloon(bloon.BloonTemplate.TemplateId)); } diff --git a/SerializationHandler.cs b/SerializationHandler.cs index 17217b2..17d5512 100644 --- a/SerializationHandler.cs +++ b/SerializationHandler.cs @@ -29,6 +29,13 @@ internal static void EnsureFolderExists() if (!Directory.Exists(FolderDirectory)) Directory.CreateDirectory(FolderDirectory); } + internal static void SaveTemplate(BloonTemplate template, string path) + { + EnsureFolderExists(); + + var content = JsonConvert.SerializeObject(template, Settings); + File.WriteAllText(path + FileExtention, content); + } internal static void SaveTemplate(BloonTemplate template) { EnsureFolderExists(); @@ -41,6 +48,21 @@ internal static bool ContainGuid(Guid guid) { return Templates.Any(a => a.Guid == guid); } + internal static bool TryLoadTemplate(BloonTemplate template) + { + EnsureFolderExists(); + + if (ContainGuid(template.Guid)) + { + MelonLogger.Msg("File already exists"); + return false; + } + + template.IsLoaded = false; + SaveTemplate(template); + Templates.Add(template); + return true; + } internal static BloonTemplate GetTemplateFromPath(string path) { EnsureFolderExists(); diff --git a/ServerHandler.cs b/ServerHandler.cs new file mode 100644 index 0000000..a60e254 --- /dev/null +++ b/ServerHandler.cs @@ -0,0 +1,129 @@ + +using BTD_Mod_Helper.Extensions; +using Il2CppAssets.Scripts.Unity; +using Il2CppAssets.Scripts.Unity.UI_New.InGame; +using JetBrains.Annotations; +using MelonLoader; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Octokit; +using Octokit.Internal; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using static BloonFactory.UI.BloonBrowserMenuPanel; + +namespace BloonFactory +{ + internal static class ServerHandler + { + public const string URL = "https://server.bloonfactory.org/"; + private static HttpClient client = new HttpClient(); + internal static async Task RequestPageUpdate() + { + HttpResponseMessage response = await client.GetAsync(URL + "getPage"); + response.EnsureSuccessStatusCode(); + byte[] bytes = await response.Content.ReadAsByteArrayAsync(); + + var obj = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bytes), new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.None }); + return obj; + } + internal static async Task DownloadTemplate(Guid guid) + { + HttpResponseMessage response = await client.GetAsync(URL + $"getTemplate={guid.ToString()}"); + MelonLogger.Msg($"Requested template from server ({guid.ToString()})"); + response.EnsureSuccessStatusCode(); + byte[] bytes = await response.Content.ReadAsByteArrayAsync(); + + var obj = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bytes), SerializationHandler.Settings); + return obj; + } + internal static async Task<(bool success, string errorCode)> UploadTemplate(BloonTemplate template, BloonCategory category, string description) + { + string creator = Game.Player.LiNKAccount?.DisplayName; + if (string.IsNullOrEmpty(creator)) + { + MelonLogger.Msg("You must be logged into a NK account to upload a template."); + return (false, "You must be logged into a NK account to upload a template."); + } + MelonLogger.Msg($"Uploading template to server ({template.Name})"); + UploadTemplateRequest request = new UploadTemplateRequest() + { + Name = template.Name, + Guid = template.Guid, + Creator = creator, + Category = (byte)category, + Description = description, + TemplateJson = JsonConvert.SerializeObject(template, SerializationHandler.Settings) + }; + + HttpResponseMessage response = await client.PostAsync(URL + "uploadTemplate", new StringContent(JsonConvert.SerializeObject(request))); + + if (!response.IsSuccessStatusCode) + { + byte[] bytes = await response.Content.ReadAsByteArrayAsync(); + return (false, Encoding.UTF8.GetString(bytes)); + } + return (true, ""); + } + } + public class PageUpdateRequest + { + public List Data; + } + public class UploadTemplateRequest + { + public string Name; + public Guid Guid; + public string Creator; + public byte Category; + public string Description; + + public string TemplateJson; + } + public class BloonBrowserEntry + { + public string Name; + public Guid Guid; + public string Creator; + public byte Category; + public string Description; + + public DateTime UploadTime = DateTime.Now; + public int Downloads = 0; + + [JsonIgnore] + public BloonCategory CategoryEnum => (BloonCategory)Category; + } + public enum BloonCategory : byte + { + Boss, + VanillaPlus, + Modded + } + public static class CategoryExtensions + { + public static string[] BloonCategoryNames = + [ + "Boss", + "Vanilla+", + "Modded" + ]; + public static string ToFriendlyString(this BloonCategory category) + { + return category switch + { + BloonCategory.Boss => "Boss", + BloonCategory.VanillaPlus => "Vanilla+", + BloonCategory.Modded => "Modded", + _ => "Unknown", + }; + } + } +} diff --git a/UI/BloonBrowserMenuPanel.cs b/UI/BloonBrowserMenuPanel.cs new file mode 100644 index 0000000..1b748ce --- /dev/null +++ b/UI/BloonBrowserMenuPanel.cs @@ -0,0 +1,168 @@ +using BTD_Mod_Helper.Api; +using BTD_Mod_Helper.Api.Components; +using BTD_Mod_Helper.Api.Enums; +using BTD_Mod_Helper.Extensions; +using Il2Cpp; +using Il2CppAssets.Scripts.GameEditor.UI.PopupPanels; +using Il2CppAssets.Scripts.Unity.Menu; +using Il2CppAssets.Scripts.Unity.UI_New.Popups; +using Il2CppNinjaKiwi.Common.ResourceUtils; +using Il2CppTMPro; +using MelonLoader; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Device; +using UnityEngine.UI; +using UnityEngine.UIElements; +using static UnityEngine.RectTransform; + +namespace BloonFactory.UI +{ + [RegisterTypeInIl2Cpp(false)] + internal class BloonBrowserMenuPanel : ModHelperPanel + { + public static SpriteReference DownloadsIcon => ModContent.GetSpriteReference("DownloadsIcon"); + + ModHelperButton downloadButton => GetDescendent("DownloadButton"); + ModHelperImage downloadedImage => GetDescendent("DownloadedImage"); + + ModHelperPanel infoPanel => GetDescendent("InfoPanel"); + + ModHelperButton infoButton => GetDescendent("InfoButton"); + + ModHelperText nameText => GetDescendent("Name"); + ModHelperText categoryText => GetDescendent("Category"); + ModHelperText descriptionText => GetDescendent("Description"); + ModHelperText downloadCountText => GetDescendent("DownloadLabel"); + + bool downloading = false; + public void SetEntry(BloonBrowserEntry entry) + { + if (entry is null) + { + gameObject.SetActive(false); + return; + } + + nameText.SetText($"{entry.Name} by {entry.Creator}"); + descriptionText.SetText(entry.Description ?? ""); + categoryText.SetText(entry.CategoryEnum.ToFriendlyString()); + + downloadButton.Button.onClick.RemoveAllListeners(); + downloadButton.Button.onClick.AddListener(new Action(() => + { + Download(entry.Guid); + MenuManager.instance.buttonClick3Sound.Play("ClickSounds"); + })); + + infoButton.Button.onClick.RemoveAllListeners(); + infoButton.Button.onClick.AddListener(new Action(() => + { + SetDescriptionOpen(!infoPanel.isActiveAndEnabled); + MenuManager.instance.buttonClick2Sound.Play("ClickSounds"); + })); + + downloadCountText.SetText(GetDownloadString(entry.Downloads)); + + SetDescriptionOpen(false); + SetDownloaded(SerializationHandler.ContainGuid(entry.Guid)); + + gameObject.SetActive(true); + } + public BloonBrowserMenuPanel(IntPtr ptr) : base(ptr) + { + } + + public static BloonBrowserMenuPanel CreateTemplate() + { + var panel = Create(new Info("Panel", 3600, 450)); + panel.FitContent(UnityEngine.UI.ContentSizeFitter.FitMode.Unconstrained, UnityEngine.UI.ContentSizeFitter.FitMode.PreferredSize); + var layout = panel.AddComponent(); + layout.childControlHeight = false; + layout.childControlWidth = false; + layout.spacing = 50; + + var mainPanel = panel.AddPanel(new Info("SubPanel", 3350 / 2, -125, 3350, 200, new Vector2(0f, 1f)), VanillaSprites.MainBGPanelBlue); + + mainPanel.AddText(new Info("Name", 850, 0, 1500, 200, new Vector2(0, 0.5f)), $"Test Bloon by JohnDoe123", 80, TextAlignmentOptions.Left).EnableAutoSizing(80, 20); + + + mainPanel.AddButton(new Info("DownloadButton", 175, 100, 200, 200, new Vector2(1, 0f)), ModHelperSprites.DownloadBtn, new Action(() => + { + + })); + + mainPanel.AddImage(new Info("DownloadedImage", 175, 100, 200, 200, new Vector2(1, 0f)), VanillaSprites.TickGreenIcon); + + panel.AddPanel(new Info("InfoPanel", 0, 75, 3600, 150, new Vector2(0.5f, 0f)), VanillaSprites.BlueInsertPanel); + + var description = panel.infoPanel.AddText(new Info("Description", 0, 5, 3500, 100, new Vector2(0.5f, 0.5f)), "", 70, TextAlignmentOptions.Left); + description.EnableAutoSizing(70, 20); + description.Text.overflowMode = TextOverflowModes.Overflow; + + mainPanel.AddButton(new Info("InfoButton", -150, 0, 150, 150, new Vector2(1, 0.5f)), VanillaSprites.InfoBtn2, new Action(() => + { + panel.SetDescriptionOpen(!panel.infoPanel.isActiveAndEnabled); + MenuManager.instance.buttonClick2Sound.Play("ClickSounds"); + })); + + mainPanel.AddText(new Info("Category", -1000, 0, 500, 200, new Vector2(1, 0.5f)), "Boss", 100, TextAlignmentOptions.Right).EnableAutoSizing(80, 20); + + mainPanel.AddImage(new Info("DownloadIcon", -600, 0, 150, 150, new Vector2(1, 0.5f)), DownloadsIcon.guidRef); + mainPanel.AddText(new Info("DownloadLabel", -400, 0, 200, 150, new Vector2(1, 0.5f)), GetDownloadString(10199), 80, TextAlignmentOptions.Left).EnableAutoSizing(80); + + panel.SetDescriptionOpen(false); + + return panel; + } + public static string GetDownloadString(int count) + { + if (count >= 1000000) + return $"{(count / 1000000f).ToString("0.0")}M"; + if (count >= 1000) + return $"{(count / 1000f).ToString("0.0")}K"; + return count.ToString(); + } + public void SetDownloaded(bool downloaded) + { + downloadButton.gameObject.SetActive(!downloaded); + downloadedImage.gameObject.SetActive(downloaded); + } + public void SetDescriptionOpen(bool active) + { + infoPanel.SetActive(active); + LayoutRebuilder.ForceRebuildLayoutImmediate(this); + } + private async void Download(Guid guid) + { + if (downloading) + return; + downloading = true; + + BloonTemplate template = await ServerHandler.DownloadTemplate(guid); + if (template == null) + { + PopupScreen.instance.SafelyQueue(screen => + { + screen.ShowPopup(PopupScreen.Placement.menuCenter, "Download Failed", "The template could not be downloaded from the server. Please try again later.", null, "Ok", null, null, Popup.TransitionAnim.Scale); + }); + downloading = false; + return; + } + if (SerializationHandler.TryLoadTemplate(template)) + { + downloading = false; + SetDownloaded(true); + PopupScreen.instance.SafelyQueue(screen => + { + screen.ShowPopup(PopupScreen.Placement.menuCenter, "Download Success", "The template was successfully downloaded from the server", null, "Ok", null, null, Popup.TransitionAnim.Scale); + }); + } + } + + } +} diff --git a/UI/BloonBrowserUI.cs b/UI/BloonBrowserUI.cs new file mode 100644 index 0000000..1f9409e --- /dev/null +++ b/UI/BloonBrowserUI.cs @@ -0,0 +1,223 @@ +using BTD_Mod_Helper.Api; +using BTD_Mod_Helper.Api.Components; +using BTD_Mod_Helper.Api.Enums; +using BTD_Mod_Helper.Api.Legends; +using BTD_Mod_Helper.Extensions; +using Il2CppAssets.Scripts.Unity; +using Il2CppAssets.Scripts.Unity.Menu; +using Il2CppAssets.Scripts.Unity.UI_New.ChallengeEditor; +using Il2CppNinjaKiwi.Common; +using MelonLoader; +using Newtonsoft.Json; +using Octokit.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; +using static BloonFactory.UI.BloonBrowserMenuPanel; +using static Il2CppTMPro.TMP_InputField; +using TaskScheduler = BTD_Mod_Helper.Api.TaskScheduler; + +namespace BloonFactory.UI +{ + /// + /// Some of this code is adapted from BTD6 Mod Helper's Content Browser + /// Thanks doombubbles! + /// + internal class BloonBrowserUI : ModGameMenu + { + public bool generatedContentReady = false; + + public BloonBrowserMenuPanel[] bloonPanels; + + public List allEntries = new List(); + + public List filteredEntries = new List(); + + public const int bloonsPerPage = 20; + public int TotalPages => 1 + ((filteredEntries?.Count ?? 1) - 1) / bloonsPerPage; + public int currentPage = 0; + + ModHelperInputField searchField; + + SortingMethod SortingMethod = SortingMethod.Popular; + public override bool OnMenuOpened(Il2CppSystem.Object data) + { + bloonPanels = new BloonBrowserMenuPanel[bloonsPerPage]; + + BloonBrowserMenuPanel template = null; + + for (int i = 0; i < bloonPanels.Length; i++) + { + if (template != null) + bloonPanels[i] = template.Duplicate($"{i}"); + else + template = bloonPanels[i] = GameMenu.scrollRect.content.gameObject.AddModHelperComponent(BloonBrowserMenuPanel.CreateTemplate()); + bloonPanels[i].AddTo(GameMenu.scrollRect.content); + } + TaskScheduler.ScheduleTask(() => + { + foreach (var panel in bloonPanels) + { + panel.gameObject.SetActive(false); + } + }); + + + ModifyElements(); + AddElements(); + + RefreshPage(); + return false; + } + public override void OnMenuUpdate() + { + if (generatedContentReady) + { + generatedContentReady = false; + SetPage(0); + + GameMenu.searchingImg.gameObject.SetActive(false); + GameMenu.refreshBtn.interactable = true; + GenerateContentForPage(); + } + } + public void ModifyElements() + { + GameMenu.GetComponentFromChildrenByName("TopBar").gameObject.active = false; + GameMenu.GetComponentFromChildrenByName("Tabs").gameObject.active = false; + + var verticalLayoutGroup = GameMenu.scrollRect.content.GetComponent(); + verticalLayoutGroup.SetPadding(50); + verticalLayoutGroup.spacing = 50; + verticalLayoutGroup.childControlWidth = false; + verticalLayoutGroup.childControlHeight = false; + GameMenu.scrollRect.rectTransform.sizeDelta += new Vector2(0, 200); + GameMenu.scrollRect.rectTransform.localPosition += new Vector3(0, 100, 0); + + GameMenu.refreshBtn.SetOnClick(() => { + RefreshPage(); + MenuManager.instance.buttonClick3Sound.Play("ClickSounds"); + }); + GameMenu.firstPageBtn.SetOnClick(() => { + SetPage(0); + MenuManager.instance.buttonClick2Sound.Play("ClickSounds"); + }); + GameMenu.previousPageBtn.SetOnClick(() =>{ + SetPage(currentPage - 1); + MenuManager.instance.buttonClick2Sound.Play("ClickSounds"); + }); + GameMenu.nextPageBtn.SetOnClick(() => { + SetPage(currentPage + 1); + MenuManager.instance.buttonClick2Sound.Play("ClickSounds"); + }); + GameMenu.lastPageBtn.SetOnClick(() => { + SetPage(TotalPages - 1); + MenuManager.instance.buttonClick2Sound.Play("ClickSounds"); + }); + } + public void AddElements() + { + var container = GameMenu.GetComponentFromChildrenByName("Container").gameObject.AddModHelperPanel(new Info("SearchBar", 0, -475, new Vector2(0.5f, 1))); + + container.AddDropdown(new Info("Ordering", 1400, -425, 900, 150), Enum.GetNames(typeof(SortingMethod)).ToIl2CppList(), 450, new Action((val) => + { + SortingMethod = (SortingMethod)val; + GenerateContentForPage(); + }), VanillaSprites.BlueInsertPanelRound, 70); + + searchField = container.AddInputField(new Info("Search", 0, -425, 1500, 150), "", VanillaSprites.BlueInsertPanelRound, new Action((string val) => + { + GenerateContentForPage(); + }), 60, CharacterValidation.None, Il2CppTMPro.TextAlignmentOptions.Left, "Search...", 50); + searchField.InputField.textComponent.enableAutoSizing = true; + } + public void RefreshPage() + { + GameMenu.searchingImg.gameObject.SetActive(true); + GameMenu.refreshBtn.interactable = false; + Task.Run(async () => + { + PageUpdateRequest request = await ServerHandler.RequestPageUpdate(); + allEntries = request.Data; + generatedContentReady = true; + }); + } + public void GenerateContentForPage() + { + FilterEntries(); + + for (int i = currentPage * bloonsPerPage; i < (currentPage + 1) * bloonsPerPage; i++) + { + int panelIndex = i - (currentPage * bloonsPerPage); + + if (i >= filteredEntries.Count) + { + bloonPanels[panelIndex].SetEntry(null); + continue; + } + + var entry = filteredEntries[i]; + + bloonPanels[panelIndex].SetEntry(entry); + } + LayoutRebuilder.MarkLayoutRootForRebuild(GameMenu.scrollRect.content); + UpdateBottomBar(); + } + public void FilterEntries() + { + string search = searchField.InputField.text?.ToLower() ?? ""; + + filteredEntries = allEntries.ToList(); + + if (!string.IsNullOrEmpty(search)) + { + filteredEntries = filteredEntries.Where(entry => + entry.Name.ToLower().Contains(search) || + entry.Creator.ToLower().Contains(search) + ).ToList(); + } + + filteredEntries = (SortingMethod switch + { + SortingMethod.Popular => filteredEntries = filteredEntries.OrderByDescending(entry => entry.Downloads).ToList(), + SortingMethod.New => filteredEntries = filteredEntries.OrderByDescending(entry => entry.UploadTime).ToList(), + SortingMethod.Old => filteredEntries = filteredEntries.OrderBy(entry => entry.UploadTime).ToList(), + }); + } + public void UpdateBottomBar() + { + GameMenu.firstPageBtn.interactable = TotalPages >= 2 && currentPage > 0; + GameMenu.previousPageBtn.interactable = TotalPages >= 2 && currentPage > 0; + + GameMenu.nextPageBtn.interactable = TotalPages >= 2 && currentPage < TotalPages - 1; + GameMenu.lastPageBtn.interactable = TotalPages >= 2 && currentPage < TotalPages - 1; + + GameMenu.totalPages = TotalPages; + GameMenu.SetCurrentPage(currentPage + 1); + } + public void SetPage(int page) + { + if (currentPage != page) + { + GameMenu.scrollRect.verticalNormalizedPosition = 1f; + } + currentPage = page; + if (page < 0) + page = 0; + if (currentPage > TotalPages - 1) + currentPage = TotalPages - 1; + + GenerateContentForPage(); + } + } + public enum SortingMethod + { + Popular, + New, + Old + } +} diff --git a/UI/BloonEditorButton.cs b/UI/BloonEditorButton.cs index 1b896a1..1db8662 100644 --- a/UI/BloonEditorButton.cs +++ b/UI/BloonEditorButton.cs @@ -33,7 +33,7 @@ public static void Create(MainMenu menu) matchLocalPosition.transformToCopy = trophyStore.transform; matchLocalPosition.offset = new Vector3(0, -325); - var text = ModHelperText.Create(new Info("BloonEditor", 100, 150), "Bloon\nEditor"); + var text = ModHelperText.Create(new Info("BloonEditor", 125, 175), "Bloon\nFactory"); text.transform.GetComponent().enableAutoSizing = true; text.transform.SetParent(modsButton.GetComponentInChildren