diff --git a/CropStacking/ModEntry.cs b/CropStacking/ModEntry.cs index a6fb117a..88d1a3e8 100644 --- a/CropStacking/ModEntry.cs +++ b/CropStacking/ModEntry.cs @@ -39,7 +39,7 @@ public override void Entry(IModHelper helper) SHelper = helper; helper.Events.GameLoop.GameLaunched += GameLoop_GameLaunched; - helper.Events.Input.ButtonPressed += Input_ButtonPressed; + //helper.Events.Input.ButtonPressed += Input_ButtonPressed; var harmony = new Harmony(ModManifest.UniqueID); harmony.PatchAll(); diff --git a/CropsSurviveSeasonChange/CropsSurviveSeasonChange.csproj b/CropsSurviveSeasonChange/CropsSurviveSeasonChange.csproj index ee30cc75..e11803bc 100644 --- a/CropsSurviveSeasonChange/CropsSurviveSeasonChange.csproj +++ b/CropsSurviveSeasonChange/CropsSurviveSeasonChange.csproj @@ -1,7 +1,7 @@  1.0.0 - net5.0 + net6.0 true AnyCPU;x64 diff --git a/CropsSurviveSeasonChange/Methods.cs b/CropsSurviveSeasonChange/Methods.cs index 82eb133d..433ca37b 100644 --- a/CropsSurviveSeasonChange/Methods.cs +++ b/CropsSurviveSeasonChange/Methods.cs @@ -6,7 +6,7 @@ public partial class ModEntry { private static bool CheckKill(bool outdoors, Crop crop, GameLocation environment) { - if (!Config.ModEnabled || crop.forageCrop.Value || crop.dead.Value || (!Config.IncludeRegrowables && crop.regrowAfterHarvest.Value != -1) || (environment.GetSeasonForLocation() == "winter" && !Config.IncludeWinter)) + if (!Config.ModEnabled || crop.forageCrop.Value || crop.dead.Value || (!Config.IncludeRegrowables && crop.GetData().RegrowDays != -1) || (environment.GetSeason() == Season.Winter && !Config.IncludeWinter)) { return outdoors; } diff --git a/CropsSurviveSeasonChange/manifest.json b/CropsSurviveSeasonChange/manifest.json index 251d7fe7..de4aa1bd 100644 --- a/CropsSurviveSeasonChange/manifest.json +++ b/CropsSurviveSeasonChange/manifest.json @@ -1,11 +1,11 @@ { "Name": "Crops Survive Season Change", "Author": "aedenthorn", - "Version": "0.1.2", + "Version": "0.2.0", "Description": "Crops Survive Season Change.", "UniqueID": "aedenthorn.CropsSurviveSeasonChange", "EntryDll": "CropsSurviveSeasonChange.dll", - "MinimumApiVersion": "3.15.0", + "MinimumApiVersion": "3.16.0", "ModUpdater": { "Repository": "StardewValleyMods", "User": "aedenthorn", diff --git a/GenieLamp/CodePatches.cs b/GenieLamp/CodePatches.cs new file mode 100644 index 00000000..348685c7 --- /dev/null +++ b/GenieLamp/CodePatches.cs @@ -0,0 +1,51 @@ +using HarmonyLib; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Newtonsoft.Json; +using StardewValley; +using StardewValley.Extensions; +using StardewValley.GameData; +using StardewValley.ItemTypeDefinitions; +using StardewValley.Menus; +using StardewValley.Objects; +using System; +using System.Collections.Generic; +using System.Text; +using Object = StardewValley.Object; +using Rectangle = Microsoft.Xna.Framework.Rectangle; + +namespace GenieLamp +{ + public partial class ModEntry + { + + [HarmonyPatch(typeof(Object), nameof(Object.performUseAction))] + public class Object_performUseAction_Patch + { + public static bool Prefix(Object __instance) + { + if (!Config.ModEnabled || (!__instance.Name.Equals(Config.LampItem) && !__instance.QualifiedItemId.Equals(Config.LampItem))) + return true; + int wishes = __instance.modData.TryGetValue(modKey, out var w) ? int.Parse(w) : 0; + if (wishes >= Config.WishesPerItem) + { + Game1.playSound("cancel", null); + Game1.showRedMessage(SHelper.Translation.Get("NoMoreWishes")); + return true; + } + try + { + Game1.playSound(Config.MenuSound, null); + } + catch { } + AccessTools.Method(typeof(ItemRegistry), "RebuildCache").Invoke(null, new object[0]); + + Game1.activeClickableMenu = new ObjectPickMenu( new NamingMenu.doneNamingBehavior(delegate (string target) + { + SpawnItem(target); + }), string.Format(SHelper.Translation.Get("WishMenuTitle"), Config.WishesPerItem - wishes)); + return false; + } + } + } +} \ No newline at end of file diff --git a/GenieLamp/GenieLamp.csproj b/GenieLamp/GenieLamp.csproj new file mode 100644 index 00000000..7b4db518 --- /dev/null +++ b/GenieLamp/GenieLamp.csproj @@ -0,0 +1,27 @@ + + + 1.0.0 + net6.0 + true + AnyCPU;x64 + + + + + + + PreserveNewest + + + + + + + + + + + PreserveNewest + + + \ No newline at end of file diff --git a/GenieLamp/IGenericModConfigMenuApi.cs b/GenieLamp/IGenericModConfigMenuApi.cs new file mode 100644 index 00000000..38158b11 --- /dev/null +++ b/GenieLamp/IGenericModConfigMenuApi.cs @@ -0,0 +1,82 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI; +using StardewModdingAPI.Utilities; +using StardewValley; + +namespace GenieLamp +{ + /// The API which lets other mods add a config UI through Generic Mod Config Menu. + public interface IGenericModConfigMenuApi + { + /********* + ** Methods + *********/ + /**** + ** Must be called first + ****/ + /// Register a mod whose config can be edited through the UI. + /// The mod's manifest. + /// Reset the mod's config to its default values. + /// Save the mod's current config to the config.json file. + /// Whether the options can only be edited from the title screen. + /// Each mod can only be registered once, unless it's deleted via before calling this again. + void Register(IManifest mod, Action reset, Action save, bool titleScreenOnly = false); + + /// Add a key binding at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + void AddKeybind(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); + + /// Add a boolean option at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + + /// Add a key binding list at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + void AddKeybindList(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); + + void AddBoolOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string fieldId = null); + + /// Add an integer option at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The minimum allowed value, or null to allow any. + /// The maximum allowed value, or null to allow any. + /// The interval of values that can be selected. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + void AddNumberOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, int? min = null, int? max = null, int? interval = null, string fieldId = null); + + /// Add a string option at the current position in the form. + /// The mod's manifest. + /// Get the current value from the mod config. + /// Set a new value in the mod config. + /// The label text to show in the form. + /// The tooltip text shown when the cursor hovers on the field, or null to disable the tooltip. + /// The values that can be selected, or null to allow any. + /// Get the display text to show for a value from , or null to show the values as-is. + /// The unique field ID for use with , or null to auto-generate a randomized ID. + void AddTextOption(IManifest mod, Func getValue, Action setValue, Func name, Func tooltip = null, string[] allowedValues = null, Func formatAllowedValue = null, string fieldId = null); + + /// Remove a mod from the config UI and delete all its options and pages. + /// The mod's manifest. + void Unregister(IManifest mod); + } +} diff --git a/GenieLamp/Methods.cs b/GenieLamp/Methods.cs new file mode 100644 index 00000000..018a79a4 --- /dev/null +++ b/GenieLamp/Methods.cs @@ -0,0 +1,55 @@ +using HarmonyLib; +using StardewValley; +using StardewValley.ItemTypeDefinitions; +using System.Collections.Generic; + +namespace GenieLamp +{ + public partial class ModEntry + { + + private static void SpawnItem(string target) + { + if (!string.IsNullOrEmpty(target)) + { + string itemId = GetItemId(target); + if (!string.IsNullOrEmpty(itemId)) + { + var item = ItemRegistry.Create(itemId, 1, 0, true); + if (item is not null) + { + int wishes = Game1.player.ActiveObject.modData.TryGetValue(modKey, out var w) ? int.Parse(w) : 0; + wishes++; + Game1.createItemDebris(item, Game1.player.Position, Game1.player.FacingDirection); + Game1.playSound(Config.WishSound, null); + if(wishes >= Config.WishesPerItem) + { + Game1.player.reduceActiveItemByOne(); + if(Game1.player.ActiveObject != null) + { + Game1.player.ActiveObject.modData[modKey] = "0"; + } + } + else + { + Game1.player.ActiveObject.modData[modKey] = wishes + ""; + } + } + } + } + Game1.activeClickableMenu.exitThisMenu(); + } + + private static string GetItemId(string target) + { + var dict = AccessTools.StaticFieldRefAccess>(typeof(ItemRegistry), "CachedItems"); + foreach (var kvp in dict) + { + var data = kvp.Value.GetParsedData(); + if (data.DisplayName.Equals(target)) + return kvp.Key; + } + return null; + } + } +} \ No newline at end of file diff --git a/GenieLamp/ModConfig.cs b/GenieLamp/ModConfig.cs new file mode 100644 index 00000000..e25f8ab1 --- /dev/null +++ b/GenieLamp/ModConfig.cs @@ -0,0 +1,14 @@ +using StardewModdingAPI; +using StardewModdingAPI.Utilities; + +namespace GenieLamp +{ + public class ModConfig + { + public bool ModEnabled { get; set; } = true; + public string LampItem { get; set; } = "Golden Mask"; + public string MenuSound { get; set; } = "cowboy_explosion"; + public string WishSound { get; set; } = "yoba"; + public int WishesPerItem { get; set; } = 3; + } +} diff --git a/GenieLamp/ModEntry.cs b/GenieLamp/ModEntry.cs new file mode 100644 index 00000000..4b58a0d8 --- /dev/null +++ b/GenieLamp/ModEntry.cs @@ -0,0 +1,100 @@ +using HarmonyLib; +using Microsoft.Xna.Framework; +using StardewModdingAPI; +using StardewValley; +using StardewValley.ItemTypeDefinitions; +using StardewValley.Objects; +using Object = StardewValley.Object; + +namespace GenieLamp +{ + /// The mod entry point. + public partial class ModEntry : Mod + { + + public static IMonitor SMonitor; + public static IModHelper SHelper; + public static ModConfig Config; + + public static ModEntry context; + + public static string modKey = "aedenthorn.GenieLamp"; + + + /// The mod entry point, called after the mod is first loaded. + /// Provides simplified APIs for writing mods. + public override void Entry(IModHelper helper) + { + Config = Helper.ReadConfig(); + + context = this; + + SMonitor = Monitor; + SHelper = helper; + + helper.Events.GameLoop.GameLaunched += GameLoop_GameLaunched; + helper.Events.GameLoop.DayStarted += GameLoop_DayStarted; + + var harmony = new Harmony(ModManifest.UniqueID); + harmony.PatchAll(); + + } + + private void GameLoop_DayStarted(object sender, StardewModdingAPI.Events.DayStartedEventArgs e) + { + Game1.player.addItemToInventory(new Object("124", 1)); + } + + private void GameLoop_GameLaunched(object sender, StardewModdingAPI.Events.GameLaunchedEventArgs e) + { + + // get Generic Mod Config Menu's API (if it's installed) + var configMenu = Helper.ModRegistry.GetApi("spacechase0.GenericModConfigMenu"); + if (configMenu is not null) + { + + // register mod + configMenu.Register( + mod: ModManifest, + reset: () => Config = new ModConfig(), + save: () => Helper.WriteConfig(Config) + ); + + configMenu.AddBoolOption( + mod: ModManifest, + name: () => Helper.Translation.Get("GMCM_Option_ModEnabled_Name"), + getValue: () => Config.ModEnabled, + setValue: value => Config.ModEnabled = value + ); + + configMenu.AddTextOption( + mod: ModManifest, + name: () => Helper.Translation.Get("GMCM_Option_LampItem_Name"), + getValue: () => Config.LampItem, + setValue: value => Config.LampItem = value + ); + + configMenu.AddNumberOption( + mod: ModManifest, + name: () => Helper.Translation.Get("GMCM_Option_WishesPerItem_Name"), + getValue: () => Config.WishesPerItem, + setValue: value => Config.WishesPerItem = value + ); + + configMenu.AddTextOption( + mod: ModManifest, + name: () => Helper.Translation.Get("GMCM_Option_MenuSound_Name"), + getValue: () => Config.MenuSound, + setValue: value => Config.MenuSound = value + ); + configMenu.AddTextOption( + mod: ModManifest, + name: () => Helper.Translation.Get("GMCM_Option_WishSound_Name"), + getValue: () => Config.WishSound, + setValue: value => Config.WishSound = value + ); + + } + } + } +} \ No newline at end of file diff --git a/GenieLamp/ObjectPickMenu.cs b/GenieLamp/ObjectPickMenu.cs new file mode 100644 index 00000000..9fe08e2e --- /dev/null +++ b/GenieLamp/ObjectPickMenu.cs @@ -0,0 +1,90 @@ +using HarmonyLib; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using StardewModdingAPI; +using StardewValley; +using StardewValley.BellsAndWhistles; +using StardewValley.ItemTypeDefinitions; +using StardewValley.Locations; +using StardewValley.Menus; +using System; +using System.Collections.Generic; +using System.Linq; +using static StardewValley.Minigames.TargetGame; + +namespace GenieLamp +{ + internal class ObjectPickMenu : NamingMenu + { + string lastText = ""; + List names = new(); + public ObjectPickMenu(doneNamingBehavior b, string title, string defaultName = "") : base(b, title, defaultName) + { + int adjust = 150; + textBox.Width += adjust * 2; + textBox.X -= adjust; + doneNamingButton.bounds.Offset(new Vector2(adjust, 0)); + randomButton.bounds.Offset(new Vector2(adjust, 0)); + textBox.textLimit = 999; + } + public override void draw(SpriteBatch b) + { + base.draw(b); + if(names is null) + { + names = new List(); + } + if(!string.IsNullOrEmpty(textBox.Text) && textBox.Text != lastText) + { + names.Clear(); + var dict = AccessTools.StaticFieldRefAccess>(typeof(ItemRegistry), "CachedItems"); + var lower = textBox.Text.ToLower(); + foreach (var kvp in dict) + { + var data = kvp.Value.GetParsedData(); + if (data.DisplayName.ToLower().StartsWith(lower) && !names.Contains(data.DisplayName)) + names.Add(data.DisplayName); + } + names.Sort(); + } + if (names.Any()) + { + for (int i = 0; i < names.Count; i++) + { + var lineY = textBox.Y + 64 + (i + 1) * Game1.smallFont.LineSpacing; + b.DrawString(Game1.smallFont, names[i], new Vector2(textBox.X, lineY), Color.LightGray); + if (ModEntry.SHelper.Input.IsDown(SButton.MouseLeft) && new Rectangle(textBox.X, lineY, (int)Game1.smallFont.MeasureString(names[i]).X, Game1.smallFont.LineSpacing).Contains(Game1.getMousePosition(true))) + { + textBox.Text = names[i]; + break; + } + } + drawMouse(b); + } + } + public override void receiveKeyPress(Keys key) + { + base.receiveKeyPress(key); + if(key == Keys.Escape) + { + exitThisMenu(); + } + } + public override void receiveLeftClick(int x, int y, bool playSound = true) + { + if (randomButton.containsPoint(x, y)) + { + var dict = AccessTools.StaticFieldRefAccess>(typeof(ItemRegistry), "CachedItems"); + var keys = dict.Keys.ToArray(); + textBox.Text = dict[keys[Game1.random.Next(keys.Length)]].GetParsedData().DisplayName; + randomButton.scale = this.randomButton.baseScale; + Game1.playSound("drumkit6", null); + } + else + { + base.receiveLeftClick(x, y, playSound); + } + } + } +} \ No newline at end of file diff --git a/GenieLamp/i18n/default.json b/GenieLamp/i18n/default.json new file mode 100644 index 00000000..e2de07f5 --- /dev/null +++ b/GenieLamp/i18n/default.json @@ -0,0 +1,9 @@ +{ + "GMCM_Option_ModEnabled_Name": "Mod Enabled", + "GMCM_Option_LampItem_Name": "Lamp Item", + "GMCM_Option_WishesPerItem_Name": "Wishes/Item", + "GMCM_Option_MenuSound_Name": "Menu Sound", + "GMCM_Option_WishSound_Name": "Wish Sound", + "WishMenuTitle": "What will you wish for? ({0} wishes remaining)", + "NoMoreWishes": "This item is out of wishes!" +} \ No newline at end of file diff --git a/GenieLamp/manifest.json b/GenieLamp/manifest.json new file mode 100644 index 00000000..e8723683 --- /dev/null +++ b/GenieLamp/manifest.json @@ -0,0 +1,9 @@ +{ + "Name": "Genie Lamp", + "Author": "aedenthorn", + "Version": "0.1.0", + "Description": "Genie Lamp.", + "UniqueID": "aedenthorn.GenieLamp", + "EntryDll": "GenieLamp.dll", + "UpdateKeys": [ "Nexus:19794" ] +} \ No newline at end of file diff --git a/StardewValleyMods.sln b/StardewValleyMods.sln index 1f6ce15b..196fb427 100644 --- a/StardewValleyMods.sln +++ b/StardewValleyMods.sln @@ -591,6 +591,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloseDoors", "CloseDoors\Cl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CropStacking", "CropStacking\CropStacking.csproj", "{35078079-6D93-4E54-9FBD-CFE746BEC267}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenieLamp", "GenieLamp\GenieLamp.csproj", "{CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -2919,6 +2921,14 @@ Global {35078079-6D93-4E54-9FBD-CFE746BEC267}.Release|Any CPU.Build.0 = Release|Any CPU {35078079-6D93-4E54-9FBD-CFE746BEC267}.Release|x64.ActiveCfg = Release|x64 {35078079-6D93-4E54-9FBD-CFE746BEC267}.Release|x64.Build.0 = Release|x64 + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Debug|x64.ActiveCfg = Debug|x64 + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Debug|x64.Build.0 = Debug|x64 + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Release|Any CPU.Build.0 = Release|Any CPU + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Release|x64.ActiveCfg = Release|x64 + {CB5B27E0-7BE9-4DB5-8CE9-C25D8D6F3089}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Swim/manifest.json b/Swim/manifest.json index 1ee04de8..d8d87256 100644 --- a/Swim/manifest.json +++ b/Swim/manifest.json @@ -5,19 +5,9 @@ "Description": "Allows you to swim.", "UniqueID": "aedenthorn.Swim", "EntryDll": "Swim.dll", - "MinimumApiVersion": "3.13.0", + "MinimumApiVersion": "3.16.0", "UpdateKeys": ["Nexus:6326"], - "ModUpdater": { - "Repository": "StardewValleyMods", - "User": "aedenthorn", - "Directory": "_releases", - "ModFolder": "Swim" - }, "Dependencies": [ - { - "UniqueID": "Platonymous.ModUpdater", - "IsRequired": false - }, { "UniqueID": "spacechase0.JsonAssets", "IsRequired": false