diff --git a/NeoQOLPack/FileDownloader.cs b/NeoQOLPack/FileDownloader.cs new file mode 100644 index 0000000..ee805bd --- /dev/null +++ b/NeoQOLPack/FileDownloader.cs @@ -0,0 +1,30 @@ +// namespace NeoQOLPack; +// +// public class FileDownloader(Mod mod) +// { +// private static readonly HttpClient HttpClient = new(); +// +// public async Task DownloadFileAsync(string url, string outputPath) +// { +// try +// { +// Directory.CreateDirectory(outputPath); +// string fileName = Path.GetFileName(url); +// string destinationPath = Path.Combine(outputPath, fileName); +// +// using var response = await HttpClient.GetAsync(url); +// response.EnsureSuccessStatusCode(); +// byte[] fileBytes = await response.Content.ReadAsByteArrayAsync(); +// await File.WriteAllBytesAsync(destinationPath, fileBytes); +// mod.Logger.Information($"file {fileName} downloaded to {outputPath}"); +// } +// catch (HttpRequestException e) +// { +// mod.Logger.Error(e.Message); +// } +// catch (Exception e) +// { +// mod.Logger.Error(e.Message); +// } +// } +// } \ No newline at end of file diff --git a/NeoQOLPack/Mod.cs b/NeoQOLPack/Mod.cs index 9792428..34fec7a 100644 --- a/NeoQOLPack/Mod.cs +++ b/NeoQOLPack/Mod.cs @@ -1,19 +1,32 @@ -using GDWeave; +using System.Net; +using System.Text.Json; +using System.Text.RegularExpressions; +using GDWeave; using GDWeave.Godot; using GDWeave.Godot.Variants; using NeoQOLPack.Mods; +using Newtonsoft.Json.Linq; using Serilog; +// using System.Runtime.InteropServices; namespace NeoQOLPack; -public class Mod : IMod { +public class Mod : IMod +{ + public IModInterface modInterface; public Config Config; public ILogger Logger; + private static readonly string versionTag = "Beta2"; + private static readonly string repo = "neomoth/NeoQOLPack"; + + private bool injectUpdateNotice = false; + public Mod(IModInterface modInterface) { + this.modInterface = modInterface; this.Config = modInterface.ReadConfig(); Logger = modInterface.Logger; - modInterface.Logger.Information("Hello, world!"); + _ = GetVersion(); modInterface.RegisterScriptMod(new InventoryStackerItem(this)); modInterface.RegisterScriptMod(new InventoryStackerPlayerData(this)); modInterface.RegisterScriptMod(new InventoryStackerInventory(this)); @@ -21,6 +34,118 @@ public Mod(IModInterface modInterface) { modInterface.RegisterScriptMod(new CosmeticLoaderGlobals(this)); modInterface.RegisterScriptMod(new CosmeticLoaderPlayer(this)); modInterface.RegisterScriptMod(new CosmeticLoaderTitle()); + // modInterface.RegisterScriptMod(new ModScriptPatcher(this, versionTag, injectUpdateNotice)); + modInterface.RegisterScriptMod(new ShopPatcher()); + modInterface.RegisterScriptMod(new PlayerHudPatcher(this)); + modInterface.RegisterScriptMod(new ShopButtonPatcher(this)); + modInterface.RegisterScriptMod(new MenuPatcher(this, versionTag)); + if (injectUpdateNotice) ; + } + + // [DllImport("user32.dll", SetLastError = true)] + // private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint uType); + // + // private const uint MB_OK = 0x00000000; + // private const uint MB_OKCANCEL = 0x00000001; + // private const uint MB_YESNO = 0x00000004; + // private const uint MB_ICONINFORMATION = 0x00000040; + // private const uint MB_ICONWARNING = 0x00000030; + // private const uint MB_ICONERROR = 0x00000010; + // private const uint MB_ICONQUESTION = 0x00000020; + // private const uint MB_SYSTEMMODAL = 0x00001000; // Make it a system modal dialog + // private const uint MB_SETFOREGROUND = 0x00010000; // Bring the message box to the foreground + // + // private static int ShowMessage(string message, string title, uint buttonType, uint iconType) + // { + // uint options = buttonType | iconType | MB_SYSTEMMODAL | MB_SETFOREGROUND; + // return MessageBox(IntPtr.Zero, message, title, options); + // } + + private async Task GetVersion() + { + modInterface.Logger.Information("Neo's QOL Pack loaded!! :3"); + try + { + // modInterface.Logger.Information($"This should run before"); + JsonDocument? githubInfoRaw = await GetLatestRelease(modInterface.Logger); + JsonElement? githubInfo = githubInfoRaw?.RootElement; + var latestVersion = githubInfo?.GetProperty("tag_name").GetString(); + // modInterface.Logger.Information($"This should run after"); + modInterface.Logger.Information($"Latest version: {latestVersion}"); + if ((bool)latestVersion?.StartsWith("Beta")) + { + var newVer = Int32.Parse(Regex.Match(latestVersion, @"\d+").Value); + var currVer = Int32.Parse(Regex.Match(versionTag, @"\d+").Value); + + injectUpdateNotice = newVer > currVer; + // int result = ShowMessage("The version of the mod you are using is out of date.\nWould you like to download the latest release?","Mod out of date!", MB_YESNO, MB_ICONWARNING); + // if (result == 0) + // { + // FileDownloader downloader = new FileDownloader(this); + // var url = githubInfo?.GetProperty("assets").GetProperty("0").GetProperty("browser_download_url").GetString(); + // bool isWine = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WINEPREFIX")); + // string path = isWine ? "/home/neomoth/Downloads/gwagwa" : "C:\\users\\steamuser\\Downloads\\gwagwa"; + // if (url is null) + // { + // modInterface.Logger.Warning("Failed to find release zip"); + // return; + // } + // await downloader.DownloadFileAsync(url, path); + // var a = ShowMessage("New version downloaded. Please relaunch the game.","Success", MB_OK, MB_ICONINFORMATION); + // } + } + } + catch (Exception e) + { + modInterface.Logger.Error(e.Message); + } + } + + private static async Task GetLatestRelease(ILogger logger) + { + logger.Information("Getting latest release..."); + string apiUrl = $"https://api.github.com/repos/neomoth/NeoQOLPatchMod/releases/latest"; + + // logger.Debug("Testing dns..."); + await Dns.GetHostEntryAsync("api.github.com"); + // logger.Debug("creating HTTP Client..."); + try + { + using HttpClient client = new HttpClient(); + client.Timeout = TimeSpan.FromSeconds(10); + + // logger.Debug("adding headers..."); + client.DefaultRequestHeaders.Add("User-Agent", "C# App"); + // logger.Debug("awaiting response..."); + HttpResponseMessage response = await client.GetAsync(apiUrl).ConfigureAwait(false); + // logger.Debug("got response."); + response.EnsureSuccessStatusCode(); + + if (response.Headers.Contains("X-RateLimit-Remaining")) + { + string? remainingRequests = response.Headers.GetValues("X-RateLimit-Remaining").FirstOrDefault(); + // logger.Debug($"GitHub API Rate Limit Remaining: {remainingRequests}"); + } + + if (!response.IsSuccessStatusCode){ + logger.Error("Unable to connect to github."); + return null; // Unable to connect to github for some reason + } + // logger.Debug("valid. getting response data..."); + string json = await response.Content.ReadAsStringAsync(); + + // logger.Debug("parsing json..."); + return JsonDocument.Parse(json); + // logger.Debug("retrieved info."); + } + catch (HttpRequestException e) + { + throw new Exception($"Http error: {e.Message}"); + } + catch (TaskCanceledException e) + { + throw new Exception($"Task cancelled: {e.Message}"); + } } public IEnumerable GetMod() diff --git a/NeoQOLPack/Mods/CosmeticLoaderGlobals.cs b/NeoQOLPack/Mods/CosmeticLoaderGlobals.cs index 9661204..1070451 100644 --- a/NeoQOLPack/Mods/CosmeticLoaderGlobals.cs +++ b/NeoQOLPack/Mods/CosmeticLoaderGlobals.cs @@ -10,7 +10,7 @@ public class CosmeticLoaderGlobals(Mod mod) : IScriptMod public IEnumerable Modify(string path, IEnumerable tokens) { - mod.Logger.Information("loaded globals.gdc"); + // mod.Logger.Information("loaded globals.gdc"); MultiTokenWaiter extendsWaiter = new MultiTokenWaiter([ t=>t.Type == TokenType.PrExtends, t => t.Type == TokenType.Newline, @@ -50,7 +50,7 @@ public IEnumerable Modify(string path, IEnumerable tokens) } else if (readyWaiter.Check(token)) { - mod.Logger.Information("found ready func"); + // mod.Logger.Information("found ready func"); yield return token; yield return new Token(TokenType.Newline, 1); diff --git a/NeoQOLPack/Mods/CosmeticLoaderPlayer.cs b/NeoQOLPack/Mods/CosmeticLoaderPlayer.cs index 7b05ed8..a5cfa32 100644 --- a/NeoQOLPack/Mods/CosmeticLoaderPlayer.cs +++ b/NeoQOLPack/Mods/CosmeticLoaderPlayer.cs @@ -14,7 +14,13 @@ public IEnumerable Modify(string path, IEnumerable tokens) t => t is IdentifierToken {Name: "_ready"}, t => t.Type == TokenType.Newline ],allowPartialMatch: true); + + + //"/iamweest": PlayerData._unlock_cosmetic("title_streamerman") + // "/colonthreetimeseight": PlayerData._unlock_cosmetic("title_colonthreetimeseight") + // "/hithisisaveryhardstringtotrytoguesslol": PlayerData._unlock_cosmetic("title_seventvowner") + foreach (Token token in tokens) { if (readyWaiter.Check(token)) @@ -27,10 +33,11 @@ public IEnumerable Modify(string path, IEnumerable tokens) yield return new Token(TokenType.ParenthesisOpen); yield return new IdentifierToken("title"); yield return new Token(TokenType.ParenthesisClose); - + yield return new Token(TokenType.Newline, 1); - - } else yield return token; + + } + else yield return token; } } } \ No newline at end of file diff --git a/NeoQOLPack/Mods/InventoryStackerInventory.cs b/NeoQOLPack/Mods/InventoryStackerInventory.cs index 3de5f91..10f2822 100644 --- a/NeoQOLPack/Mods/InventoryStackerInventory.cs +++ b/NeoQOLPack/Mods/InventoryStackerInventory.cs @@ -50,28 +50,22 @@ public IEnumerable Modify(string path, IEnumerable tokens) { yield return token; - var t1 = new Token(TokenType.Period); - var t2 = new IdentifierToken("_stack_items"); - var t3 = new Token(TokenType.ParenthesisOpen); - var t4 = new Token(TokenType.ParenthesisClose); - var t5 = new Token(TokenType.Newline, 1); + // mod.Logger.Information($"tokens: {t1}{t2}{t3}{t4}{t5} at {token}"); - mod.Logger.Information($"tokens: {t1}{t2}{t3}{t4}{t5} at {token}"); - - mod.Logger.Information("#################### FOUND REFRESH FUNC ######################"); // C - mod.Logger.Information("WAWAWAWAWAWAWWA"); + // mod.Logger.Information("#################### FOUND REFRESH FUNC ######################"); // C + // mod.Logger.Information("WAWAWAWAWAWAWWA"); yield return new Token(TokenType.Dollar); yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); - yield return t1; - yield return t2; - yield return t3; - yield return t4; - yield return t5; + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_stack_items"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 1); } else if (skipperWaiter.Check(token)) { - mod.Logger.Information("#################### FOUND SKIP FUNC ######################"); // C + // mod.Logger.Information("#################### FOUND SKIP FUNC ######################"); // C yield return token; yield return new Token(TokenType.CfIf); diff --git a/NeoQOLPack/Mods/InventoryStackerItem.cs b/NeoQOLPack/Mods/InventoryStackerItem.cs index 74db87c..3bc4eb8 100644 --- a/NeoQOLPack/Mods/InventoryStackerItem.cs +++ b/NeoQOLPack/Mods/InventoryStackerItem.cs @@ -27,7 +27,7 @@ public IEnumerable Modify(string path, IEnumerable tokens) { if (varWaiter.Check(token)) { - mod.Logger.Information("#################### FOUND VAR SPOT ######################"); // C + // mod.Logger.Information("#################### FOUND VAR SPOT ######################"); // C yield return token; yield return new Token(TokenType.PrVar); @@ -51,7 +51,7 @@ public IEnumerable Modify(string path, IEnumerable tokens) } else if (updateWaiter.Check(token)) { - mod.Logger.Information("#################### FOUND UPDATE FUNC ######################"); // C + // mod.Logger.Information("#################### FOUND UPDATE FUNC ######################"); // C yield return token; yield return new Token(TokenType.Newline, 1); diff --git a/NeoQOLPack/Mods/InventoryStackerPlayerData.cs b/NeoQOLPack/Mods/InventoryStackerPlayerData.cs index 795734f..ae03c2e 100644 --- a/NeoQOLPack/Mods/InventoryStackerPlayerData.cs +++ b/NeoQOLPack/Mods/InventoryStackerPlayerData.cs @@ -30,54 +30,299 @@ public IEnumerable Modify(string path, IEnumerable tokens) { if (entryWaiter.Check(token)) { - mod.Logger.Information("#################### FOUND ENTRY FUNC ######################"); + // mod.Logger.Information("#################### FOUND ENTRY FUNC ######################"); yield return token; //if $"/root/MyModId": yield return new Token(TokenType.Newline, 1); - yield return new Token(TokenType.Dollar); - yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); - yield return new Token(TokenType.Period); - yield return new IdentifierToken("_append_entry"); - yield return new Token(TokenType.ParenthesisOpen); + // yield return new Token(TokenType.Dollar); + // yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + // yield return new Token(TokenType.Period); + // yield return new IdentifierToken("_append_entry"); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new IdentifierToken("entry"); + // yield return new Token(TokenType.ParenthesisClose); + + // entry["locked"] = false + // entry["stack_size"] = 0 + // entry["stacked"] = false + yield return new IdentifierToken("entry"); - yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("locked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("entry"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stack_size")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new IntVariant(0)); + yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("entry"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stacked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline, 1); } else if (readyWaiter.Check(token)) { - mod.Logger.Information("#################### FOUND READY FUNC ######################"); + // mod.Logger.Information("#################### FOUND READY FUNC ######################"); yield return token; yield return new Token(TokenType.Newline, 1); - yield return new Token(TokenType.Dollar); - yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); - yield return new Token(TokenType.Period); - yield return new IdentifierToken("_append_entry"); - yield return new Token(TokenType.ParenthesisOpen); + // yield return new Token(TokenType.Dollar); + // yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + // yield return new Token(TokenType.Period); + // yield return new IdentifierToken("_append_entry"); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new IdentifierToken("FALLBACK_ITEM"); + // yield return new Token(TokenType.ParenthesisClose); + yield return new IdentifierToken("FALLBACK_ITEM"); - yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("locked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("FALLBACK_ITEM"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stack_size")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new IntVariant(0)); + yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("FALLBACK_ITEM"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stacked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + + yield return new Token(TokenType.Newline, 1); + } else if (loadedWaiter.Check(token)) { - mod.Logger.Information("#################### FOUND LOAD FUNC ######################"); + // mod.Logger.Information("#################### FOUND LOAD FUNC ######################"); yield return token; - yield return new Token(TokenType.Newline, 1); - yield return new Token(TokenType.Dollar); - yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + // for item in PlayerData.inventory: + // if !item.has("locked"): item["locked"] = false + // if !item.has("stack_size"): item["stack_size"] = 0 # note: stack size is zero indexed, 0 = 1, 1 = 2, etc + // if !item.has("stacked"): item["stacked"] = false # tracks if item is considered stacked with another item + + yield return new Token(TokenType.CfFor); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.OpIn); + yield return new IdentifierToken("inventory"); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 2); + yield return new Token(TokenType.CfIf); + yield return new Token(TokenType.OpNot); + yield return new IdentifierToken("item"); yield return new Token(TokenType.Period); - yield return new IdentifierToken("_initialize_keys"); + yield return new IdentifierToken("has"); yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("locked")); yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("locked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline, 2); + yield return new Token(TokenType.CfIf); + yield return new Token(TokenType.OpNot); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("has"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("stack_size")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stack_size")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new IntVariant(0)); + yield return new Token(TokenType.Newline, 2); + yield return new Token(TokenType.CfIf); + yield return new Token(TokenType.OpNot); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("has"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("stacked")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stacked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); yield return new Token(TokenType.Newline, 1); - yield return new Token(TokenType.Dollar); - yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + + + // yield return new Token(TokenType.Newline, 1); + // yield return new Token(TokenType.Dollar); + // yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + // yield return new Token(TokenType.Period); + // yield return new IdentifierToken("_initialize_keys"); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new Token(TokenType.ParenthesisClose); + // yield return new Token(TokenType.Newline, 1); + // yield return new Token(TokenType.Dollar); + // yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + // yield return new Token(TokenType.Period); + // yield return new IdentifierToken("_stack_items"); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new Token(TokenType.ParenthesisClose); + + + + + yield return new Token(TokenType.PrVar); + yield return new IdentifierToken("tools_to_stack"); + yield return new Token(TokenType.OpAssign); + yield return new Token(TokenType.BracketOpen); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.Newline, 1); + yield return new Token(TokenType.PrVar); + yield return new IdentifierToken("items_marked_for_stack"); + yield return new Token(TokenType.OpAssign); + yield return new Token(TokenType.BracketOpen); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.Newline, 1); + + yield return new Token(TokenType.CfFor); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.OpIn); + yield return new IdentifierToken("inventory"); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 2); + yield return new Token(TokenType.PrVar); + yield return new IdentifierToken("file"); + yield return new Token(TokenType.OpAssign); + yield return new IdentifierToken("Globals"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("item_data"); + yield return new Token(TokenType.BracketOpen); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("id")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("file")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.Newline, 2); + yield return new Token(TokenType.CfIf); + yield return new IdentifierToken("file"); yield return new Token(TokenType.Period); - yield return new IdentifierToken("_stack_items"); + yield return new IdentifierToken("category"); + yield return new Token(TokenType.OpEqual); + yield return new ConstantToken(new StringVariant("tool")); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 3); + yield return new Token(TokenType.PrVar); + yield return new IdentifierToken("found_item"); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline, 3); + yield return new Token(TokenType.CfFor); + yield return new IdentifierToken("t_item"); + yield return new Token(TokenType.OpIn); + yield return new IdentifierToken("tools_to_stack"); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 4); + yield return new Token(TokenType.CfIf); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("id")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpEqual); + yield return new IdentifierToken("t_item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("id")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 5); + yield return new IdentifierToken("found_item"); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(true)); + yield return new Token(TokenType.Newline, 5); + yield return new IdentifierToken("t_item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stack_size")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssignAdd); + yield return new ConstantToken(new IntVariant(1)); + yield return new Token(TokenType.Newline, 5); + yield return new IdentifierToken("items_marked_for_stack"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("append"); yield return new Token(TokenType.ParenthesisOpen); + yield return new IdentifierToken("item"); yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 5); + yield return new Token(TokenType.CfBreak); + yield return new Token(TokenType.Newline, 3); + yield return new Token(TokenType.CfIf); + yield return new Token(TokenType.OpNot); + yield return new IdentifierToken("found_item"); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 4); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stack_size")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new IntVariant(0)); + yield return new Token(TokenType.Newline, 4); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.BracketOpen); + yield return new ConstantToken(new StringVariant("stacked")); + yield return new Token(TokenType.BracketClose); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline, 4); + yield return new IdentifierToken("tools_to_stack"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("append"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new IdentifierToken("item"); + yield return new Token(TokenType.ParenthesisClose); + // var tools_to_stack = [] + // var items_marked_for_stack = [] + + // # Required to ensure everyone's save is updated with the new dictionary keys + // for item in PlayerData.inventory: + // var file = Globals.item_data[item["id"]]["file"] + // if file.category == "tool": + // var found_item = false + // for t_item in tools_to_stack: + // if item["id"] == t_item["id"]: + // found_item = true + // t_item["stack_size"] += 1 + // items_marked_for_stack.append(item) + // break + // if not found_item: + // item["stack_size"] = 0 + // item["stacked"] = false + // tools_to_stack.append(item) + // + yield return new Token(TokenType.Newline, 1); } else yield return token; diff --git a/NeoQOLPack/Mods/InventoryStackerSelect.cs b/NeoQOLPack/Mods/InventoryStackerSelect.cs index 9d8bb3d..668877f 100644 --- a/NeoQOLPack/Mods/InventoryStackerSelect.cs +++ b/NeoQOLPack/Mods/InventoryStackerSelect.cs @@ -26,7 +26,7 @@ public IEnumerable Modify(string path, IEnumerable tokens) mod.Logger.Information("PENIS"); } - mod.Logger.Information("#################### FOUND ADD FUNC ######################"); + // mod.Logger.Information("#################### FOUND ADD FUNC ######################"); yield return token; yield return new IdentifierToken("i"); diff --git a/NeoQOLPack/Mods/MenuPatcher.cs b/NeoQOLPack/Mods/MenuPatcher.cs new file mode 100644 index 0000000..6e1e416 --- /dev/null +++ b/NeoQOLPack/Mods/MenuPatcher.cs @@ -0,0 +1,176 @@ +using GDWeave.Godot; +using GDWeave.Godot.Variants; +using GDWeave.Modding; + +namespace NeoQOLPack.Mods; + +public class MenuPatcher(Mod mod, string version) : IScriptMod +{ + public bool ShouldRun(string path) => path == "res://Scenes/Menus/Main Menu/main_menu.gdc"; + + public IEnumerable Modify(string path, IEnumerable tokens) + { + MultiTokenWaiter extendsWaiter = new MultiTokenWaiter([ + t=>t.Type is TokenType.PrExtends, + t=>t.Type is TokenType.Newline + ], allowPartialMatch: true); + + MultiTokenWaiter buttonWaiter = new MultiTokenWaiter([ + t => t is IdentifierToken {Name: "Panel"}, + t => t.Type is TokenType.OpDiv, + t => t is IdentifierToken {Name: "Panel"}, + t => t.Type is TokenType.OpDiv, + t => t is IdentifierToken {Name: "HBoxContainer"}, + t => t.Type is TokenType.OpDiv, + t => t is IdentifierToken {Name: "Button"}, + t => t.Type is TokenType.Period, + t => t is IdentifierToken {Name: "disabled"}, + t => t.Type is TokenType.OpAssign, + // t=>t is ConstantToken{Value:StringVariant{Value:"lobby_browser/Panel/Panel/HBoxContainer/Button"}}, + // t=> t.Type is TokenType.OpAssign, + // t=>t.Type is TokenType.Newline + ], allowPartialMatch:false); + + MultiTokenWaiter disabledWaiter = new MultiTokenWaiter([ + t => t is IdentifierToken {Name: "_process"}, + t => t is IdentifierToken {Name: "Panel"}, + t => t is IdentifierToken {Name: "Panel"}, + t => t is IdentifierToken {Name: "Panel"}, + // t => t.Type is TokenType.OpDiv, + // t => t is IdentifierToken {Name: "Panel"}, + // t => t.Type is TokenType.OpDiv, + // t => t is IdentifierToken {Name: "HBoxContainer"}, + // t => t.Type is TokenType.OpDiv, + // t => t is IdentifierToken {Name: "Button"}, + // t => t.Type is TokenType.Period, + // t => t is IdentifierToken {Name: "disabled"}, + // t => t.Type is TokenType.OpAssign, + // t => t.Type is TokenType.ParenthesisOpen, + t=>t is IdentifierToken {Name: "disabled"}, + t=>t.Type is TokenType.OpOr, + t=>t is IdentifierToken {Name: "refreshing"}, + // t=>t is IdentifierToken {Name: "_process"}, + // t=>t is IdentifierToken {Name: "hovered_button"}, + // t=>t.Type is TokenType.ParenthesisClose, + // t=>t.Type is TokenType.Newline + ], allowPartialMatch:true); + + MultiTokenWaiter readyWaiter = new MultiTokenWaiter([ + t=>t is IdentifierToken {Name: "_ready"}, + t=>t.Type is TokenType.Newline + ], allowPartialMatch: true); + + //$lobby_browser / Panel / Panel / HBoxContainer / Button.disabled = (disabled or refreshing) and !offline_selected + //"%serv_options".connect("item_selected", self, "on_select") + + foreach (Token token in tokens) + { + if (extendsWaiter.Check(token)) + { + yield return token; + yield return new Token(TokenType.PrVar); + yield return new IdentifierToken("offline_selected"); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline); + yield return new Token(TokenType.PrFunction); + yield return new IdentifierToken("on_select"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new IdentifierToken("index"); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Colon); + yield return new Token(TokenType.Newline, 1); + // yield return new Token(TokenType.BuiltInFunc, (uint?)BuiltinFunction.TextPrint); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new ConstantToken(new StringVariant("coooooooooooooooooooooooooock")); + // yield return new Token(TokenType.ParenthesisClose); + // yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("offline_selected"); + yield return new Token(TokenType.OpAssign); + yield return new ConstantToken(new BoolVariant(true)); + yield return new Token(TokenType.CfIf); + yield return new IdentifierToken("index"); + yield return new Token(TokenType.OpEqual); + yield return new ConstantToken(new IntVariant(2)); + yield return new Token(TokenType.CfElse); + yield return new ConstantToken(new BoolVariant(false)); + yield return new Token(TokenType.Newline, 1); + yield return new Token(TokenType.BuiltInFunc, (uint?)BuiltinFunction.TextPrint); + yield return new Token(TokenType.ParenthesisOpen); + yield return new IdentifierToken("offline_selected"); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 1); + yield return new Token(TokenType.Newline); + } + else if (readyWaiter.Check(token)) + { + yield return token; + + // yield return new Token(TokenType.BuiltInFunc, (uint?)BuiltinFunction.TextPrint); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new ConstantToken(new StringVariant("peeeeeeeeeeeeeeeeeeeeeeeeeeeeeenis")); + // yield return new Token(TokenType.ParenthesisClose); + // yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("get_node"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_append_version"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new Token(TokenType.Self); + yield return new Token(TokenType.Comma); + yield return new ConstantToken(new StringVariant(version)); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 1); + yield return new Token(TokenType.Dollar); + yield return new ConstantToken(new StringVariant("%serv_options")); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("connect"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("item_selected")); + yield return new Token(TokenType.Comma); + yield return new Token(TokenType.Self); + yield return new Token(TokenType.Comma); + yield return new ConstantToken(new StringVariant("on_select")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 1); + } + else if (buttonWaiter.Check(token)) + { + yield return token; + mod.Logger.Debug("ough"); + yield return new Token(TokenType.ParenthesisOpen); + } + else if (disabledWaiter.Check(token)) + { + mod.Logger.Information("mhm yep found thang"); + yield return token; + + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.OpAnd); + yield return new Token(TokenType.OpNot); + yield return new IdentifierToken("offline_selected"); + yield return new Token(TokenType.Newline, 1); + + // $lobby_browser / Panel / Panel / HBoxContainer / Button.disabled = (disabled or refreshing) #and !offline_selected + // yield return new IdentifierToken("get_node"); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new ConstantToken(new StringVariant("lobby_browser/Panel/Panel/HBoxContainer/Button")); + // yield return new Token(TokenType.ParenthesisClose); + // yield return new Token(TokenType.Period); + // yield return new IdentifierToken("disabled"); + // yield return new Token(TokenType.OpAssign); + // yield return new Token(TokenType.ParenthesisOpen); + // yield return new IdentifierToken("disabled"); + // yield return new Token(TokenType.OpOr); + // yield return new IdentifierToken("refreshing"); + // yield return new Token(TokenType.ParenthesisClose); + // yield return new Token(TokenType.OpAnd); + // yield return new Token(TokenType.OpNot); + // yield return new IdentifierToken("offline_selected"); + } + else yield return token; + } + } +} \ No newline at end of file diff --git a/NeoQOLPack/Mods/ModScriptPatcher.cs b/NeoQOLPack/Mods/ModScriptPatcher.cs new file mode 100644 index 0000000..7381fd3 --- /dev/null +++ b/NeoQOLPack/Mods/ModScriptPatcher.cs @@ -0,0 +1,31 @@ +using GDWeave.Godot; +using GDWeave.Godot.Variants; +using GDWeave.Modding; + +namespace NeoQOLPack.Mods; + +public class ModScriptPatcher(Mod mod, string version, bool shouldNotify) : IScriptMod +{ + public bool ShouldRun(string path) => path == "res://mods/NeoQOLPack/main.gdc"; + + public IEnumerable Modify(string path, IEnumerable tokens) + { + MultiTokenWaiter readyWaiter = new MultiTokenWaiter([ + t=>t is IdentifierToken {Name: "_ready"}, + t=>t.Type is TokenType.Newline + ], allowPartialMatch: true); + + foreach (Token token in tokens) + { + if (readyWaiter.Check(token)) + { + yield return token; + yield return new IdentifierToken("version"); + yield return new Token(TokenType.OpEqual); + yield return new ConstantToken(new StringVariant(version)); + yield return new Token(TokenType.Newline, 1); + } + else yield return token; + } + } +} \ No newline at end of file diff --git a/NeoQOLPack/Mods/PlayerHudPatcher.cs b/NeoQOLPack/Mods/PlayerHudPatcher.cs new file mode 100644 index 0000000..495f2db --- /dev/null +++ b/NeoQOLPack/Mods/PlayerHudPatcher.cs @@ -0,0 +1,58 @@ +using GDWeave.Godot; +using GDWeave.Godot.Variants; +using GDWeave.Modding; + +namespace NeoQOLPack.Mods; + +public class PlayerHudPatcher(Mod mod) : IScriptMod +{ + public bool ShouldRun(string path) => path == "res://Scenes/HUD/playerhud.gdc"; + + public IEnumerable Modify(string path, IEnumerable tokens) + { + MultiTokenWaiter chatWaiter = new MultiTokenWaiter([ + t => t.Type == TokenType.CfMatch, + t => t is IdentifierToken { Name: "line" }, + t=>t.Type is TokenType.Colon, + t => t.Type is TokenType.Newline + ], allowPartialMatch: false); + + foreach (Token token in tokens) + { + if (chatWaiter.Check(token)) + { + mod.Logger.Debug("HIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII"); + yield return token; + yield return new Token(TokenType.Newline, 4); + yield return new ConstantToken(new StringVariant("/iamweest")); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("PlayerData"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_unlock_cosmetic"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("title_streamerman")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 4); + yield return new ConstantToken(new StringVariant("/colonthreetimeseight")); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("PlayerData"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_unlock_cosmetic"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("title_colonthreetimeseight")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 4); + yield return new ConstantToken(new StringVariant("/hithisisaveryhardstringtotrytoguesslol")); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("PlayerData"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_unlock_cosmetic"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("title_seventvowner")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Newline, 4); + } + else yield return token; + } + } +} \ No newline at end of file diff --git a/NeoQOLPack/Mods/ShopButtonPatcher.cs b/NeoQOLPack/Mods/ShopButtonPatcher.cs new file mode 100644 index 0000000..f1dda7e --- /dev/null +++ b/NeoQOLPack/Mods/ShopButtonPatcher.cs @@ -0,0 +1,54 @@ +using GDWeave.Godot; +using GDWeave.Godot.Variants; +using GDWeave.Modding; + +namespace NeoQOLPack.Mods; + +public class ShopButtonPatcher(Mod mod) : IScriptMod +{ + public bool ShouldRun(string path) => path == "res://Scenes/HUD/Shop/ShopButtons/shop_button.gdc"; + + public IEnumerable Modify(string path, IEnumerable tokens) + { + //price_label.text = prefix + str(cost) + MultiTokenWaiter setWaiter = new MultiTokenWaiter([ + t => t is IdentifierToken { Name: "price_label"}, + t => t.Type is TokenType.Period, + t=> t is IdentifierToken {Name: "text"}, + t=>t.Type is TokenType.OpAssign, + t=> t is IdentifierToken {Name: "prefix"}, + t => t.Type is TokenType.OpAdd, + t => t is IdentifierToken { Name: "cost"}, + t => t.Type is TokenType.Newline + ], allowPartialMatch: true); + + foreach (Token token in tokens) + { + if (setWaiter.Check(token)) + { + mod.Logger.Debug("awawawawawa loaded found whatever kys"); + yield return token; + + //#price_label.text = get_node("/root/Main")._shorten_cost(cost) + + yield return new Token(TokenType.Newline, 1); + yield return new IdentifierToken("price_label"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("text"); + yield return new Token(TokenType.OpAssign); + yield return new IdentifierToken("get_node"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_shorten_cost"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new IdentifierToken("cost"); + yield return new Token(TokenType.ParenthesisClose); + + yield return new Token(TokenType.Newline, 1); + } + else yield return token; + } + } +} \ No newline at end of file diff --git a/NeoQOLPack/Mods/ShopPatcher.cs b/NeoQOLPack/Mods/ShopPatcher.cs new file mode 100644 index 0000000..509abcd --- /dev/null +++ b/NeoQOLPack/Mods/ShopPatcher.cs @@ -0,0 +1,52 @@ +using GDWeave.Godot; +using GDWeave.Godot.Variants; +using GDWeave.Modding; + +namespace NeoQOLPack.Mods; + +public class ShopPatcher : IScriptMod +{ + public bool ShouldRun(string path) => path == "res://Scenes/HUD/Shop/shop.gdc"; + + public IEnumerable Modify(string path, IEnumerable tokens) + { + MultiTokenWaiter replaceWaiter = new MultiTokenWaiter([ + t => t is IdentifierToken { Name: "replace" }, + t => t is ConstantToken { Value: StringVariant { Value: "_slot_used" } }, + t => t.Type is TokenType.Newline + ], allowPartialMatch: true); + + //if(node.name=="titles"): get_node("/root/Main")._append_shop_buttons(grid,self) + foreach (Token token in tokens) + { + if (replaceWaiter.Check(token)) + { + yield return token; + + yield return new Token(TokenType.Newline, 3); + + yield return new Token(TokenType.CfIf); + yield return new IdentifierToken("node"); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("name"); + yield return new Token(TokenType.OpEqual); + yield return new ConstantToken(new StringVariant("titles")); + yield return new Token(TokenType.Colon); + yield return new IdentifierToken("get_node"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new ConstantToken(new StringVariant("/root/NeoQOLPack")); + yield return new Token(TokenType.ParenthesisClose); + yield return new Token(TokenType.Period); + yield return new IdentifierToken("_append_shop_buttons"); + yield return new Token(TokenType.ParenthesisOpen); + yield return new IdentifierToken("grid"); + yield return new Token(TokenType.Comma); + yield return new Token(TokenType.Self); + yield return new Token(TokenType.ParenthesisClose); + + yield return new Token(TokenType.Newline, 2); + } + else yield return token; + } + } +} \ No newline at end of file diff --git a/NeoQOLPack/NeoQOLPack.csproj b/NeoQOLPack/NeoQOLPack.csproj index 2d55de0..048bd3f 100644 --- a/NeoQOLPack/NeoQOLPack.csproj +++ b/NeoQOLPack/NeoQOLPack.csproj @@ -15,6 +15,10 @@ + + + + = 10000: + var shortened = cost / 1000 + return str(shortened)+"K" + return str(cost) + +static func _append_shop_buttons(parent,ref): + var button = preload("res://Scenes/HUD/Shop/ShopButtons/shop_button.tscn").instance() + button.set_script(preload("res://Scenes/HUD/Shop/ShopButtons/button_cosmetic_unlock.gd")) + button.cosmetic_unlock = "title_ihavestupidamountsofmoney" + #button.cosmetic_unlock = item + button.cost = 999999 + #button.cost = Globals.cosmetic_data[item]["file"].cost + parent.add_child(button) + button.hud = ref.get_node(ref.hud) + button.connect("mouse_entered", ref, "_item_entered", [button]) + button.connect("_used", ref, "_slot_used") + static func _load_mod_resources(): print("Loading NEOQOL Resources...") var resource_count = 0