├── .gitattributes ├── FishingAssistant ├── Image │ └── FishingAssistant.gif ├── manifest.json ├── packages.config ├── I18n.tt ├── FishingAssistant.sln ├── Framework │ ├── KeyHelper.cs │ ├── SFishingRod.cs │ ├── SBobberBar.cs │ └── SModEntry.cs ├── Menu │ ├── Option │ │ ├── DescriptionElement.cs │ │ ├── BaseOptionElement.cs │ │ ├── CheckboxOptionElement.cs │ │ ├── ButtonOptionElement.cs │ │ ├── KeyListenerOptionElement.cs │ │ ├── PagingOptionElement.cs │ │ └── SliderOptionElement.cs │ ├── MenuHelper.cs │ └── FishingAssistantMenu.cs ├── Properties │ └── AssemblyInfo.cs ├── i18n │ ├── ru.json │ ├── pt.json │ ├── de.json │ ├── zh.json │ └── default.json ├── Component │ ├── CheatAction.cs │ ├── FishPreview.cs │ └── AutoAction.cs ├── FishingAssistant.csproj ├── ModConfig.cs ├── ModEntry.cs ├── README.md └── I18n.cs ├── README.md └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /FishingAssistant/Image/FishingAssistant.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KilZenc/Stardew-SMAPI-Mods/HEAD/FishingAssistant/Image/FishingAssistant.gif -------------------------------------------------------------------------------- /FishingAssistant/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Fishing Assistant", 3 | "Author": "KilZenc", 4 | "Version": "1.3.5", 5 | "Description": "Personal Fishing Assistant For You", 6 | "UniqueID": "KilZenc.FishingAssistant", 7 | "EntryDll": "FishingAssistant.dll", 8 | "MinimumApiVersion": "3.2.2", 9 | "UpdateKeys": [ "Nexus:5815" ] 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KilZenc Stardew Vally Mod Project 2 | SMAPI mods for stardew valley game. 3 | 4 | ## Fishing Assistant 5 | Your personal fishing assistant. that will handle the tough, boring and tiring fishing for you. 6 | 7 | for more information see [here](FishingAssistant/README.md) 8 | 9 | ![FishingAssistant](FishingAssistant/Image/FishingAssistant.gif) 10 | -------------------------------------------------------------------------------- /FishingAssistant/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FishingAssistant/I18n.tt: -------------------------------------------------------------------------------- 1 | <#@ template hostspecific="true" language="C#" #> 2 | <#@ output extension=".cs" #> 3 | <#@ assembly name="$(TargetDir)\Pathoschild.Stardew.ModTranslationClassBuilder.dll"#> 4 | <#@ import namespace="Pathoschild.Stardew.ModTranslationClassBuilder" #> 5 | <#= 6 | TranslationClassBuilder.Generate( 7 | jsonPath: this.Host.ResolvePath("i18n/default.json") 8 | ) 9 | #> 10 | -------------------------------------------------------------------------------- /FishingAssistant/FishingAssistant.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30907.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FishingAssistant", "FishingAssistant.csproj", "{BE962F18-EE51-4BE9-8DB7-9B1BB5AF94A6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {BE962F18-EE51-4BE9-8DB7-9B1BB5AF94A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {BE962F18-EE51-4BE9-8DB7-9B1BB5AF94A6}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {BE962F18-EE51-4BE9-8DB7-9B1BB5AF94A6}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {BE962F18-EE51-4BE9-8DB7-9B1BB5AF94A6}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {02A4551B-DA51-4AFD-9ABC-F9CF221D161D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /FishingAssistant/Framework/KeyHelper.cs: -------------------------------------------------------------------------------- 1 | namespace FishingAssistant.Framework 2 | { 3 | internal static class KeyHelper 4 | { 5 | public static string bobberBarHeight = "bobberBarHeight"; 6 | public static string bobberBarPos = "bobberBarPos"; 7 | public static string bobberPosition = "bobberPosition"; 8 | public static string bossFish = "bossFish"; 9 | public static string caughtDoubleFish = "caughtDoubleFish"; 10 | public static string difficulty = "difficulty"; 11 | public static string distanceFromCatching = "distanceFromCatching"; 12 | public static string fadeOut = "fadeOut"; 13 | public static string fishQuality = "fishQuality"; 14 | public static string fishSize = "fishSize"; 15 | public static string fromFishPond = "fromFishPond"; 16 | public static string perfect = "perfect"; 17 | public static string itemCategory = "itemCategory"; 18 | public static string treasure = "treasure"; 19 | public static string treasureCatchLevel = "treasureCatchLevel"; 20 | public static string treasureCaught = "treasureCaught"; 21 | public static string treasurePosition = "treasurePosition"; 22 | public static string treasureShake = "treasureShake"; 23 | public static string whichFish = "whichFish"; 24 | } 25 | } -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/DescriptionElement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using StardewValley; 4 | using StardewValley.Menus; 5 | 6 | namespace FishingAssistant.Menu.Option 7 | { 8 | /// An options element which contains descriptive text. 9 | internal class DescriptionElement : BaseOptionsElement 10 | { 11 | /********* 12 | ** Public methods 13 | *********/ 14 | 15 | /// Construct an instance. 16 | /// The checkbox label. 17 | public DescriptionElement(string label) 18 | : base(label, -1, -1, 0, 0, 0) { } 19 | 20 | /// Draw the component to the screen. 21 | /// The sprite batch being drawn. 22 | /// The X position at which to draw, relative to the bounds. 23 | /// The Y position at which to draw, relative to the bounds. 24 | /// The menu drawing the component. 25 | public override void draw(SpriteBatch spriteBatch, int slotX, int slotY, IClickableMenu context = null) 26 | { 27 | int yOffset = 0; 28 | spriteBatch.DrawString(Game1.smallFont, this.label, new Vector2(slotX + this.bounds.X, slotY + this.bounds.Y + yOffset), Color.Black); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /FishingAssistant/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("FishingAssistant")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("FishingAssistant")] 12 | [assembly: AssemblyCopyright("Copyright © 2021")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("be962f18-ee51-4be9-8db7-9b1bb5af94a6")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/BaseOptionElement.cs: -------------------------------------------------------------------------------- 1 | using StardewModdingAPI; 2 | using StardewValley.Menus; 3 | 4 | namespace FishingAssistant.Menu.Option 5 | { 6 | /// The base implementation for a cheat option element. 7 | internal abstract class BaseOptionsElement : OptionsElement 8 | { 9 | /********* 10 | ** Protected methods 11 | *********/ 12 | 13 | /// Construct an instance. 14 | /// The element label. 15 | protected BaseOptionsElement(string label) 16 | : base(label) { } 17 | 18 | /// Construct an instance. 19 | /// The display label. 20 | /// The X pixel position at which to draw the element. 21 | /// The Y pixel position at which to draw the element. 22 | /// The pixel width. 23 | /// The pixel height. 24 | /// The option ID. 25 | protected BaseOptionsElement(string label, int x, int y, int width, int height, int whichOption = -1) 26 | : base(label, x, y, width, height, whichOption) { } 27 | 28 | /// Get the X offset at which to render the element. 29 | protected int GetOffsetX() 30 | { 31 | // Android port doesn't consider the element width, so we do so here 32 | return Constants.TargetPlatform == GamePlatform.Android 33 | ? bounds.Width + 8 34 | : 0; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /FishingAssistant/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | //General 3 | "mod.name" : "Fishing Assistant", 4 | "mod.status.enable" : "Включен", 5 | "mod.status.disable" : "Отключен", 6 | 7 | //HUD messages 8 | "hud.message.mod.toggle" : "{0} Fishing Assistant.", 9 | "hud.message.cast.power" : "{0} Макс. сила броска.", 10 | "hud.message.catch.treasure" : "{0} Ловли сундука.", 11 | "hud.message.request.disable" : "Мод будет отключен, когда завершится задание.", 12 | "hud.message.force.enable" : "Нажмите снова {0} для принудительного включения.", 13 | "hud.message.low.stamina" : "Мало энергии.", 14 | "hud.message.inventory.full" : "Полный багаж.", 15 | "hud.message.auto.disable" : "{0} - автоматически отключить мод", 16 | "hud.message.config.saved" : "Новые настройки сохранены", 17 | 18 | //Config Menu 19 | "menu.config.header.control": "Управление", 20 | "menu.config.button.toggle.mod": "Сменить кнопку запуска мода", 21 | "menu.config.button.max.cast.power": "Кнопка макс. броска", 22 | "menu.config.button.catch.treasure": "Кнопка ловли сундуков", 23 | "menu.config.button.open.menu": "Кнопка открытия меню", 24 | "menu.config.reset.control": "Сбросить настройки", 25 | "menu.config.press-new-key": "Нажмите на новую клавишу...", 26 | 27 | "menu.config.header.general": "Общее", 28 | "menu.config.label.mod.status.position": "Расположение статуса мода", 29 | "menu.config.label.auto.pause.fishing": "Включить авто-паузу во время рыбалки", 30 | "menu.config.label.pause.fishing.time": "Пауза во время рыбалки", 31 | 32 | "menu.config.header.fishing.rod": "Рыбная удочка", 33 | "menu.config.label.infinite.bait": "Бесконечная наживка", 34 | "menu.config.label.infinite.tackle": "Бесконечные снасти", 35 | 36 | "menu.config.header.mini-game": "Мини-игра рыбалки", 37 | "menu.config.label.always.perfect": "Всегда безупречно", 38 | "menu.config.label.always.catch.double": "Всегда ловить по две рыбы", 39 | "menu.config.label.always.treasure": "Всегда находить сундук", 40 | "menu.config.label.instant.fishbite": "Мгновенный клев рыбы", 41 | "menu.config.label.instant.catch.fish": "Мгновенная ловля рыбы", 42 | "menu.config.label.instant.catch.treasure": "Мгновенная ловля сундука", 43 | "menu.config.label.fish.difficulty.multiplier": "Множитель сложности рыбы", 44 | "menu.config.label.fish.difficulty.additive": "Добавка к сложности рыбы", 45 | "menu.config.description.fish.difficulty": "Значение, отличимое от этого, можно изменить в config.json.", 46 | "menu.config.description.android-config-note": "Вы можете изменить кнопки на экране в config.json для модов Fishing Assistant и Virtual Keyboard.", 47 | 48 | "menu.config.header.fish.info": "Информация о Рыбалке", 49 | "menu.config.label.fishinfo.display.position": "Расположение информации об рыбе", 50 | "menu.config.label.show.fish.name": "Показывать название рыбы", 51 | "menu.config.label.show.treasure": "Показывать сундук", 52 | "menu.config.label.show.uncaught.fish": "Показывать непойманную рыбу", 53 | "menu.config.label.show.legendaly.fish": "Показывать легендарную рыбу", 54 | } 55 | -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/CheckboxOptionElement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using StardewValley; 4 | using StardewValley.Menus; 5 | using System; 6 | 7 | namespace FishingAssistant.Menu.Option 8 | { 9 | /// A checkbox UI option. 10 | internal class CheckboxOptionElement : BaseOptionsElement 11 | { 12 | /********* 13 | ** Fields 14 | *********/ 15 | 16 | /// A callback to invoke when the value changes. 17 | private readonly Action SetValue; 18 | 19 | /// Whether the slider should be disabled. 20 | private readonly Func IsDisabled; 21 | 22 | /********* 23 | ** Accessors 24 | *********/ 25 | 26 | /// Whether the checkbox is currently checked. 27 | public bool IsChecked { get; private set; } 28 | 29 | /********* 30 | ** Public methods 31 | *********/ 32 | 33 | /// Construct an instance. 34 | /// The checkbox label. 35 | /// The initial value to set. 36 | /// A callback to invoke when the value changes. 37 | public CheckboxOptionElement(string label, bool value, Action setValue, Func disabled = null) 38 | : base(label, -1, -1, 9 * Game1.pixelZoom, 9 * Game1.pixelZoom, 0) 39 | { 40 | this.IsChecked = value; 41 | this.SetValue = setValue; 42 | this.IsDisabled = disabled ?? (() => false); 43 | } 44 | 45 | /// Handle the player clicking the left mouse button. 46 | /// The cursor's X pixel position. 47 | /// The cursor's Y pixel position. 48 | public override void receiveLeftClick(int x, int y) 49 | { 50 | if (greyedOut) 51 | return; 52 | Game1.soundBank.PlayCue("drumkit6"); 53 | base.receiveLeftClick(x, y); 54 | IsChecked = !IsChecked; 55 | SetValue(IsChecked); 56 | } 57 | 58 | /// Draw the component to the screen. 59 | /// The sprite batch being drawn. 60 | /// The X position at which to draw, relative to the bounds. 61 | /// The Y position at which to draw, relative to the bounds. 62 | /// The menu drawing the component. 63 | public override void draw(SpriteBatch spriteBatch, int slotX, int slotY, IClickableMenu context = null) 64 | { 65 | this.greyedOut = this.IsDisabled(); 66 | spriteBatch.Draw(Game1.mouseCursors, new Vector2(slotX + bounds.X, slotY + bounds.Y), IsChecked ? OptionsCheckbox.sourceRectChecked : OptionsCheckbox.sourceRectUnchecked, Color.White * (greyedOut ? 0.33f : 1f), 0.0f, Vector2.Zero, Game1.pixelZoom, SpriteEffects.None, 0.4f); 67 | base.draw(spriteBatch, slotX + GetOffsetX(), slotY, context); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/ButtonOptionElement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using StardewValley; 4 | using StardewValley.Menus; 5 | using System; 6 | 7 | namespace FishingAssistant.Menu.Option 8 | { 9 | /// A button which invokes a callback when clicked. 10 | internal class ButtonOptionElement : BaseOptionsElement 11 | { 12 | /********* 13 | ** Fields 14 | *********/ 15 | 16 | /// The action to perform when the button is toggled (or null to handle it manually). 17 | private readonly Action Toggle; 18 | 19 | /// The source rectangle for the 'set' button sprite. 20 | private readonly Rectangle SetButtonSprite = new Rectangle(294, 428, 21, 11); 21 | 22 | /// The button area in screen pixels. 23 | private Rectangle SetButtonBounds; 24 | 25 | /********* 26 | ** Public methods 27 | *********/ 28 | 29 | /// Construct an instance. 30 | /// The field label. 31 | /// The field width. 32 | /// The action to perform when the button is toggled. 33 | /// Whether the button should be disabled. 34 | public ButtonOptionElement(string label, int slotWidth, Action toggle, bool disabled = false) 35 | : base(label, -1, -1, slotWidth + 1, 11 * Game1.pixelZoom) 36 | { 37 | this.SetButtonBounds = new Rectangle(slotWidth - 28 * Game1.pixelZoom, -1 + Game1.pixelZoom * 3, 21 * Game1.pixelZoom, 11 * Game1.pixelZoom); 38 | this.Toggle = toggle; 39 | this.greyedOut = disabled; 40 | } 41 | 42 | /// Handle the player clicking the left mouse button. 43 | /// The cursor's X pixel position. 44 | /// The cursor's Y pixel position. 45 | public override void receiveLeftClick(int x, int y) 46 | { 47 | if (this.greyedOut || !this.SetButtonBounds.Contains(x, y)) 48 | return; 49 | 50 | // callback handler 51 | this.Toggle(); 52 | } 53 | 54 | /// Draw the component to the screen. 55 | /// The sprite batch being drawn. 56 | /// The X position at which to draw, relative to the bounds. 57 | /// The Y position at which to draw, relative to the bounds. 58 | /// The menu drawing the component. 59 | public override void draw(SpriteBatch spriteBatch, int slotX, int slotY, IClickableMenu context = null) 60 | { 61 | Utility.drawTextWithShadow(spriteBatch, this.label, Game1.dialogueFont, new Vector2(this.bounds.X + slotX, this.bounds.Y + slotY), this.greyedOut ? Game1.textColor * 0.33f : Game1.textColor, 1f, 0.15f); 62 | Utility.drawWithShadow(spriteBatch, Game1.mouseCursors, new Vector2(this.SetButtonBounds.X + slotX, this.SetButtonBounds.Y + slotY), this.SetButtonSprite, Color.White, 0.0f, Vector2.Zero, Game1.pixelZoom, false, 0.15f); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /FishingAssistant/i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | //General 3 | "mod.name" : "Fishing Assistant", 4 | "mod.status.enable" : "Ativado", 5 | "mod.status.disable" : "Desativado", 6 | "general.position.left" : "Esquerda", 7 | "general.position.right" : "Direita", 8 | "general.position.top" : "Acima", 9 | "general.position.bottom" : "Abaixo", 10 | "general.position.upper-right" : "Acima, à direita", 11 | "general.position.upper-left" : "Acima, à esquerda", 12 | "general.position.lower-right" : "Abaixo, à direita", 13 | "general.position.lower-left" : "Abaixo, à esquerda", 14 | 15 | //HUD messages 16 | "hud.message.mod.toggle" : "{0} Fishing Assistant - Assistente de Pesca.", 17 | "hud.message.cast.power" : "{0} Barra de distância Máx..", 18 | "hud.message.catch.treasure" : "{0} Pegar tesouro.", 19 | "hud.message.request.disable" : "Mod será desativado após execução atual.", 20 | "hud.message.force.enable" : "Pressione {0} novamente para forçar ativação.", 21 | "hud.message.low.stamina" : "Pouca energia.", 22 | "hud.message.inventory.full" : "Inventário cheio.", 23 | "hud.message.auto.disable" : "Desativar mod automaticamente em {0}", 24 | "hud.message.config.saved" : "Salvar nova configuração", 25 | 26 | //Config Menu 27 | "menu.config.header.control": "Controle", 28 | "menu.config.button.toggle.mod": "Tecla de alternância de mod", 29 | "menu.config.button.max.cast.power": "Tecla de barra de distância", 30 | "menu.config.button.catch.treasure": "Tecla de pegar tesouros", 31 | "menu.config.button.open.menu": "Tecla menu button Abrir botão ", 32 | "menu.config.reset.control": "Resetar", 33 | "menu.config.press-new-key": "Pressione nova tecla...", 34 | 35 | "menu.config.header.general": "Geral", 36 | "menu.config.label.mod.status.position": "Status de posição do mod", 37 | "menu.config.label.auto.pause.fishing": "Ativar pausa automática ao pescar", 38 | "menu.config.label.pause.fishing.time": "Tempo de pausa ao pescar", 39 | 40 | "menu.config.header.fishing.rod": "Vara de pesca", 41 | "menu.config.label.infinite.bait": "Isca infinita", 42 | "menu.config.label.infinite.tackle": "Anzol infinito", 43 | 44 | "menu.config.header.mini-game": "Mini-game de pesca", 45 | "menu.config.label.always.perfect": "Perfeição absoluta", 46 | "menu.config.label.always.catch.double": "Sempre capturar dois peixes", 47 | "menu.config.label.always.treasure": "Sempre encontrar Tesouros", 48 | "menu.config.label.instant.fishbite": "Mordida instantânea de peixe", 49 | "menu.config.label.instant.catch.fish": "Captura instantânea de peixe", 50 | "menu.config.label.instant.catch.treasure": "Captura instantânea de tesouro", 51 | "menu.config.label.fish.difficulty.multiplier": "Multiplicador de dificuldade de peixe", 52 | "menu.config.label.fish.difficulty.additive": "Adicional de Dificuldade de Peixes", 53 | "menu.config.description.fish.difficulty": "Outros valores fora esses que podem ser selecionados nos arquivos externos de configurações.", 54 | "menu.config.description.android-config-note": "Você pode mudar as teclas nos arquivos de configuração externos para os mods Fishing Assistant e Virtual Keyboard.", 55 | 56 | "menu.config.header.fish.info": "Informações sobre os peixes", 57 | "menu.config.label.fishinfo.display.position": "Localização dos peixes", 58 | "menu.config.label.show.fish.name": "Mostrar nome dos peixes", 59 | "menu.config.label.show.treasure": "Mostrar tesouros", 60 | "menu.config.label.show.uncaught.fish": "Mostrar peixes não capturados", 61 | "menu.config.label.show.legendaly.fish": "Mostrar peixes lendários", 62 | } 63 | -------------------------------------------------------------------------------- /FishingAssistant/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | //General 3 | "mod.name" : "Fishing Assistant", 4 | "mod.status.enable" : "Aktiviert", 5 | "mod.status.disable" : "Deaktiviert", 6 | "general.position.left" : "Links", 7 | "general.position.right" : "Rechts", 8 | "general.position.top" : "Oben", 9 | "general.position.bottom" : "Unten", 10 | "general.position.upper-right" : "Oben-Rechts", 11 | "general.position.upper-left" : "Oben-Links", 12 | "general.position.lower-right" : "Unten-Rechts", 13 | "general.position.lower-left" : "Unten-Links", 14 | 15 | //HUD messages 16 | "hud.message.mod.toggle" : "{0} Fishing Assistant.", 17 | "hud.message.cast.power" : "{0} Max. Wurfkraft.", 18 | "hud.message.catch.treasure" : "{0} Schatz sammeln.", 19 | "hud.message.request.disable" : "Mod wird nach dieser Aktion deaktiviert.", 20 | "hud.message.force.enable" : "Drücke {0} erneut, um Aktivierung zu erzwingen.", 21 | "hud.message.low.stamina" : "Geringe Energie.", 22 | "hud.message.inventory.full" : "Inventar voll.", 23 | "hud.message.auto.disable" : "Auto Mod-Abschaltung an {0}", 24 | "hud.message.config.saved" : "Speichere neue Konfiguration", 25 | "hud.message.auto.attach.bait" : "Wurm-Köder autom. anbringen : {0}", 26 | "hud.message.auto.attach.tackle" : "Spezial-Köder autom. anbringen : {0}", 27 | 28 | //Config Menu 29 | "menu.config.header.control": "Steuerung", 30 | "menu.config.button.toggle.mod": "Mod Durchschalten Taste", 31 | "menu.config.button.max.cast.power": "Wurfkraft Taste", 32 | "menu.config.button.catch.treasure": "Schatz sammeln Taste", 33 | "menu.config.button.open.menu": "Menü öffnen Taste", 34 | "menu.config.reset.control": "Zurücksetzen", 35 | "menu.config.press-new-key": "Drücke neue Taste...", 36 | 37 | "menu.config.header.general": "Allgemein", 38 | "menu.config.label.mod.status.position": "Mod-Status Position", 39 | "menu.config.label.auto.pause.fishing": "Auto-Pause beim Fischen aktivieren", 40 | "menu.config.label.pause.fishing.time": "Fangzeit pausieren", 41 | 42 | "menu.config.header.fishing.rod": "Angel", 43 | "menu.config.label.auto.attach.bait": "Wurm-Köder autom. anbringen", 44 | "menu.config.label.auto.attach.tackle": "Spezial-Köder autom. anbringen", 45 | "menu.config.label.infinite.bait": "Unendlich Wurm-Köder", 46 | "menu.config.label.infinite.tackle": "Unendlich Spezial-Köder", 47 | 48 | "menu.config.header.mini-game": "Fischen Mini-Game", 49 | "menu.config.label.always.perfect": "Immer Perfekt", 50 | "menu.config.label.always.catch.double": "Immer doppelten Fisch fangen", 51 | "menu.config.label.always.treasure": "Immer Schatz finden", 52 | "menu.config.label.instant.fishbite": "Sofortiger Anbiss der Fische", 53 | "menu.config.label.instant.catch.fish": "Sofortiges Fangen der Fische", 54 | "menu.config.label.instant.catch.treasure": "Sofortiges Schatz sammeln", 55 | "menu.config.label.fish.difficulty.multiplier": "Fisch-Schwierigkeitsmultiplikator", 56 | "menu.config.label.fish.difficulty.additive": "Fisch-Schwierigkeitszusatz", 57 | "menu.config.description.fish.difficulty": "Ein anderer Wert als dieser kann in der Konfigurationsdatei eingestellt werden.", 58 | "menu.config.description.android-config-note": "Du kannst die On-Screen-Tasten in den Konfigurationsdateien für die Mods Fishing Assistant und Virtual Keyboard ändern.", 59 | 60 | "menu.config.header.fish.info": "Angel-Informationen", 61 | "menu.config.label.fishinfo.display.position": "Fischinfo Position", 62 | "menu.config.label.show.fish.name": "Zeige Fischnamen", 63 | "menu.config.label.show.treasure": "Zeige Schatz", 64 | "menu.config.label.show.uncaught.fish": "Zeige ungefangenen Fisch", 65 | "menu.config.label.show.legendaly.fish": "Zeige legendären Fisch", 66 | } 67 | -------------------------------------------------------------------------------- /FishingAssistant/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | //General 3 | "mod.name" : "钓鱼助手", 4 | "mod.status.enable" : "启用", 5 | "mod.status.disable" : "禁用", 6 | "general.position.left" : "左侧", 7 | "general.position.right" : "右侧", 8 | "general.position.top" : "顶部", 9 | "general.position.bottom" : "底部", 10 | "general.position.upper-right" : "右上角", 11 | "general.position.upper-left" : "左上角", 12 | "general.position.lower-right" : "右下角", 13 | "general.position.lower-left" : "左下角", 14 | 15 | //HUD messages 16 | "hud.message.mod.toggle" : "{0} 钓鱼助手。", 17 | "hud.message.cast.power" : "{0} 最大抛竿力量。", 18 | "hud.message.catch.treasure" : "{0} 捕获宝藏。", 19 | "hud.message.request.disable" : "模组将在任务完成后禁用。", 20 | "hud.message.force.enable" : "再次按下 {0} 以强制启用。", 21 | "hud.message.low.stamina" : "低能量。", 22 | "hud.message.inventory.full" : "物品栏已满。", 23 | "hud.message.auto.disable" : "在 {0} 自动禁用模组", 24 | "hud.message.config.saved" : "正在保存新配置", 25 | "hud.message.auto.attach.bait" : "自动安装鱼饵:{0}", 26 | "hud.message.auto.attach.tackle" : "自动安装钓具:{0}", 27 | 28 | //Config Menu 29 | "menu.config.header.control": "控制", 30 | "menu.config.button.toggle.mod": "开关模组按钮", 31 | "menu.config.button.max.cast.power": "抛竿力量按钮", 32 | "menu.config.button.catch.treasure": "捕获宝藏按钮", 33 | "menu.config.button.open.menu": "打开菜单按钮", 34 | "menu.config.reset.control": "重置", 35 | "menu.config.press-new-key": "按下新按键…", 36 | 37 | "menu.config.header.general": "常规", 38 | "menu.config.label.mod.status.position": "模组状态位置", 39 | "menu.config.label.auto.pause.fishing": "自动暂停钓鱼", 40 | "menu.config.label.pause.fishing.time": "暂停钓鱼时间", 41 | "menu.config.label.auto.eat.food": "自动吃食物", 42 | "menu.config.label.energy.to.eat.food": "吃的能量", 43 | "menu.config.label.allow.eat.fish": "允许吃鱼", 44 | "menu.config.etc.allow.eat.fish.warning": "小心,传说之鱼可能会被吃掉。", 45 | "menu.config.etc.when-low-energy": "低能量时", 46 | 47 | "menu.config.header.automation": "自动化", 48 | "menu.config.label.current.preset": "自动化预设", 49 | "menu.config.label.preset.default": "默认", 50 | "menu.config.label.preset.custom": "自定义", 51 | "menu.config.label.preset.disable": "禁用", 52 | "menu.config.label.automation.auto.cast": "自动抛竿", 53 | "menu.config.label.automation.auto.hook": "自动上钩", 54 | "menu.config.label.automation.auto.play-mini-game": "自动玩迷你游戏", 55 | "menu.config.label.automation.auto.close-popup": "自动关闭弹窗", 56 | "menu.config.label.automation.auto.loot-treasure": "自动收集宝藏", 57 | "menu.config.description.enable.custom.automation": "启用此部分,将“自动化预设”设置为“自定义”。", 58 | 59 | "menu.config.header.fishing.rod": "鱼竿", 60 | "menu.config.label.auto.attach.bait": "自动安装鱼饵", 61 | "menu.config.label.auto.attach.tackle": "自动安装钓具", 62 | "menu.config.label.infinite.bait": "无限鱼饵", 63 | "menu.config.label.infinite.tackle": "无限钓具", 64 | 65 | "menu.config.header.enchantment": "附魔", 66 | "menu.config.label.enchantment.auto-hook": "添加自动上钩附魔", 67 | "menu.config.label.enchantment.efficient": "添加省力附魔", 68 | "menu.config.label.enchantment.master": "添加精通附魔", 69 | "menu.config.label.enchantment.preserving": "添加耐用附魔", 70 | "menu.config.label.enchantment.when-held": "仅在持有时添加附魔", 71 | 72 | "menu.config.header.mini-game": "迷你钓鱼游戏", 73 | "menu.config.label.always.perfect": "总是完美", 74 | "menu.config.label.always.catch.double": "总是钓到两条鱼", 75 | "menu.config.label.always.treasure": "总是找到宝藏", 76 | "menu.config.label.instant.fishbite": "立即咬钩", 77 | "menu.config.label.instant.catch.fish": "立即捕获鱼", 78 | "menu.config.label.instant.catch.treasure": "立即捕获宝藏", 79 | "menu.config.label.fish.difficulty.multiplier": "鱼类难度系数", 80 | "menu.config.label.fish.difficulty.additive": "鱼类难度附加值", 81 | "menu.config.description.fish.difficulty": "可以在配置文件中设置其他值。", 82 | "menu.config.description.android-config-note": "你可以在钓鱼助手和虚拟键盘(Virtual Keyboard)模组的配置文件中更改屏幕上的按钮。", 83 | 84 | "menu.config.header.fish.info": "鱼类信息", 85 | "menu.config.label.fishinfo.display.position": "鱼类信息位置", 86 | "menu.config.label.show.fish.name": "显示鱼的名称", 87 | "menu.config.label.show.treasure": "显示宝藏", 88 | "menu.config.label.show.uncaught.fish": "显示未捕获的鱼", 89 | "menu.config.label.show.legendaly.fish": "显示传说鱼类", 90 | } 91 | -------------------------------------------------------------------------------- /FishingAssistant/Menu/MenuHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using StardewValley; 4 | using StardewValley.Menus; 5 | 6 | namespace FishingAssistant.Menu 7 | { 8 | internal static class MenuHelper 9 | { 10 | public static string GetLocalizationText(string value) 11 | { 12 | switch (value) 13 | { 14 | case "Left": return I18n.General_Position_Left(); 15 | case "Right": return I18n.General_Position_Right(); 16 | case "Top": return I18n.General_Position_Top(); 17 | case "Bottom": return I18n.General_Position_Bottom(); 18 | case "UpperRight": return I18n.General_Position_UpperRight(); 19 | case "UpperLeft": return I18n.General_Position_UpperLeft(); 20 | case "LowerRight": return I18n.General_Position_LowerRight(); 21 | case "LowerLeft": return I18n.General_Position_LowerLeft(); 22 | case "Default": return I18n.Menu_Config_Label_Preset_Default(); 23 | case "Custom": return I18n.Menu_Config_Label_Preset_Custom(); 24 | case "Disable": return I18n.Menu_Config_Label_Preset_Disable(); 25 | default: return ""; 26 | } 27 | } 28 | 29 | /// Draw a button texture fir the given text to the screen. 30 | /// The X position at which to draw. 31 | /// The Y position at which to draw. 32 | /// The text font. 33 | /// The button text. 34 | /// The button's horizontal alignment relative to . The possible values are 0 (left), 1 (center), or 2 (right). 35 | /// The button opacity, as a value from 0 (transparent) to 1 (opaque). 36 | /// Whether to draw a shadow under the tab. 37 | public static void DrawTab(int x, int y, SpriteFont font, string text, int align = 0, float alpha = 1, bool drawShadow = true) 38 | { 39 | SpriteBatch spriteBatch = Game1.spriteBatch; 40 | Vector2 bounds = font.MeasureString(text); 41 | 42 | DrawTab(x, y, (int)bounds.X, (int)bounds.Y, out Vector2 drawPos, align, alpha, drawShadow: drawShadow); 43 | Utility.drawTextWithShadow(spriteBatch, text, font, drawPos, Game1.textColor); 44 | } 45 | 46 | /// Draw a button texture to the screen. 47 | /// The X position at which to draw. 48 | /// The Y position at which to draw. 49 | /// The width of the button's inner content. 50 | /// The height of the button's inner content. 51 | /// The position at which the content should be drawn. 52 | /// The button's horizontal alignment relative to . The possible values are 0 (left), 1 (center), or 2 (right). 53 | /// The button opacity, as a value from 0 (transparent) to 1 (opaque). 54 | /// Whether to draw a shadow under the tab. 55 | public static void DrawTab(int x, int y, int innerWidth, int innerHeight, out Vector2 innerDrawPosition, int align = 0, float alpha = 1, bool drawShadow = true) 56 | { 57 | SpriteBatch spriteBatch = Game1.spriteBatch; 58 | 59 | // calculate outer coordinates 60 | int outerWidth = innerWidth + 4 * Game1.pixelZoom * 2; 61 | int outerHeight = innerHeight + Game1.tileSize / 3; 62 | switch (align) 63 | { 64 | case 1: align = -outerWidth / 2; break; 65 | case 2: align = -outerWidth; break; 66 | default: align = 0; break; 67 | } 68 | int offsetX = align; 69 | 70 | // draw texture 71 | IClickableMenu.drawTextureBox(spriteBatch, Game1.menuTexture, new Rectangle(0, 256, 60, 60), x + offsetX, y, outerWidth, outerHeight + Game1.tileSize / 16, Color.White * alpha, drawShadow: drawShadow); 72 | innerDrawPosition = new Vector2(x + 4 * Game1.pixelZoom + offsetX, y + 4 * Game1.pixelZoom); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /FishingAssistant/Component/CheatAction.cs: -------------------------------------------------------------------------------- 1 | using StardewModdingAPI; 2 | using StardewValley; 3 | 4 | namespace FishingAssistant 5 | { 6 | internal partial class ModEntry : Mod 7 | { 8 | private void AddEnchantment() 9 | { 10 | if (Config.AddAutoHookEnchantment && !fishingRod.hasEnchantmentOfType()) 11 | fishingRod.enchantments.Add(new AutoHookEnchantment()); 12 | 13 | if (Config.AddEfficientEnchantment && !fishingRod.hasEnchantmentOfType()) 14 | fishingRod.enchantments.Add(new EfficientToolEnchantment()); 15 | 16 | if (Config.AddMasterEnchantment && !fishingRod.hasEnchantmentOfType()) 17 | fishingRod.enchantments.Add(new MasterEnchantment()); 18 | 19 | if (Config.AddPreservingEnchantment && !fishingRod.hasEnchantmentOfType()) 20 | fishingRod.enchantments.Add(new PreservingEnchantment()); 21 | } 22 | 23 | /// Make bait and tackle last long forever 24 | private void ApplyInfiniteBaitAndTackle() 25 | { 26 | if (!(Context.IsWorldReady && fishingRod != null)) 27 | return; 28 | 29 | if (Config.InfiniteBait && fishingRod.attachments?.Length > 0 && fishingRod.attachments[0] != null && fishingRod.attachments[0].Stack < fishingRod.attachments[0].maximumStackSize()) 30 | fishingRod.attachments[0].Stack = fishingRod.attachments[0].maximumStackSize(); 31 | 32 | if (Config.InfiniteTackle && fishingRod.attachments?.Length > 1 && fishingRod.attachments[1] != null && fishingRod.attachments[1].uses.Value != 0) 33 | fishingRod.attachments[1].uses.Value = 0; 34 | } 35 | 36 | /// Make fish instantly bite when fishing 37 | private void InstantFishBite() 38 | { 39 | if (Config.InstantFishBite && (IsRodFishing && !IsRodHit && RodTimeUntilFishBite > 0)) 40 | RodTimeUntilFishBite = 0; 41 | } 42 | 43 | /// End fishing minigame 44 | private void EndMiniGame() 45 | { 46 | if (Game1.isFestival()) 47 | return; 48 | 49 | Game1.playSound("jingle1"); 50 | 51 | int attachmentValue = fishingRod.attachments[0] == null ? -1 : fishingRod.attachments[0].parentSheetIndex; 52 | bool caughtDouble = Config.AlwaysCatchDoubleFish || (!BarBossFish && attachmentValue == 774 && Game1.random.NextDouble() < 0.25 + Game1.player.DailyLuck / 2.0); 53 | 54 | fishingRod.pullFishFromWater(BarWhichFish, BarFishSize, BarFishQuality, (int)BarDifficulty, BarTreasureCaught, BarPerfect, BarFromFishPond, caughtDouble); 55 | Game1.exitActiveMenu(); 56 | 57 | Game1.setRichPresence("location", Game1.currentLocation.Name); 58 | } 59 | 60 | /// Force treasure spawn every time when fishing 61 | private void AlwayFindTreasure() 62 | { 63 | if (Config.AlwaysFindTreasure && !Game1.isFestival()) 64 | BarHasTreasure = true; 65 | } 66 | 67 | /// Make fish instantly catch, simply skip minigame 68 | private void InstantCatchFish() 69 | { 70 | if (Config.InstantCatchFish) 71 | { 72 | if (BarHasTreasure) 73 | BarTreasureCaught = true; 74 | BarDistanceFromCatching = 1.0f; 75 | } 76 | } 77 | 78 | /// Make treasure is instantly caught when appearing 79 | private void InstantCatchTreasure() 80 | { 81 | if (Config.InstantCatchTreasure && (BarHasTreasure || Config.AlwaysFindTreasure)) 82 | BarTreasureCaught = true; 83 | } 84 | 85 | /// Override fish difficult 86 | private void OverrideFishDifficult() 87 | { 88 | BarDifficulty *= Config.FishDifficultyMultiplier; 89 | BarDifficulty += Config.FishDifficultyAdditive; 90 | if (BarDifficulty < 0) BarDifficulty = 0; 91 | } 92 | 93 | /// Force fishing minigame alway perfect 94 | private void AlwayPerfectResult() 95 | { 96 | if (Config.AlwaysPerfect) 97 | BarPerfect = true; 98 | } 99 | 100 | /// Force fishing rod cast power to max 101 | private void MaxCastPower() 102 | { 103 | if (maxCastPower) 104 | RodCastPower = 1.01f; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /FishingAssistant/i18n/default.json: -------------------------------------------------------------------------------- 1 | { 2 | //General 3 | "mod.name" : "Fishing Assistant", 4 | "mod.status.enable" : "Enable", 5 | "mod.status.disable" : "Disable", 6 | "general.position.left" : "Left", 7 | "general.position.right" : "Right", 8 | "general.position.top" : "Top", 9 | "general.position.bottom" : "Bottom", 10 | "general.position.upper-right" : "UpperRight", 11 | "general.position.upper-left" : "UpperLeft", 12 | "general.position.lower-right" : "LowerRight", 13 | "general.position.lower-left" : "LowerLeft", 14 | 15 | //HUD messages 16 | "hud.message.mod.toggle" : "{0} Fishing Assistant.", 17 | "hud.message.cast.power" : "{0} Max cast power.", 18 | "hud.message.catch.treasure" : "{0} Catch treasure.", 19 | "hud.message.request.disable" : "Mod will disable after task is finished.", 20 | "hud.message.force.enable" : "Pressed {0} again to force enable.", 21 | "hud.message.low.stamina" : "Low energy.", 22 | "hud.message.inventory.full" : "Inventory full.", 23 | "hud.message.auto.disable" : "Auto disable mod on {0}", 24 | "hud.message.config.saved" : "Saving new config", 25 | "hud.message.auto.attach.bait" : "Auto attach bait : {0}", 26 | "hud.message.auto.attach.tackle" : "Auto attach tackle : {0}", 27 | 28 | //Config Menu 29 | "menu.config.header.control": "Control", 30 | "menu.config.button.toggle.mod": "Toggle Mod Button", 31 | "menu.config.button.max.cast.power": "Cast power button", 32 | "menu.config.button.catch.treasure": "Catch treasure button", 33 | "menu.config.button.open.menu": "Open menu button", 34 | "menu.config.reset.control": "Reset", 35 | "menu.config.press-new-key": "Press New Key...", 36 | 37 | "menu.config.header.general": "General", 38 | "menu.config.label.mod.status.position": "Mod status position", 39 | "menu.config.label.auto.pause.fishing": "Auto pause fishing", 40 | "menu.config.label.pause.fishing.time": "Pause fishing time", 41 | "menu.config.label.auto.eat.food": "Auto eat food", 42 | "menu.config.label.energy.to.eat.food": "Energy to eat", 43 | "menu.config.label.allow.eat.fish": "Allow eating fish", 44 | "menu.config.etc.allow.eat.fish.warning": "Be careful, the legendary fish may be eaten.", 45 | "menu.config.etc.when-low-energy": "When low energy", 46 | 47 | "menu.config.header.automation": "Automation", 48 | "menu.config.label.current.preset": "Automation preset", 49 | "menu.config.label.preset.default": "Default", 50 | "menu.config.label.preset.custom": "Custom", 51 | "menu.config.label.preset.disable": "Disable", 52 | "menu.config.label.automation.auto.cast": "Auto cast fishing rod", 53 | "menu.config.label.automation.auto.hook": "Auto hook fish", 54 | "menu.config.label.automation.auto.play-mini-game": "Auto play mini-game", 55 | "menu.config.label.automation.auto.close-popup": "Auto close popup", 56 | "menu.config.label.automation.auto.loot-treasure": "Auto loot treasure", 57 | "menu.config.description.enable.custom.automation": "To enable this section. set Automation preset to Custom.", 58 | 59 | "menu.config.header.fishing.rod": "Fishing rod", 60 | "menu.config.label.auto.attach.bait": "Auto attach bait", 61 | "menu.config.label.auto.attach.tackle": "Auto attach tackle", 62 | "menu.config.label.infinite.bait": "Infinite bait", 63 | "menu.config.label.infinite.tackle": "Infinite tackle", 64 | 65 | "menu.config.header.enchantment": "Enchantment", 66 | "menu.config.label.enchantment.auto-hook": "Add Auto-Hook enchantment", 67 | "menu.config.label.enchantment.efficient": "Add Efficient enchantment", 68 | "menu.config.label.enchantment.master": "Add Master enchantment", 69 | "menu.config.label.enchantment.preserving": "Add Preserving enchantment", 70 | "menu.config.label.enchantment.when-held": "Only add enchantment when held", 71 | 72 | "menu.config.header.mini-game": "Fishing mini-game", 73 | "menu.config.label.always.perfect": "Always Perfect", 74 | "menu.config.label.always.catch.double": "Always catch double fish", 75 | "menu.config.label.always.treasure": "Always found treasure", 76 | "menu.config.label.instant.fishbite": "Instant fish bite", 77 | "menu.config.label.instant.catch.fish": "Instant catch fish", 78 | "menu.config.label.instant.catch.treasure": "Instant catch treasure", 79 | "menu.config.label.fish.difficulty.multiplier": "Fish difficulty multiplier", 80 | "menu.config.label.fish.difficulty.additive": "Fish difficulty additive", 81 | "menu.config.description.fish.difficulty": "Value other than this can be set in config file.", 82 | "menu.config.description.android-config-note": "You can change the on-screen buttons in the config files for the Fishing Assistant and Virtual Keyboard mods.", 83 | 84 | "menu.config.header.fish.info": "Fishing information", 85 | "menu.config.label.fishinfo.display.position": "Fish info position", 86 | "menu.config.label.show.fish.name": "Show fish name", 87 | "menu.config.label.show.treasure": "Show treasure", 88 | "menu.config.label.show.uncaught.fish": "Show uncaught fish", 89 | "menu.config.label.show.legendaly.fish": "Show legendary fish", 90 | } 91 | -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/KeyListenerOptionElement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using Microsoft.Xna.Framework.Input; 4 | using StardewModdingAPI; 5 | using StardewValley; 6 | using StardewValley.Menus; 7 | using System; 8 | 9 | namespace FishingAssistant.Menu.Option 10 | { 11 | /// A button which lets the user choose a key binding. 12 | internal class KeyListenerOptionElement : BaseOptionsElement 13 | { 14 | /********* 15 | ** Fields 16 | *********/ 17 | 18 | /// The current key binding. 19 | private SButton Value; 20 | 21 | /// The action to perform when the key binding is chosen. 22 | private readonly Action SetValue; 23 | 24 | /// The translated 'press new key' label. 25 | private readonly string PressNewKeyLabel; 26 | 27 | /// The source rectangle for the 'set' button sprite. 28 | private readonly Rectangle SetButtonSprite = new Rectangle(294, 428, 21, 11); 29 | 30 | /// The button area in screen pixels. 31 | private Rectangle SetButtonBounds; 32 | 33 | /// The button to set when the player clears it. 34 | private readonly SButton ClearToButton; 35 | 36 | private string ListenerMessage; 37 | private bool Listening; 38 | 39 | /********* 40 | ** Public methods 41 | *********/ 42 | 43 | /// Construct an instance. 44 | /// The field label. 45 | /// The field width. 46 | /// The current key binding. 47 | /// The action to perform when the button is toggled. 48 | /// The button to set when the player clears it. 49 | public KeyListenerOptionElement(string label, int slotWidth, SButton value, Action setValue, SButton clearToButton = SButton.None) 50 | : base(label, -1, -1, slotWidth + 1, 11 * Game1.pixelZoom) 51 | { 52 | Value = value; 53 | PressNewKeyLabel = I18n.Menu_Config_PressNewKey(); 54 | SetValue = setValue; 55 | SetButtonBounds = new Rectangle(slotWidth - 28 * Game1.pixelZoom, -1 + Game1.pixelZoom * 3, 21 * Game1.pixelZoom, 11 * Game1.pixelZoom); 56 | ClearToButton = clearToButton; 57 | } 58 | 59 | /// Handle the player clicking the left mouse button. 60 | /// The cursor's X pixel position. 61 | /// The cursor's Y pixel position. 62 | public override void receiveLeftClick(int x, int y) 63 | { 64 | if (greyedOut || Listening || !SetButtonBounds.Contains(x, y) || Constants.TargetPlatform == GamePlatform.Android) 65 | return; 66 | 67 | Listening = true; 68 | Game1.soundBank.PlayCue("breathin"); 69 | GameMenu.forcePreventClose = true; 70 | ListenerMessage = PressNewKeyLabel; 71 | } 72 | 73 | /// Handle the player pressing a keyboard button. 74 | /// The key that was pressed. 75 | public override void receiveKeyPress(Keys key) 76 | { 77 | if (greyedOut || !Listening) 78 | return; 79 | 80 | if (key == Keys.Escape) 81 | { 82 | Value = ClearToButton; 83 | Game1.soundBank.PlayCue("bigDeSelect"); 84 | } 85 | else 86 | { 87 | Value = key.ToSButton(); 88 | Game1.soundBank.PlayCue("coin"); 89 | } 90 | 91 | SetValue(Value); 92 | Listening = false; 93 | GameMenu.forcePreventClose = false; 94 | } 95 | 96 | /// Draw the component to the screen. 97 | /// The sprite batch being drawn. 98 | /// The X position at which to draw, relative to the bounds. 99 | /// The Y position at which to draw, relative to the bounds. 100 | /// The menu drawing the component. 101 | public override void draw(SpriteBatch spriteBatch, int slotX, int slotY, IClickableMenu context = null) 102 | { 103 | Utility.drawTextWithShadow(spriteBatch, $"{label}: {Value}", Game1.dialogueFont, new Vector2(bounds.X + slotX, bounds.Y + slotY), greyedOut ? Game1.textColor * 0.33f : Game1.textColor, 1f, 0.15f); 104 | if (Constants.TargetPlatform != GamePlatform.Android) 105 | Utility.drawWithShadow(spriteBatch, Game1.mouseCursors, new Vector2(SetButtonBounds.X + slotX, SetButtonBounds.Y + slotY), SetButtonSprite, Color.White, 0.0f, Vector2.Zero, Game1.pixelZoom, false, 0.15f); 106 | 107 | if (Listening) 108 | { 109 | spriteBatch.Draw(Game1.staminaRect, new Rectangle(0, 0, Game1.graphics.GraphicsDevice.Viewport.Width, Game1.graphics.GraphicsDevice.Viewport.Height), new Rectangle(0, 0, 1, 1), Color.Black * 0.75f, 0.0f, Vector2.Zero, SpriteEffects.None, 0.999f); 110 | spriteBatch.DrawString(Game1.dialogueFont, ListenerMessage, Utility.getTopLeftPositionForCenteringOnScreen(Game1.tileSize * 3, Game1.tileSize), Color.White, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, 0.9999f); 111 | } 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/PagingOptionElement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using Microsoft.Xna.Framework.Input; 4 | using StardewValley; 5 | using StardewValley.Menus; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace FishingAssistant.Menu.Option 10 | { 11 | //Still not available to use 12 | internal class PagingOptionElement : BaseOptionsElement 13 | { 14 | private readonly List options = new List(); 15 | 16 | private readonly List displayOptions = new List(); 17 | 18 | private readonly Action SetValue; 19 | 20 | private int selected; 21 | 22 | private static bool snapNext; 23 | 24 | private static bool snapPrevious; 25 | 26 | private Rectangle previousButton; 27 | 28 | private Rectangle nextButton; 29 | 30 | private static Rectangle previousButtonSource = new Rectangle(352, 495, 12, 11); 31 | 32 | private static Rectangle nextButtonSource = new Rectangle(365, 495, 12, 11); 33 | 34 | private Rectangle displayTxt; 35 | 36 | public PagingOptionElement(string label, List options, Action setValue, int x = -1, int y = -1) 37 | : base(label, x, y, 7 * Game1.pixelZoom, 7 * Game1.pixelZoom) 38 | { 39 | base.label = label; 40 | this.options = options; 41 | this.displayOptions = options; 42 | this.SetValue = setValue; 43 | 44 | if (x == -1) { x = 32; } 45 | if (y == -1) { y = 16; } 46 | 47 | int margin = 14; 48 | int width = 48; 49 | int hight = 44; 50 | 51 | int txtSize = (int)Game1.dialogueFont.MeasureString(options[0]).X; 52 | foreach (string displayOption in displayOptions) 53 | { 54 | txtSize = Math.Max((int)Game1.dialogueFont.MeasureString(displayOption).X, txtSize); 55 | } 56 | 57 | base.bounds = new Rectangle(x, y, txtSize + (width * 2) + (margin * 2), hight); 58 | this.previousButton = new Rectangle(x, y, width, hight); 59 | this.displayTxt = new Rectangle(x + previousButton.Width + margin, y, txtSize, hight); 60 | this.nextButton = new Rectangle(x + previousButton.Width + (margin * 2) + txtSize, y, width, hight); 61 | } 62 | 63 | public override void receiveLeftClick(int x, int y) 64 | { 65 | if (!base.greyedOut && this.options.Count > 0) 66 | { 67 | if (this.previousButton.Contains(x, y) && this.selected != 0) 68 | { 69 | this.selected--; 70 | snapPrevious = true; 71 | Game1.playSound("drumkit6"); 72 | } 73 | else if (this.nextButton.Contains(x, y) && this.selected != this.options.Count - 1) 74 | { 75 | this.selected++; 76 | snapNext = true; 77 | Game1.playSound("drumkit6"); 78 | } 79 | 80 | if (this.selected < 0) 81 | this.selected = 0; 82 | else if (this.selected >= this.options.Count) 83 | this.selected = this.options.Count - 1; 84 | 85 | SetValue(options[selected]); 86 | } 87 | } 88 | 89 | public override void receiveKeyPress(Keys key) 90 | { 91 | base.receiveKeyPress(key); 92 | if (Game1.options.snappyMenus && Game1.options.gamepadControls) 93 | { 94 | if (Game1.options.doesInputListContain(Game1.options.moveRightButton, key)) 95 | { 96 | this.receiveLeftClick(this.nextButton.Center.X, this.nextButton.Center.Y); 97 | } 98 | else if (Game1.options.doesInputListContain(Game1.options.moveLeftButton, key)) 99 | { 100 | this.receiveLeftClick(this.previousButton.Center.X, this.previousButton.Center.Y); 101 | } 102 | } 103 | } 104 | 105 | public override void draw(SpriteBatch b, int slotX, int slotY, IClickableMenu context = null) 106 | { 107 | b.Draw(Game1.mouseCursors, new Vector2(slotX + this.previousButton.X, slotY + this.previousButton.Y), previousButtonSource, Color.White * (base.greyedOut ? 0.33f : 1f) * ((this.selected == 0) ? 0.5f : 1f), 0f, Vector2.Zero, 4f, SpriteEffects.None, 0.4f); 108 | b.DrawString(Game1.dialogueFont, (this.selected < this.displayOptions.Count && this.selected != -1) ? this.displayOptions[this.selected] : "", new Vector2(slotX + this.displayTxt.X, slotY + this.displayTxt.Y), Game1.textColor); 109 | b.Draw(Game1.mouseCursors, new Vector2(slotX + this.nextButton.X, slotY + this.nextButton.Y), nextButtonSource, Color.White * (base.greyedOut ? 0.33f : 1f) * ((this.selected == this.displayOptions.Count - 1) ? 0.5f : 1f), 0f, Vector2.Zero, 4f, SpriteEffects.None, 0.4f); 110 | 111 | if (!Game1.options.snappyMenus && Game1.options.gamepadControls) 112 | { 113 | if (snapPrevious) 114 | { 115 | Game1.setMousePosition(slotX + this.previousButton.Center.X, slotY + this.previousButton.Center.Y); 116 | snapPrevious = false; 117 | } 118 | else if (snapNext) 119 | { 120 | Game1.setMousePosition(slotX + this.nextButton.Center.X, slotY + this.nextButton.Center.Y); 121 | snapNext = false; 122 | } 123 | } 124 | Utility.drawTextWithShadow(b, label, Game1.dialogueFont, new Vector2(slotX + nextButton.X + nextButton.Width + 4 + GetOffsetX(), slotY + this.bounds.Y), Game1.textColor); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /FishingAssistant/FishingAssistant.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BE962F18-EE51-4BE9-8DB7-9B1BB5AF94A6} 8 | Library 9 | Properties 10 | FishingAssistant 11 | FishingAssistant 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 41 | 42 | 43 | packages\Pathoschild.Stardew.ModTranslationClassBuilder.1.0.0\lib\net452\Pathoschild.Stardew.ModTranslationClassBuilder.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | True 60 | True 61 | I18n.tt 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | TextTemplatingFileGenerator 94 | I18n.cs 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /FishingAssistant/ModConfig.cs: -------------------------------------------------------------------------------- 1 | using StardewModdingAPI; 2 | 3 | namespace FishingAssistant 4 | { 5 | internal class ModConfig 6 | { 7 | /// The default values. 8 | public static ModConfig Defaults { get; } = new ModConfig(); 9 | 10 | /// Button for toggle max or free fishing rod cast power 11 | public SButton EnableModButton { get; set; } = SButton.F5; 12 | 13 | /// Button for toggle max or free fishing rod cast power 14 | public SButton CastPowerButton { get; set; } = SButton.F6; 15 | 16 | /// Button for toggle catch or ignore treasure in fishing minigame 17 | public SButton CatchTreasureButton { get; set; } = SButton.F7; 18 | 19 | /// Button for roload config file 20 | public SButton OpenMenuButton { get; set; } = SButton.F8; 21 | 22 | /// Whether the game should consider every catch to be perfectly executed, even if it wasn't. 23 | public bool AlwaysPerfect { get; set; } = false; 24 | 25 | /// Whether to always find treasure. 26 | public bool AlwaysFindTreasure { get; set; } = false; 27 | 28 | /// Make fish to bite instantly. 29 | public bool InstantFishBite { get; set; } = false; 30 | 31 | /// Whether to catch fish instantly. 32 | public bool InstantCatchFish { get; set; } = false; 33 | 34 | /// Whether to catch treasure instantly. 35 | public bool InstantCatchTreasure { get; set; } = false; 36 | 37 | /// Whether fishing tackles last forever. 38 | public bool InfiniteTackle { get; set; } = false; 39 | 40 | /// Whether fishing bait lasts forever. 41 | public bool InfiniteBait { get; set; } = false; 42 | 43 | /// A multiplier applied to the fish difficulty. This can a number between 0 and 1 to lower difficulty, or more than 1 to increase it. 44 | public float FishDifficultyMultiplier { get; set; } = 1; 45 | 46 | /// A value added to the fish difficulty. This can be less than 0 to decrease difficulty, or more than 0 to increase it. 47 | public float FishDifficultyAdditive { get; set; } = 0; 48 | 49 | /// Make you catch double fish every time 50 | public bool AlwaysCatchDoubleFish { get; set; } = false; 51 | 52 | /// Position to display fish info when playing fishing minigame 53 | public string ModStatusDisplayPosition { get; set; } = "Right"; 54 | 55 | /// Should mod show fish info while catching fish? 56 | public bool DisplayFishInfo { get; set; } = true; 57 | 58 | /// Position to display fish info when playing fishing minigame 59 | public string FishInfoDisplayPosition { get; set; } = "UpperRight"; 60 | 61 | /// Show fish name with fish info 62 | public bool ShowFishName { get; set; } = true; 63 | 64 | /// Show treasure with fish info 65 | public bool ShowTreasure { get; set; } = true; 66 | 67 | /// Show fish info whether uncaught or not 68 | public bool ShowUncaughtFishSpecies { get; set; } = false; 69 | 70 | /// Alway show fish info if current fish is legendary 71 | public bool AlwaysShowLegendaryFish { get; set; } = false; 72 | 73 | /// Add auto-Hook enchantment to fishing rod 74 | public bool AddAutoHookEnchantment { get; set; } = false; 75 | 76 | /// Add Efficient enchantment to fishing rod 77 | public bool AddEfficientEnchantment { get; set; } = false; 78 | 79 | /// Add Master enchantment to fishing rod 80 | public bool AddMasterEnchantment { get; set; } = false; 81 | 82 | /// Add Preserving enchantment to fishing rod 83 | public bool AddPreservingEnchantment { get; set; } = false; 84 | 85 | /// Add Preserving enchantment to fishing rod 86 | public bool OnlyAddEnchantmentWhenHeld { get; set; } = false; 87 | 88 | /// 89 | public string AutomationPresets { get; set; } = "Default"; 90 | 91 | /// Auto cast fishing rod on custon preset 92 | public bool AutoCastFishingRod { get; set; } = true; 93 | 94 | /// Auto hook fish rod on custon preset 95 | public bool AutoHookFish { get; set; } = true; 96 | 97 | /// Auto play fishing mini-game on custon preset 98 | public bool AutoPlayMiniGame { get; set; } = true; 99 | 100 | /// Auto close fish popup on custon preset 101 | public bool AutoClosePopup { get; set; } = true; 102 | 103 | /// Auto loot treasure on custon preset 104 | public bool AutoLootTreasure { get; set; } = true; 105 | 106 | /// Shold mod auto attach bait if possible. 107 | public bool AutoAttachBait { get; set; } = false; 108 | 109 | /// Shold mod auto attach bait if possible. 110 | public bool AutoAttachTackles { get; set; } = false; 111 | 112 | /// Whether to pause fishing on given time 113 | public bool EnableAutoPauseFishing { get; set; } = true; 114 | 115 | /// Time to stop fishing 116 | public int PauseFishingTime { get; set; } = 24; 117 | 118 | /// Whether to eat some food if need 119 | public bool EnableAutoEatFood { get; set; } = false; 120 | 121 | /// Amount of energy in percent to find food to eat 122 | public int EnergyPrecentToEat { get; set; } = 0; 123 | 124 | /// Allow to eat fish that you caught 125 | public bool AllowEatingFish { get; set; } = false; 126 | } 127 | } -------------------------------------------------------------------------------- /FishingAssistant/ModEntry.cs: -------------------------------------------------------------------------------- 1 | using FishingAssistant.Menu; 2 | using StardewModdingAPI; 3 | using StardewModdingAPI.Events; 4 | using StardewValley; 5 | using StardewValley.Menus; 6 | using StardewValley.Tools; 7 | 8 | namespace FishingAssistant 9 | { 10 | partial class ModEntry : Mod 11 | { 12 | private ModConfig Config; 13 | private bool isPause; 14 | 15 | /// The mod entry point, called after the mod is first loaded. 16 | public override void Entry(IModHelper helper) 17 | { 18 | // Initialize mod 19 | Initialize(helper); 20 | } 21 | 22 | /// Raised after the game is launched, right before the first update tick. 23 | private void OnGameLaunched(object sender, GameLaunchedEventArgs e) 24 | { 25 | // read the Config for display position and get list priority for displayOrder 26 | ReloadConfig(); 27 | } 28 | 29 | /// Raised after the in-game clock time changes. 30 | private void OnTimeChanged(object sender, TimeChangedEventArgs e) 31 | { 32 | AutoStopFishingOnTime(); 33 | } 34 | 35 | /// Raised before the game ends the current day. 36 | /// This happens before it starts setting up the next day and before StardewModdingAPI.Events.IGameLoopEvents.Saving. 37 | private void OnDayStarted(object sender, DayStartedEventArgs e) 38 | { 39 | isForceEnable = false; 40 | hasEnableRequest = false; 41 | hasDisableRequest = false; 42 | } 43 | 44 | /// Raised after the game state is updated (≈60 times per second). 45 | private void OnUpdateTicked(object sender, UpdateTickedEventArgs e) 46 | { 47 | if (!modEnable || !Context.IsWorldReady) 48 | return; 49 | 50 | // Apply infinite bait/tackle 51 | ApplyInfiniteBaitAndTackle(); 52 | 53 | if (IsFishingMiniGameReady()) 54 | { 55 | //Force fishing minigame result to be perfect 56 | AlwayPerfectResult(); 57 | 58 | //Auto play minigame 59 | AutoPlayMiniGame(); 60 | } 61 | else 62 | { 63 | //Auto loot item in treasure chest 64 | AutoLootTreasure(); 65 | } 66 | 67 | if (Game1.player?.CurrentTool is FishingRod rod) 68 | { 69 | fishingRod = rod; 70 | 71 | if (isPause) 72 | { 73 | //Unpause mod status and get new player data 74 | isPause = false; 75 | GetPlayerData(); 76 | } 77 | 78 | AddEnchantment(); 79 | 80 | AutoEatFood(ignoreCondition: false); 81 | 82 | // Find bait and tackle add attach to fishing rod 83 | AutoAttachBait(); 84 | AutoAttachTackles(); 85 | 86 | // Cast fishing rod if possible 87 | AutoCastFishingRod(); 88 | 89 | // Force max cast power 90 | MaxCastPower(); 91 | 92 | // Make fish instantly bite 93 | InstantFishBite(); 94 | 95 | //Auto hook fish when fish bite 96 | AutoHook(); 97 | 98 | //Auto close fish popup 99 | AutoCloseFishPopup(); 100 | } 101 | else 102 | { 103 | //Change mod status to pasue 104 | isPause = true; 105 | modState = ModState.Pause; 106 | autoCastDelay = defaultAutoCastDelay; 107 | autoClosePopupDelay = defaultAutoClosePopupDelay; 108 | autoLootDelay = defaultAutoLootDelay; 109 | 110 | if (Config.OnlyAddEnchantmentWhenHeld) 111 | fishingRod.ClearEnchantments(); 112 | } 113 | } 114 | 115 | /// Raised after a game menu is opened, closed, or replaced. 116 | private void OnMenuChanged(object sender, MenuChangedEventArgs e) 117 | { 118 | //Save config value 119 | if (e.OldMenu is FishingAssistantMenu) 120 | { 121 | Helper.WriteConfig(Config); 122 | ReloadConfig(false); 123 | } 124 | 125 | if (!modEnable) 126 | return; 127 | 128 | // Check if fishing minigame is start 129 | if (e.NewMenu is BobberBar bar) 130 | OnFishingMiniGameStart(bar); 131 | 132 | // Check if fishing minigame is end 133 | if (e.OldMenu is BobberBar) 134 | OnFishingMiniGameEnd(); 135 | } 136 | 137 | /// When a menu is open, raised after that menu is drawn to the sprite batch but before it's rendered to the screen. 138 | private void OnRenderMenu(object sender, RenderedActiveMenuEventArgs e) 139 | { 140 | DrawFishInfo(); 141 | } 142 | 143 | /// 144 | /// Raised before drawing the HUD (item toolbar, clock, etc) to the screen. The vanilla 145 | /// HUD may be hidden at this point (e.g. because a menu is open). Content drawn 146 | /// to the sprite batch at this point will appear under the HUD. 147 | /// 148 | private void OnRenderingHud(object sender, RenderingHudEventArgs e) 149 | { 150 | DrawModStatus(); 151 | } 152 | 153 | /// Raised after the player presses a button on the keyboard, controller, or mouse. 154 | private void OnButtonPressed(object sender, ButtonPressedEventArgs e) 155 | { 156 | if (!Context.IsWorldReady) return; 157 | 158 | //Enable or disable mod 159 | OnEnableModButtonPressed(e); 160 | 161 | //Toggle cast power to max or free 162 | OnCastPowerButtonPressed(e); 163 | 164 | //Toggle catch or ignore treasure when play fishing minigame 165 | OnCatchTreasureButtonPressed(e); 166 | 167 | //Open in-game mod menu 168 | OnToggleMenuButtonPressed(e); 169 | } 170 | 171 | private void OnFishingMiniGameStart(BobberBar bar) 172 | { 173 | bobberBar = bar; 174 | inFishingMiniGame = true; 175 | isCatching = true; 176 | fishPreviewFishId = 0; 177 | 178 | //Overide fish difficulty 179 | OverrideFishDifficult(); 180 | 181 | //Make treasure appear every time 182 | AlwayFindTreasure(); 183 | 184 | //Instantly catch fish when minigame start 185 | InstantCatchFish(); 186 | 187 | //Instantly catch treasure when treasure appear 188 | InstantCatchTreasure(); 189 | } 190 | 191 | private void OnFishingMiniGameEnd() 192 | { 193 | //Reset varible value 194 | inFishingMiniGame = false; 195 | isCatching = false; 196 | catchingTreasure = false; 197 | 198 | if (hasDisableRequest) 199 | { 200 | hasDisableRequest = false; 201 | modEnable = false; 202 | modState = ModState.Disable; 203 | } 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /FishingAssistant/Menu/Option/SliderOptionElement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using StardewValley; 4 | using StardewValley.Menus; 5 | using System; 6 | 7 | namespace FishingAssistant.Menu.Option 8 | { 9 | /// A UI slider for selecting from a range of values. 10 | internal class SliderOptionElement : BaseOptionsElement 11 | { 12 | /********* 13 | ** Fields 14 | *********/ 15 | 16 | /// The field label. 17 | private readonly string Label; 18 | 19 | /// The callback to invoke when the value changes. 20 | private readonly Action SetValue; 21 | 22 | /// A callback to invoke when the value changes. 23 | private readonly Action SetValueBool; 24 | 25 | /// The minimum value that can be selected using the field. 26 | private readonly int MinValue; 27 | 28 | /// The maximum value that can be selected using the field. 29 | private readonly int MaxValue; 30 | 31 | /// The current value. 32 | private int Value; 33 | 34 | /// The value's position in the range of values between and . 35 | private float ValuePosition; 36 | 37 | /// Whether the slider should be disabled. 38 | private readonly Func IsDisabled; 39 | 40 | /// Format the display label. 41 | private readonly Func Format; 42 | 43 | /// The pixel width of the slider area. 44 | private int PixelWidth => this.bounds.Width - 10 * Game1.pixelZoom; 45 | 46 | /********* 47 | ** Public methods 48 | *********/ 49 | 50 | /// Construct an instance. 51 | /// The field label. 52 | /// The initial value. 53 | /// The maximum value that can be selected using the field. 54 | /// The callback to invoke when the value changes. 55 | /// The minimum value that can be selected using the field. 56 | /// Whether the slider should be disabled. 57 | /// Format the display label. 58 | /// The field width. 59 | public SliderOptionElement(string label, int value, int maxValue, Action setValue, Action setValueBool = null, int minValue = 0, Func disabled = null, Func format = null, int width = 48) 60 | : base(label, -1, -1, width * Game1.pixelZoom, 6 * Game1.pixelZoom, 0) 61 | { 62 | this.Label = label; 63 | this.Value = value; 64 | this.MinValue = minValue; 65 | this.MaxValue = maxValue; 66 | this.SetValue = setValue; 67 | this.SetValueBool = setValueBool; 68 | this.IsDisabled = disabled ?? (() => false); 69 | this.Format = format ?? (v => v.ToString()); 70 | 71 | this.ValuePosition = this.GetRangePosition(); 72 | } 73 | 74 | /// Handle the player holding the left mouse button. 75 | /// The cursor's X pixel position. 76 | /// The cursor's Y pixel position. 77 | public override void leftClickHeld(int x, int y) 78 | { 79 | if (this.greyedOut) 80 | return; 81 | 82 | base.leftClickHeld(x, y); 83 | 84 | this.ValuePosition = GetRangePosition(x, this.bounds.X, this.bounds.X + this.PixelWidth); 85 | this.Value = GetValueAtPosition(this.ValuePosition, this.MinValue, this.MaxValue); 86 | } 87 | 88 | /// Handle the player clicking the left mouse button. 89 | /// The cursor's X pixel position. 90 | /// The cursor's Y pixel position. 91 | public override void receiveLeftClick(int x, int y) 92 | { 93 | if (this.greyedOut) 94 | return; 95 | base.receiveLeftClick(x, y); 96 | this.leftClickHeld(x, y); 97 | } 98 | 99 | /// Handle the player releasing the left mouse button. 100 | /// The cursor's X pixel position. 101 | /// The cursor's Y pixel position. 102 | public override void leftClickReleased(int x, int y) 103 | { 104 | this.ValuePosition = this.GetRangePosition(); // snap to value position 105 | this.SetValue(this.Value); 106 | if (SetValueBool != null) 107 | this.SetValueBool(false); 108 | } 109 | 110 | /// Draw the component to the screen. 111 | /// The sprite batch being drawn. 112 | /// The X position at which to draw, relative to the bounds. 113 | /// The Y position at which to draw, relative to the bounds. 114 | /// The menu drawing the component. 115 | public override void draw(SpriteBatch spriteBatch, int slotX, int slotY, IClickableMenu context = null) 116 | { 117 | this.label = $"{this.Label}: {this.Format(this.Value)}"; 118 | this.greyedOut = this.IsDisabled(); 119 | 120 | base.draw(spriteBatch, slotX + this.GetOffsetX(), slotY, context); 121 | 122 | int sliderOffsetX = GetValueAtPosition(this.ValuePosition, 0, this.PixelWidth); 123 | IClickableMenu.drawTextureBox(spriteBatch, Game1.mouseCursors, OptionsSlider.sliderBGSource, slotX + this.bounds.X, slotY + this.bounds.Y, this.bounds.Width, this.bounds.Height, Color.White * (this.greyedOut ? 0.33f : 1f), Game1.pixelZoom, false); 124 | spriteBatch.Draw(Game1.mouseCursors, new Vector2(slotX + this.bounds.X + sliderOffsetX, slotY + this.bounds.Y), OptionsSlider.sliderButtonRect, Color.White * (this.greyedOut ? 0.33f : 1f), 0.0f, Vector2.Zero, Game1.pixelZoom, SpriteEffects.None, 0.9f); 125 | } 126 | 127 | /********* 128 | ** Private methods 129 | *********/ 130 | 131 | /// Get the 's fractional position, as a value between 0 () and 1 (). 132 | private float GetRangePosition() 133 | { 134 | return GetRangePosition(this.Value, this.MinValue, this.MaxValue); 135 | } 136 | 137 | /**** 138 | ** Math helpers 139 | ****/ 140 | 141 | /// Get a value's fractional position within a range, as a value between 0 () and 1 (). 142 | /// The value within the range. 143 | /// The minimum value. 144 | /// The maximum value. 145 | public static float GetRangePosition(int value, int minValue, int maxValue) 146 | { 147 | float position = (value - minValue) / (float)(maxValue - minValue); 148 | return MathHelper.Clamp(position, 0, 1); 149 | } 150 | 151 | /// Get the value from a position within a range. 152 | /// The position within the range, where 0 is the minimum value and 1 is the maximum value. 153 | /// The minimum value. 154 | /// The maximum value. 155 | public static int GetValueAtPosition(float position, int minValue, int maxValue) 156 | { 157 | float value = position * (maxValue - minValue) + minValue; 158 | return (int)MathHelper.Clamp(value, minValue, maxValue); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /FishingAssistant/Framework/SFishingRod.cs: -------------------------------------------------------------------------------- 1 | using FishingAssistant.Framework; 2 | using StardewModdingAPI; 3 | using StardewValley; 4 | using StardewValley.Tools; 5 | using System; 6 | 7 | namespace FishingAssistant 8 | { 9 | internal partial class ModEntry : Mod 10 | { 11 | private FishingRod fishingRod; 12 | 13 | private bool IsRodBobberStillInAir 14 | { 15 | get 16 | { 17 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 18 | return fishingRod.castedButBobberStillInAir; 19 | } 20 | } 21 | 22 | private bool IsRodCasting 23 | { 24 | get 25 | { 26 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 27 | return fishingRod.isCasting; 28 | } 29 | } 30 | 31 | private bool IsRodFishCaught 32 | { 33 | get 34 | { 35 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 36 | return fishingRod.fishCaught; 37 | } 38 | set 39 | { 40 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 41 | fishingRod.fishCaught = value; 42 | } 43 | } 44 | 45 | private bool IsRodFishing 46 | { 47 | get 48 | { 49 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 50 | return fishingRod.isFishing; 51 | } 52 | } 53 | 54 | private bool IsRodHit 55 | { 56 | get 57 | { 58 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 59 | return fishingRod.hit; 60 | } 61 | } 62 | 63 | private bool IsRodInUse 64 | { 65 | get 66 | { 67 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 68 | return fishingRod.inUse(); 69 | } 70 | } 71 | 72 | private bool IsRodNibbing 73 | { 74 | get 75 | { 76 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 77 | return fishingRod.isNibbling; 78 | } 79 | } 80 | 81 | private bool IsRodPullingOutOfWater 82 | { 83 | get 84 | { 85 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 86 | return fishingRod.pullingOutOfWater; 87 | } 88 | } 89 | 90 | private bool IsRodReeling 91 | { 92 | get 93 | { 94 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 95 | return fishingRod.isReeling; 96 | } 97 | } 98 | 99 | private bool IsRodShowingTreasure 100 | { 101 | get 102 | { 103 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 104 | return fishingRod.showingTreasure; 105 | } 106 | set 107 | { 108 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 109 | fishingRod.showingTreasure = value; 110 | } 111 | } 112 | 113 | private bool IsRodTimingCasting 114 | { 115 | get 116 | { 117 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 118 | return fishingRod.isTimingCast; 119 | } 120 | } 121 | 122 | private bool IsRodTreasureCaught 123 | { 124 | get 125 | { 126 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 127 | return fishingRod.treasureCaught; 128 | } 129 | } 130 | 131 | private float RodCastPower 132 | { 133 | set 134 | { 135 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 136 | fishingRod.castingPower = value; 137 | } 138 | } 139 | 140 | private bool RodCaughtDoubleFish 141 | { 142 | get 143 | { 144 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 145 | return Helper.Reflection.GetField(fishingRod, KeyHelper.caughtDoubleFish, true).GetValue(); 146 | } 147 | set 148 | { 149 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 150 | Helper.Reflection.GetField(fishingRod, KeyHelper.caughtDoubleFish, true).SetValue(value); 151 | } 152 | } 153 | 154 | private int RodFishQuality 155 | { 156 | get 157 | { 158 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 159 | return Helper.Reflection.GetField(fishingRod, KeyHelper.fishQuality, true).GetValue(); 160 | } 161 | set 162 | { 163 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 164 | Helper.Reflection.GetField(fishingRod, KeyHelper.fishQuality, true).SetValue(value); 165 | } 166 | } 167 | 168 | private bool RodFromFishPond 169 | { 170 | get 171 | { 172 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 173 | return Helper.Reflection.GetField(fishingRod, KeyHelper.fromFishPond, true).GetValue(); 174 | } 175 | set 176 | { 177 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 178 | Helper.Reflection.GetField(fishingRod, KeyHelper.fromFishPond, true).SetValue(value); 179 | } 180 | } 181 | 182 | private string RodItemCategory 183 | { 184 | get 185 | { 186 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 187 | return Helper.Reflection.GetField(fishingRod, KeyHelper.itemCategory, true).GetValue(); 188 | } 189 | set 190 | { 191 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 192 | Helper.Reflection.GetField(fishingRod, KeyHelper.itemCategory, true).SetValue(value); 193 | } 194 | } 195 | 196 | private float RodTimeUntilFishBite 197 | { 198 | get 199 | { 200 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 201 | return fishingRod.timeUntilFishingBite; 202 | } 203 | set 204 | { 205 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 206 | fishingRod.timeUntilFishingBite = value; 207 | } 208 | } 209 | 210 | private int RodWhichFish 211 | { 212 | get 213 | { 214 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 215 | return Helper.Reflection.GetField(fishingRod, KeyHelper.whichFish, true).GetValue(); 216 | } 217 | set 218 | { 219 | if (fishingRod == null) throw new NullReferenceException(nameof(fishingRod)); 220 | Helper.Reflection.GetField(fishingRod, KeyHelper.whichFish, true).SetValue(value); 221 | } 222 | } 223 | 224 | /// Is player able to cast fishing rod 225 | /// True if player can cast fishing rod 226 | private bool RodIsNotInUse() 227 | { 228 | return Context.CanPlayerMove && Game1.activeClickableMenu == null && !fishingRod.inUse(); 229 | } 230 | 231 | /// Is fish bite or not 232 | /// True if fish bite 233 | private bool IsRodCanHook() 234 | { 235 | return IsRodNibbing && !IsRodReeling && !IsRodHit && !IsRodPullingOutOfWater && !IsRodFishCaught; 236 | } 237 | 238 | /// Is player currenly showing fish 239 | /// True if fish popup is show 240 | private bool IsRodShowingFish() 241 | { 242 | return !Context.CanPlayerMove && IsRodFishCaught && IsRodInUse && !IsRodCasting && !IsRodTimingCasting && !IsRodReeling && !IsRodPullingOutOfWater && !IsRodShowingTreasure; 243 | } 244 | } 245 | } -------------------------------------------------------------------------------- /FishingAssistant/Component/FishPreview.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using StardewModdingAPI; 4 | using StardewValley; 5 | using StardewValley.Menus; 6 | using StardewValley.Tools; 7 | using System; 8 | using System.Collections.Generic; 9 | using Object = StardewValley.Object; 10 | 11 | namespace FishingAssistant 12 | { 13 | internal partial class ModEntry : Mod 14 | { 15 | private bool isCatching = false; 16 | private int fishPreviewFishId = 0; 17 | private Object fishSprite; 18 | private Object treasureSprite; 19 | private bool showFish; 20 | private bool showText; 21 | private string textValue; 22 | private IList displayOrder; 23 | private Vector2 textSize; 24 | private int margin = 18; 25 | private int spriteSize = 64; 26 | private float scale = 0.7f; 27 | private int boxwidth; 28 | private int boxheight; 29 | private int treasureSpacing = 50; 30 | private int treasureOffset = 8; 31 | 32 | private void DrawFishInfo() 33 | { 34 | if (Game1.player == null || !Game1.player.IsLocalPlayer || !Config.DisplayFishInfo) 35 | return; 36 | 37 | if (Game1.activeClickableMenu is BobberBar bar && isCatching) 38 | { 39 | // stop drawing on fadeOut 40 | if (BarFadeOut) 41 | return; 42 | 43 | // figure out which fish is being caught 44 | int newFishId = BarWhichFish; 45 | 46 | // check if fish has changed somehow. If yes, re-make the fish sprite and its display text. 47 | GetFishData(newFishId); 48 | 49 | // call a function to position and draw the box 50 | DrawFishDisplay(displayOrder, bar); 51 | } 52 | } 53 | 54 | private void GetFishData(int newFishId) 55 | { 56 | if (newFishId != fishPreviewFishId) // catching a new fish OR the fish species has changed mid-cast 57 | { 58 | fishPreviewFishId = newFishId; 59 | Monitor.Log($"Currently catching: {fishPreviewFishId}", LogLevel.Trace); 60 | 61 | // save fish object to use in drawing // check for errors? 62 | fishSprite = new Object(fishPreviewFishId, 1); 63 | 64 | // determine if species has been caught before 65 | bool caughtSpecies = Game1.player.fishCaught.ContainsKey(fishPreviewFishId) && Game1.player.fishCaught[fishPreviewFishId][0] > 0; 66 | 67 | // is it a legendary fish? 68 | bool isLegendary = Helper.Reflection.GetMethod(typeof(FishingRod), "isFishBossFish").Invoke(fishPreviewFishId); 69 | Monitor.Log($"Catching legendary fish? {isLegendary}", LogLevel.Trace); 70 | 71 | // determine value of showFish value 72 | showFish = Config.ShowUncaughtFishSpecies || caughtSpecies || (Config.AlwaysShowLegendaryFish && isLegendary); 73 | 74 | // determine value of showText value 75 | showText = Config.ShowFishName; 76 | 77 | // determine text to show if true 78 | if (showText && showFish) 79 | { 80 | textValue = fishSprite.DisplayName; // does it need translation help? (Seems to work with French already) 81 | this.Monitor.Log($"Fish name: {textValue}", LogLevel.Trace); 82 | } 83 | else 84 | { 85 | textValue = "???"; 86 | } 87 | 88 | // determine width and height of display box 89 | boxwidth = 160; boxheight = 100; 90 | 91 | textSize = Game1.dialogueFont.MeasureString(textValue) * scale; 92 | 93 | if (showText && showFish) { boxwidth = Math.Max(160, (2 * margin) + (int)textSize.X); } 94 | if (showText) { boxheight += (int)textSize.Y; } 95 | } 96 | } 97 | 98 | private void DrawFishDisplay(IList positions, BobberBar bar) 99 | { 100 | // call a function to determine the x and y coords 101 | DetermineCoordinates(positions, bar, out int x, out int y); 102 | // call a function to draw the box 103 | DrawAtCoordinates((int)(x * (Game1.options.zoomLevel / Game1.options.uiScale)), (int)(y * (Game1.options.zoomLevel / Game1.options.uiScale))); 104 | } 105 | 106 | private void DetermineCoordinates(IList positions, BobberBar bar, out int x, out int y) 107 | { 108 | // define offset values 109 | int xLeftOffset = -32; int xCenterOffset = 32; int xRightOffset = 80; 110 | int yTopOffset = -32; int yUpperOffset = 0; int yLowerOffset = -40; int yBottomOffset = -12; 111 | 112 | // determine x and y positions 113 | foreach (string position in positions) 114 | { 115 | // set the correct display coordinates from position values in order of priority 116 | switch (position) 117 | { 118 | case "Top": 119 | x = bar.xPositionOnScreen + (bar.width / 2) - (boxwidth / 2) + xCenterOffset; 120 | y = bar.yPositionOnScreen - boxheight + yTopOffset; 121 | break; 122 | 123 | case "UpperRight": 124 | x = bar.xPositionOnScreen + bar.width + xRightOffset; 125 | y = bar.yPositionOnScreen + yUpperOffset; 126 | break; 127 | 128 | case "UpperLeft": 129 | x = bar.xPositionOnScreen - boxwidth + xLeftOffset; 130 | y = bar.yPositionOnScreen + yUpperOffset; 131 | break; 132 | 133 | case "Bottom": 134 | x = bar.xPositionOnScreen + (bar.width / 2) - (boxwidth / 2) + xCenterOffset; 135 | y = bar.yPositionOnScreen + bar.height + yBottomOffset; 136 | break; 137 | 138 | case "LowerRight": 139 | x = bar.xPositionOnScreen + bar.width + xRightOffset; 140 | y = bar.yPositionOnScreen + bar.height - boxheight + yLowerOffset; 141 | break; 142 | 143 | case "LowerLeft": 144 | x = bar.xPositionOnScreen - boxwidth + xLeftOffset; 145 | y = bar.yPositionOnScreen + bar.height - boxheight + yLowerOffset; 146 | break; 147 | 148 | default: 149 | // default to UpperRight position 150 | x = bar.xPositionOnScreen + bar.width + xRightOffset; 151 | y = bar.yPositionOnScreen + yUpperOffset; 152 | //this.Monitor.Log($"Invalid position {position} listed in displayOrder.", LogLevel.Debug); 153 | break; 154 | } 155 | 156 | // if the box display is in bounds, break the loop. Otherwise proceed to alternative display position(s). 157 | if (x >= 0 && y >= 0 && x + boxwidth <= Game1.viewport.Width && y + boxheight <= Game1.viewport.Height) 158 | return; 159 | } 160 | // if no suitable location found in foreach, default to UpperRight position 161 | x = bar.xPositionOnScreen + bar.width + xRightOffset; 162 | y = bar.yPositionOnScreen + yUpperOffset; 163 | //this.Monitor.Log($"No suitable coordinate position found in displayOrder.", LogLevel.Debug); 164 | } 165 | 166 | private void DrawAtCoordinates(int x, int y) 167 | { 168 | // draw box of height and width at location 169 | IClickableMenu.drawTextureBox(Game1.spriteBatch, x, y, boxwidth, boxheight, Color.White); 170 | 171 | //Calculate draw position for fish 172 | Vector2 drawPos; 173 | if (Config.ShowTreasure && BarHasTreasure && !BarTreasureCaught) 174 | drawPos = new Vector2(x + (boxwidth / 2) - (spriteSize / 2) - (treasureSpacing / 2) + treasureOffset, y + margin); 175 | else 176 | drawPos = new Vector2(x + (boxwidth / 2) - (spriteSize / 2), y + margin); 177 | 178 | if (showFish) // if showFish, draw fish in normal color 179 | fishSprite.drawInMenu(Game1.spriteBatch, drawPos, 1.0f, 1.0f, 1.0f, StackDrawType.Hide); 180 | else // else (if not showFish), draw fish in black color 181 | fishSprite.drawInMenu(Game1.spriteBatch, drawPos, 1.0f, 1.0f, 1.0f, StackDrawType.Hide, Color.Black * 0.8f, true); 182 | 183 | if (showText) // if showText, center the text x below the fish 184 | Game1.spriteBatch.DrawString(Game1.dialogueFont, textValue, new Vector2(x + (boxwidth / 2) - ((int)textSize.X / 2), y + spriteSize + margin), Color.Black, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f); 185 | 186 | // if show treasure draw treasure with fish icon 187 | if (Config.ShowTreasure && BarHasTreasure && !BarTreasureCaught) 188 | { 189 | treasureSprite = new Object(693, 1); 190 | treasureSprite.drawInMenu(Game1.spriteBatch, new Vector2(x + (boxwidth / 2) - (spriteSize / 2) + (treasureSpacing / 2) + treasureOffset, y + margin), 1.0f, 1.0f, 1.0f, StackDrawType.Hide); 191 | } 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /FishingAssistant/README.md: -------------------------------------------------------------------------------- 1 | ## Fishing Assistant 2 | Simple ```Stardew Valley Mod``` which allows you to ```automatically catching fish``` and customize fishing mechanics by making it easier or harder even cheating it. this mod comes with additional features like fish and treasure preview, auto-stop fishing at night or low stamina, and more to come. 3 | 4 | ## Feature 5 | List of currently available mod features 6 | - Automation mode (cast fishing rod, hook fish, play mini-games, loot treasure). 7 | - Prevent players from fishing when stamina is low. 8 | - Auto-stop fishing at night (time can be changed in the config file). 9 | - Preview current fish and show you if this round has a treasure. 10 | - Configurable cheat in a config file. 11 | 12 | ## Requirements 13 | SMAPI - Stardew Modding API. [SMAPI Homepage](https://smapi.io/) | [Nexus](https://www.nexusmods.com/stardewvalley/mods/2400) 14 | 15 | ## Installation 16 | 17 | - Decompress the downloaded zip file. 18 | - Move the `FishingAssistant` folder to the `Mods` directory of your StardewValley installation. If you dont have a `Mods` directory, please ensure you have already downloaded and installed **SMAPI** from the [SMAPI Homepage](https://smapi.io/)| [Nexus](https://www.nexusmods.com/stardewvalley/mods/2400) and have launched the game at least once after its installation. 19 | 20 | ## Usage 21 | When first run, the mod includes a default configuration that disable all cheat. You can enable this mod in-game by pressing `F5` button on keyboard. Force cast power to max by pressing `F6` button on keyboard. Catch or ignore treasure by pressing `F7` button on keyboard and reload the new configuration value by pressing `F8` button on keyboard. 22 | 23 | To further tweak the fishing mechanics, you need to edit the `Mod configuration file` located in the `mod's` directory. This file is created automatically once the game has been launched at least once with the mod installed. Please refer to the **Configuration** section for details on how to further tweak the mod. 24 | 25 | ## Remarks 26 | - This mod doesn't affect the achievements you can get through Steam, fished counts will still increment as normal when a fish is reeled. 27 | - The mod is designed to be compatible with the **Multiplayer/Coop mode** of the game. **This is still being tested**, May have problems playing the animation of the characters on other players. 28 | - Bug report are available at the Nexus mod page [link](https://www.nexusmods.com/stardewvalley/mods/5815?tab=bugs). Bug fixes are based upon availability, priority and problem level 29 | 30 | ## Configuration 31 | The `configuration file` is located on the mod's folder under the StardewValley installation directory, and it's automatically created the first time the game runs with this mod installed. 32 | 33 | ### EnableModButton 34 | **Default value:** `F5` 35 | **Possible values:** See `Modding:Player Guide/Key Bindings` on Stardew valley wiki. [Here](https://stardewcommunitywiki.com/Modding:Player_Guide/Key_Bindings#Button_codes) 36 | 37 | ``` 38 | A button to enable this mod, press again for disable. 39 | ``` 40 | 41 | ### CastPowerButton 42 | **Default value:** `F6` 43 | **Possible values:** See `Modding:Player Guide/Key Bindings` on Stardew valley wiki. [Here](https://stardewcommunitywiki.com/Modding:Player_Guide/Key_Bindings#Button_codes) 44 | 45 | ``` 46 | A button for force fishing rod cast power to max, press again for disable. 47 | ``` 48 | 49 | ### CatchTreasureButton 50 | **Default value:** `F7` 51 | **Possible values:** See `Modding:Player Guide/Key Bindings` on Stardew valley wiki. [Here](https://stardewcommunitywiki.com/Modding:Player_Guide/Key_Bindings#Button_codes) 52 | 53 | ``` 54 | A button for toggle catch or ignore treasure when treasure appeared. 55 | ``` 56 | 57 | ### OpenMenuButton 58 | **Default value:** `F8` 59 | **Possible values:** See `Modding:Player Guide/Key Bindings` on Stardew valley wiki. [Here](https://stardewcommunitywiki.com/Modding:Player_Guide/Key_Bindings#Button_codes) 60 | 61 | ``` 62 | A button for open in-game config menu. 63 | ``` 64 | 65 | ### AlwaysPerfect 66 | **Default value:** `false` 67 | **Possible values:** `true | false` 68 | 69 | ``` 70 | Making the result of fishing always perfect, even if it wasn't. 71 | ``` 72 | 73 | ### AlwaysFindTreasure 74 | **Default value:** `false` 75 | **Possible values:** `true | false` 76 | 77 | ``` 78 | Causing you to find a treasure every time you fish. 79 | ``` 80 | 81 | ### InstantFishBite 82 | **Default value:** `false` 83 | **Possible values:** `true | false` 84 | 85 | ``` 86 | Fish will instantly bite when you throw a fishing rod. 87 | ``` 88 | 89 | ### InstantCatchFish 90 | **Default value:** `false` 91 | **Possible values:** `true | false` 92 | 93 | ``` 94 | Instantly catch fish when fish hooked. 95 | ``` 96 | 97 | ### InstantCatchTreasure 98 | **Default value:** `false` 99 | **Possible values:** `true | false` 100 | 101 | ``` 102 | Instantly catch treasure when treasure appeared. 103 | ``` 104 | 105 | ### InfiniteTackle 106 | **Default value:** `false` 107 | **Possible values:** `true | false` 108 | 109 | ``` 110 | Make your fishing tackle last long forever. 111 | ``` 112 | 113 | ### InfiniteBait 114 | **Default value:** `false` 115 | **Possible values:** `true | false` 116 | 117 | ``` 118 | Make your fishing bait last long forever. 119 | ``` 120 | 121 | ### FishDifficultyMultiplier 122 | **Default value:** `1` 123 | **Possible values:** `0 - 1 to lower difficulty, or more than 1 to increase it.` 124 | 125 | ``` 126 | A multiplier applied to the fish difficulty. 127 | ``` 128 | 129 | ### FishDifficultyAdditive 130 | **Default value:** `0` 131 | **Possible values:** `< 0 to lower difficulty, or > 0 to increase it.` 132 | 133 | ``` 134 | A value added to the fish difficulty. 135 | ``` 136 | 137 | ### AlwaysCatchDoubleFish 138 | **Default value:** `false` 139 | **Possible values:** `true | false` 140 | 141 | ``` 142 | Make you catch double fish every time. 143 | ``` 144 | 145 | ### ModStatusDisplayPosition 146 | **Default value:** `Left` 147 | **Possible values:** `Left | Right` 148 | 149 | ``` 150 | Position of mod status info 151 | ``` 152 | 153 | ### DisplayFishInfo 154 | **Default value:** `true` 155 | **Possible values:** `true | false` 156 | 157 | ``` 158 | Should mod show fish info while catching fish? 159 | ``` 160 | 161 | ### FishInfoDisplayPosition 162 | **Default value:** `"UpperRight"` 163 | **Possible values:** `"Top" | "UpperRight" | "UpperLeft" | "Bottom" | "LowerRight" | "LowerLeft"` 164 | 165 | ``` 166 | Position to display fish info when playing a fishing minigame. 167 | ``` 168 | 169 | ### ShowFishName 170 | **Default value:** `true` 171 | **Possible values:** `true | false` 172 | 173 | ``` 174 | Shows the text of the fish name under the icon. 175 | ``` 176 | 177 | ### ShowTreasure 178 | **Default value:** `true` 179 | **Possible values:** `true | false` 180 | 181 | ``` 182 | Show treasure icon with fish info. 183 | ``` 184 | 185 | ### ShowUncaughtFishSpecies 186 | **Default value:** `false` 187 | **Possible values:** `true | false` 188 | 189 | ``` 190 | Show a preview for all fish species, even ones you have never caught. 191 | ``` 192 | 193 | ### AlwaysShowLegendaryFish 194 | **Default value:** `false` 195 | **Possible values:** `true | false` 196 | 197 | ``` 198 | Show a preview for legendary fish. 199 | ``` 200 | 201 | ### AddAutoHookEnchantment 202 | **Default value:** `false` 203 | **Possible values:** `true | false` 204 | 205 | ``` 206 | Add Auto-Hook enchantment to fishing rod. 207 | ``` 208 | 209 | ### AddEfficientEnchantment 210 | **Default value:** `false` 211 | **Possible values:** `true | false` 212 | 213 | ``` 214 | Add Efficient enchantment to fishing rod. 215 | ``` 216 | 217 | ### AddMasterEnchantment 218 | **Default value:** `false` 219 | **Possible values:** `true | false` 220 | 221 | ``` 222 | Add Master enchantment to fishing rod. 223 | ``` 224 | 225 | ### AddPreservingEnchantment 226 | **Default value:** `false` 227 | **Possible values:** `true | false` 228 | 229 | ``` 230 | Add Preserving enchantment to fishing rod. 231 | ``` 232 | 233 | ### OnlyAddEnchantmentWhenHeld 234 | **Default value:** `false` 235 | **Possible values:** `true | false` 236 | 237 | ``` 238 | Remove all enchantment when fishing rod is switching out. 239 | ``` 240 | 241 | ### AutomationPresets 242 | **Default value:** `"Default"` 243 | **Possible values:** `"Default" | "Custom" | "Disable"` 244 | 245 | ``` 246 | What automation preset that this mod should use. 247 | ``` 248 | 249 | ### AutoCastFishingRod 250 | **Default value:** `true` 251 | **Possible values:** `true | false` 252 | 253 | ``` 254 | Should mod do auto cast fishing rod. 255 | ``` 256 | 257 | ### AutoHookFish 258 | **Default value:** `true` 259 | **Possible values:** `true | false` 260 | 261 | ``` 262 | Should mod do auto hook fish. 263 | ``` 264 | 265 | ### AutoPlayMiniGame 266 | **Default value:** `true` 267 | **Possible values:** `true | false` 268 | 269 | ``` 270 | Should mod do auto play fishing mini-game. 271 | ``` 272 | 273 | ### AutoClosePopup 274 | **Default value:** `true` 275 | **Possible values:** `true | false` 276 | 277 | ``` 278 | Should mod do auto close popup. 279 | ``` 280 | 281 | ### AutoLootTreasure 282 | **Default value:** `true` 283 | **Possible values:** `true | false` 284 | 285 | ``` 286 | Should mod do auto loot treasure. 287 | ``` 288 | 289 | ### AutoAttachBait 290 | **Default value:** `false` 291 | **Possible values:** `true | false` 292 | 293 | ``` 294 | Should mod auto attach bait for you. 295 | ``` 296 | 297 | ### AutoAttachTackles 298 | **Default value:** `false` 299 | **Possible values:** `true | false` 300 | 301 | ``` 302 | Should mod auto attach tackle for you. 303 | ``` 304 | 305 | ### EnableAutoPauseFishing 306 | **Default value:** `true` 307 | **Possible values:** `true | false` 308 | 309 | ``` 310 | Should mod auto pause fishing on night. 311 | ``` 312 | 313 | ### PauseFishingTime 314 | **Default value:** `24` 315 | **Possible values:** `6 - 25` 316 | 317 | ``` 318 | Time to stop fishing. 319 | ``` 320 | 321 | ### EnableAutoEatFood 322 | **Default value:** `false` 323 | **Possible values:** `true | false` 324 | 325 | ``` 326 | Whether to eat some food if need. 327 | ``` 328 | 329 | ### EnergyPrecentToEat 330 | **Default value:** `0` 331 | **Possible values:** `0 - 95` 332 | 333 | ``` 334 | Amount of energy in percent to find food to eat. 335 | ``` 336 | 337 | ### AllowEatingFish 338 | **Default value:** `false` 339 | **Possible values:** `true | false` 340 | 341 | ``` 342 | Allow to eat fish that you caught. 343 | ``` 344 | 345 | ## Open-Source commitment 346 | This mod is **Open-Source**, which means its code is public, freely-available, and covered by an open-source license. 347 | 348 | For more details about why OpenSourcing is important on StardewValley mods see the Open-Source Wiki entry on [Stardew Valley Wiki](https://stardewvalleywiki.com/Modding:Open_source). 349 | 350 | # Licensing 351 | This mod is licensed under the GNU Lesser General Public License v3.0 License. For more information see [LGPL License details](https://github.com/KilZenc/Stardew-SMAPI-Mods/blob/main/LICENSE) 352 | -------------------------------------------------------------------------------- /FishingAssistant/Framework/SBobberBar.cs: -------------------------------------------------------------------------------- 1 | using FishingAssistant.Framework; 2 | using Microsoft.Xna.Framework; 3 | using StardewModdingAPI; 4 | using StardewValley; 5 | using StardewValley.Menus; 6 | using System; 7 | 8 | namespace FishingAssistant 9 | { 10 | internal partial class ModEntry : Mod 11 | { 12 | private BobberBar bobberBar; 13 | 14 | private float BarBobberPosition 15 | { 16 | get 17 | { 18 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 19 | return Helper.Reflection.GetField(bobberBar, KeyHelper.bobberPosition, true).GetValue(); 20 | } 21 | set 22 | { 23 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 24 | Helper.Reflection.GetField(bobberBar, KeyHelper.bobberPosition, true).SetValue(value); 25 | } 26 | } 27 | 28 | private bool BarBossFish 29 | { 30 | get 31 | { 32 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 33 | return Helper.Reflection.GetField(bobberBar, KeyHelper.bossFish, true).GetValue(); 34 | } 35 | set 36 | { 37 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 38 | Helper.Reflection.GetField(bobberBar, KeyHelper.bossFish, true).SetValue(value); 39 | } 40 | } 41 | 42 | private float BarDifficulty 43 | { 44 | get 45 | { 46 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 47 | return Helper.Reflection.GetField(bobberBar, KeyHelper.difficulty, true).GetValue(); 48 | } 49 | set 50 | { 51 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 52 | Helper.Reflection.GetField(bobberBar, KeyHelper.difficulty, true).SetValue(value); 53 | } 54 | } 55 | 56 | private float BarDistanceFromCatching 57 | { 58 | get 59 | { 60 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 61 | return Helper.Reflection.GetField(bobberBar, KeyHelper.distanceFromCatching, true).GetValue(); 62 | } 63 | set 64 | { 65 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 66 | Helper.Reflection.GetField(bobberBar, KeyHelper.distanceFromCatching, true).SetValue(value); 67 | } 68 | } 69 | 70 | private bool BarFadeOut 71 | { 72 | get 73 | { 74 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 75 | return Helper.Reflection.GetField(bobberBar, KeyHelper.fadeOut, true).GetValue(); 76 | } 77 | set 78 | { 79 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 80 | Helper.Reflection.GetField(bobberBar, KeyHelper.fadeOut, true).SetValue(value); 81 | } 82 | } 83 | 84 | private int BarFishQuality 85 | { 86 | get 87 | { 88 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 89 | return Helper.Reflection.GetField(bobberBar, KeyHelper.fishQuality, true).GetValue(); 90 | } 91 | set 92 | { 93 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 94 | Helper.Reflection.GetField(bobberBar, KeyHelper.fishQuality, true).SetValue(value); 95 | } 96 | } 97 | 98 | private int BarFishSize 99 | { 100 | get 101 | { 102 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 103 | return Helper.Reflection.GetField(bobberBar, KeyHelper.fishSize, true).GetValue(); 104 | } 105 | set 106 | { 107 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 108 | Helper.Reflection.GetField(bobberBar, KeyHelper.fishSize, true).SetValue(value); 109 | } 110 | } 111 | 112 | private bool BarFromFishPond 113 | { 114 | get 115 | { 116 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 117 | return Helper.Reflection.GetField(bobberBar, KeyHelper.fromFishPond, true).GetValue(); 118 | } 119 | set 120 | { 121 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 122 | Helper.Reflection.GetField(bobberBar, KeyHelper.fromFishPond, true).SetValue(value); 123 | } 124 | } 125 | 126 | private bool BarHasTreasure 127 | { 128 | get 129 | { 130 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 131 | return Helper.Reflection.GetField(bobberBar, KeyHelper.treasure, true).GetValue(); 132 | } 133 | set 134 | { 135 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 136 | Helper.Reflection.GetField(bobberBar, KeyHelper.treasure, true).SetValue(value); 137 | } 138 | } 139 | 140 | private int BarHeight // Hardcoded Max: 568 141 | { 142 | get 143 | { 144 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 145 | return Helper.Reflection.GetField(bobberBar, KeyHelper.bobberBarHeight, true).GetValue(); 146 | } 147 | set 148 | { 149 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 150 | Helper.Reflection.GetField(bobberBar, KeyHelper.bobberBarHeight, true).SetValue(value); 151 | } 152 | } 153 | 154 | private bool BarPerfect 155 | { 156 | get 157 | { 158 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 159 | return Helper.Reflection.GetField(bobberBar, KeyHelper.perfect, true).GetValue(); 160 | } 161 | set 162 | { 163 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 164 | Helper.Reflection.GetField(bobberBar, KeyHelper.perfect, true).SetValue(value); 165 | } 166 | } 167 | 168 | private float BarPosition 169 | { 170 | get 171 | { 172 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 173 | return Helper.Reflection.GetField(bobberBar, KeyHelper.bobberBarPos, true).GetValue(); 174 | } 175 | set 176 | { 177 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 178 | Helper.Reflection.GetField(bobberBar, KeyHelper.bobberBarPos, true).SetValue(value); 179 | } 180 | } 181 | 182 | private float BarTreasureCatchLevel 183 | { 184 | get 185 | { 186 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 187 | return Helper.Reflection.GetField(bobberBar, KeyHelper.treasureCatchLevel, true).GetValue(); 188 | } 189 | set 190 | { 191 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 192 | Helper.Reflection.GetField(bobberBar, KeyHelper.treasureCatchLevel, true).SetValue(value); 193 | } 194 | } 195 | 196 | private bool BarTreasureCaught 197 | { 198 | get 199 | { 200 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 201 | return Helper.Reflection.GetField(bobberBar, KeyHelper.treasureCaught, true).GetValue(); 202 | } 203 | set 204 | { 205 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 206 | Helper.Reflection.GetField(bobberBar, KeyHelper.treasureCaught, true).SetValue(value); 207 | } 208 | } 209 | 210 | private float BarTreasurePosition 211 | { 212 | get 213 | { 214 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 215 | return Helper.Reflection.GetField(bobberBar, KeyHelper.treasurePosition, true).GetValue(); 216 | } 217 | set 218 | { 219 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 220 | Helper.Reflection.GetField(bobberBar, KeyHelper.treasurePosition, true).SetValue(value); 221 | } 222 | } 223 | 224 | private Vector2 BarTreasureShake 225 | { 226 | set 227 | { 228 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 229 | Helper.Reflection.GetField(bobberBar, KeyHelper.treasureShake, true).SetValue(value); 230 | } 231 | } 232 | 233 | private int BarWhichFish 234 | { 235 | get 236 | { 237 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 238 | return Helper.Reflection.GetField(bobberBar, KeyHelper.whichFish, true).GetValue(); 239 | } 240 | set 241 | { 242 | if (bobberBar == null) throw new NullReferenceException(nameof(bobberBar)); 243 | Helper.Reflection.GetField(bobberBar, KeyHelper.whichFish, true).SetValue(value); 244 | } 245 | } 246 | 247 | /// Temporarily mothod for handle treasure catch error 248 | private void HandleTreasureCatchError() 249 | { 250 | treasureCatchStep = BarTreasureCatchLevel; 251 | Vector2 shakeValue = new Vector2(Game1.random.Next(-2, 3), Game1.random.Next(-2, 3)); 252 | 253 | BarTreasureCatchLevel = treasureCatchStep + 0.0135f * 2; 254 | BarTreasureShake = shakeValue; 255 | 256 | if (BarTreasureCatchLevel >= 1.0) 257 | { 258 | Game1.playSound("newArtifact"); 259 | BarTreasureCaught = true; 260 | treasureCatchStep = 0; 261 | } 262 | } 263 | 264 | /// Check if treasure chest is in bobber bar or not 265 | /// True if treasure chest is ni bobber bar 266 | private bool IsTreasureInBar() 267 | { 268 | return BarTreasurePosition + 12.0 <= BarPosition - 32.0 + BarHeight && BarTreasurePosition - 16.0 >= BarPosition - 32.0; 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /FishingAssistant/Framework/SModEntry.cs: -------------------------------------------------------------------------------- 1 | using FishingAssistant.Menu; 2 | using Microsoft.Xna.Framework; 3 | using StardewModdingAPI; 4 | using StardewModdingAPI.Events; 5 | using StardewValley; 6 | using StardewValley.Menus; 7 | using System.Collections.Generic; 8 | 9 | namespace FishingAssistant 10 | { 11 | internal partial class ModEntry : Mod 12 | { 13 | private bool modEnable; 14 | private bool hasDisableRequest; 15 | private bool hasEnableRequest; 16 | private bool isForceEnable; 17 | 18 | private int playerStandingX; 19 | private int playerStandingY; 20 | private int playerFacingDirection; 21 | 22 | private bool maxCastPower; 23 | private bool autoHook; 24 | private bool autoCatchTreasure; 25 | 26 | private bool inFishingMiniGame; 27 | 28 | private ClickableTextureComponent modEnableIcon; 29 | private ClickableTextureComponent maxCastIcon; 30 | private ClickableTextureComponent catchTreasureIcon; 31 | 32 | private int boxSize = 96; 33 | private int iconSize = 40; 34 | private int screenMargin = 8; 35 | private int spacing = 2; 36 | private int toolBarWidth = 0; 37 | 38 | public bool IsForceEnable { get => isForceEnable; set => isForceEnable = value; } 39 | 40 | public List ModDisplayPosition = new List { "Left", "Right" }; 41 | public List AutomationPresets = new List { "Default", "Custom", "Disable" }; 42 | public List FishInfoDisplayPosition = new List { "Top", "UpperRight", "UpperLeft", "Bottom", "LowerRight", "LowerLeft" }; 43 | 44 | private void Initialize(IModHelper helper) 45 | { 46 | Config = helper.ReadConfig(); 47 | I18n.Init(helper.Translation); 48 | 49 | helper.Events.GameLoop.GameLaunched += OnGameLaunched; 50 | helper.Events.GameLoop.UpdateTicked += OnUpdateTicked; 51 | helper.Events.Display.MenuChanged += OnMenuChanged; 52 | helper.Events.Display.RenderedActiveMenu += OnRenderMenu; 53 | helper.Events.Input.ButtonPressed += OnButtonPressed; 54 | helper.Events.Display.RenderingHud += OnRenderingHud; 55 | helper.Events.GameLoop.TimeChanged += OnTimeChanged; 56 | helper.Events.GameLoop.DayStarted += OnDayStarted; 57 | 58 | Monitor.Log("Initialized (press F8 to reload config)", LogLevel.Info); 59 | } 60 | 61 | private void OnEnableModButtonPressed(ButtonPressedEventArgs e) 62 | { 63 | if (e.Button == Config.EnableModButton) 64 | ToggleMod(); 65 | } 66 | 67 | private void ToggleMod() 68 | { 69 | if (modEnable) 70 | { 71 | isForceEnable = false; 72 | if (modState == ModState.Fishing || modState == ModState.Loot) 73 | { 74 | hasDisableRequest = true; 75 | Game1.playSound("coin"); 76 | AddHUDMessage(2, I18n.Hud_Message_Request_Disable()); 77 | return; 78 | } 79 | } 80 | else 81 | { 82 | if (Game1.timeOfDay >= Config.PauseFishingTime * 100 && Config.EnableAutoPauseFishing) 83 | { 84 | if (hasEnableRequest) 85 | { 86 | hasEnableRequest = false; 87 | isForceEnable = true; 88 | } 89 | else 90 | { 91 | hasEnableRequest = true; 92 | Game1.playSound("coin"); 93 | AddHUDMessage(2, I18n.Hud_Message_Force_Enable(), Config.EnableModButton.ToString()); 94 | return; 95 | } 96 | } 97 | } 98 | 99 | modEnable = isForceEnable || !modEnable; 100 | 101 | if (Game1.isFestival()) 102 | { 103 | string status = modEnable ? I18n.Mod_Status_Enable() : I18n.Mod_Status_Disable(); 104 | AddHUDMessage(2, I18n.Hud_Message_Mod_Toggle(), status); 105 | } 106 | 107 | if (modEnable) GetPlayerData(); 108 | else modState = ModState.Disable; 109 | } 110 | 111 | private void OnCastPowerButtonPressed(ButtonPressedEventArgs e) 112 | { 113 | if (e.Button == Config.CastPowerButton) 114 | { 115 | maxCastPower = !maxCastPower; 116 | if (Game1.isFestival()) 117 | { 118 | string status = maxCastPower ? I18n.Mod_Status_Enable() : I18n.Mod_Status_Disable(); 119 | AddHUDMessage(2, I18n.Hud_Message_Cast_Power(), status); 120 | } 121 | } 122 | } 123 | 124 | private void OnCatchTreasureButtonPressed(ButtonPressedEventArgs e) 125 | { 126 | if (e.Button == Config.CatchTreasureButton) 127 | { 128 | autoCatchTreasure = !autoCatchTreasure; 129 | if (Game1.isFestival()) 130 | { 131 | string status = autoCatchTreasure ? I18n.Mod_Status_Enable() : I18n.Mod_Status_Disable(); 132 | AddHUDMessage(2, I18n.Hud_Message_Catch_Treasure(), status); 133 | } 134 | } 135 | } 136 | 137 | private void OnToggleMenuButtonPressed(ButtonPressedEventArgs e) 138 | { 139 | if (e.Button == Config.OpenMenuButton && !(Game1.activeClickableMenu is FishingAssistantMenu)) 140 | { 141 | Game1.activeClickableMenu = new FishingAssistantMenu(this, Config); 142 | } 143 | } 144 | 145 | private void ReloadConfig(bool hideMessage = true) 146 | { 147 | Config = Helper.ReadConfig(); 148 | 149 | autoEatWhenLowEnergy = (Config.EnableAutoEatFood && Config.EnergyPrecentToEat == 0); 150 | 151 | switch (Config.FishInfoDisplayPosition) 152 | { 153 | case "Top": 154 | displayOrder = new List() { "Top", "UpperRight", "UpperLeft", "LowerRight" }; 155 | break; 156 | 157 | case "UpperRight": 158 | displayOrder = new List() { "UpperRight", "UpperLeft", "LowerRight" }; 159 | break; 160 | 161 | case "UpperLeft": 162 | displayOrder = new List() { "UpperLeft", "UpperRight", "LowerLeft" }; 163 | break; 164 | 165 | case "Bottom": 166 | displayOrder = new List() { "Bottom", "LowerRight", "LowerLeft", "UpperRight" }; 167 | 168 | break; 169 | 170 | case "LowerRight": 171 | displayOrder = new List() { "LowerRight", "LowerLeft", "UpperRight" }; 172 | break; 173 | 174 | case "LowerLeft": 175 | displayOrder = new List() { "LowerLeft", "LowerRight", "UpperLeft" }; 176 | break; 177 | 178 | default: 179 | displayOrder = new List() { "UpperRight", "UpperLeft", "LowerLeft" }; 180 | this.Monitor.Log($"Invalid config value {Config.FishInfoDisplayPosition} for FishDisplayPosition. Valid entries include Top, Bottom, UpperRight, UpperLeft, LowerRight and LowerLeft.", LogLevel.Warn); 181 | break; 182 | } 183 | 184 | if (!hideMessage) 185 | AddHUDMessage(2, I18n.Hud_Message_Config_Saved()); 186 | } 187 | 188 | public void ResetConfigurationToDefault() 189 | { 190 | Config.AutomationPresets = ModConfig.Defaults.AutomationPresets; 191 | Config.AutoAttachBait = ModConfig.Defaults.AutoAttachBait; 192 | Config.AutoAttachTackles = ModConfig.Defaults.AutoAttachTackles; 193 | Config.AutoCastFishingRod = ModConfig.Defaults.AutoCastFishingRod; 194 | Config.AutoHookFish = ModConfig.Defaults.AutoHookFish; 195 | Config.AutoPlayMiniGame = ModConfig.Defaults.AutoPlayMiniGame; 196 | Config.AutoClosePopup = ModConfig.Defaults.AutoClosePopup; 197 | Config.AutoLootTreasure = ModConfig.Defaults.AutoLootTreasure; 198 | Config.EnableAutoPauseFishing = ModConfig.Defaults.EnableAutoPauseFishing; 199 | Config.PauseFishingTime = ModConfig.Defaults.PauseFishingTime; 200 | Config.EnableAutoEatFood = ModConfig.Defaults.EnableAutoEatFood; 201 | Config.EnergyPrecentToEat = ModConfig.Defaults.EnergyPrecentToEat; 202 | Config.AllowEatingFish = ModConfig.Defaults.AllowEatingFish; 203 | 204 | Helper.WriteConfig(Config); 205 | ReloadConfig(); 206 | } 207 | 208 | private void GetPlayerData() 209 | { 210 | playerStandingX = Game1.player.getStandingX(); 211 | playerStandingY = Game1.player.getStandingY(); 212 | playerFacingDirection = Game1.player.getDirection() != -1 ? Game1.player.getDirection() : Game1.player.FacingDirection; 213 | } 214 | 215 | private bool IsFishingMiniGameReady() 216 | { 217 | return inFishingMiniGame && bobberBar != null; 218 | } 219 | 220 | private void AddHUDMessage(int whatType, string key, params object[] args) 221 | { 222 | if (!Context.IsWorldReady) return; 223 | Game1.addHUDMessage(new HUDMessage(string.Format(key, args), whatType)); 224 | } 225 | 226 | private void DrawModStatus() 227 | { 228 | if (Game1.eventUp) return; 229 | 230 | CalculateModPosition(out float toolBarTransparency, out int screenXPos, out int screenYPos, out int boxCenterY, out int boxCenterX); 231 | 232 | bool drawBox = DrawModStatusBox(toolBarTransparency, screenXPos, screenYPos, ref boxCenterX); 233 | 234 | DrawStatusIcon(toolBarTransparency, boxCenterY, boxCenterX, drawBox); 235 | } 236 | 237 | private void DrawStatusIcon(float toolBarTransparency, int boxCenterY, int boxCenterX, bool drawBox) 238 | { 239 | DrawIcon(modEnable, new Rectangle(20, 428, 10, 10), 240 | boxCenterX - (boxSize / 4) - spacing, 241 | boxCenterY - (boxSize / 4) - spacing, 242 | modEnableIcon, 2f); 243 | 244 | DrawIcon(maxCastPower, new Rectangle(545, 1921, 53, 19), 245 | boxCenterX - (boxSize / 4), 246 | boxCenterY + (boxSize / 4) - (iconSize / 2) + spacing, 247 | maxCastIcon, 1f); 248 | 249 | DrawIcon(autoCatchTreasure, new Rectangle(137, 412, 10, 11), 250 | boxCenterX + (boxSize / 4) - (iconSize / 2) + spacing, 251 | boxCenterY - (boxSize / 4) - spacing, 252 | catchTreasureIcon, 2f); 253 | 254 | void DrawIcon(bool value, Rectangle source, int x, int y, ClickableTextureComponent icon, float scale) 255 | { 256 | float iconTransparency = value ? 1 : drawBox ? 0.2f : 0; 257 | icon = new ClickableTextureComponent(new Rectangle(x, y, iconSize, iconSize), Game1.mouseCursors, source, scale); 258 | icon.draw(Game1.spriteBatch, Color.White * toolBarTransparency * iconTransparency, 0); 259 | } 260 | } 261 | 262 | private bool DrawModStatusBox(float toolBarTransparency, int screenXPos, int screenYPos, ref int boxCenterX) 263 | { 264 | bool drawBox = false; 265 | 266 | if (modEnable || maxCastPower || autoCatchTreasure) 267 | { 268 | if (Game1.activeClickableMenu != null) 269 | { 270 | IClickableMenu.drawTextureBox(Game1.spriteBatch, Game1.menuTexture, new Rectangle(0, 256, 60, 60), screenXPos - boxSize / 2, screenYPos, boxSize, boxSize, Color.White * toolBarTransparency, 1, drawShadow: false); 271 | boxCenterX = screenXPos; 272 | } 273 | else 274 | { 275 | if (Config.ModStatusDisplayPosition == "Left") 276 | { 277 | IClickableMenu.drawTextureBox(Game1.spriteBatch, Game1.menuTexture, new Rectangle(0, 256, 60, 60), screenXPos - toolBarWidth - boxSize, screenYPos, boxSize, boxSize, Color.White * toolBarTransparency, drawShadow: false); 278 | boxCenterX = screenXPos - toolBarWidth - boxSize / 2; 279 | } 280 | else if (Config.ModStatusDisplayPosition == "Right") 281 | { 282 | IClickableMenu.drawTextureBox(Game1.spriteBatch, Game1.menuTexture, new Rectangle(0, 256, 60, 60), screenXPos + toolBarWidth, screenYPos, boxSize, boxSize, Color.White * toolBarTransparency, 1, drawShadow: false); 283 | boxCenterX = screenXPos + toolBarWidth + boxSize / 2; 284 | } 285 | } 286 | drawBox = true; 287 | } 288 | 289 | return drawBox; 290 | } 291 | 292 | private void CalculateModPosition(out float toolBarTransparency, out int screenXPos, out int screenYPos, out int boxCenterY, out int boxCenterX) 293 | { 294 | bool alignTop = false; 295 | Point playerGlobalPos = Game1.player.GetBoundingBox().Center; 296 | Vector2 playerLocalVec = Game1.GlobalToLocal(globalPosition: new Vector2(playerGlobalPos.X, playerGlobalPos.Y), viewport: Game1.viewport); 297 | 298 | toolBarTransparency = 1; 299 | for (int i = 0; i < Game1.onScreenMenus.Count; i++) 300 | { 301 | if (Game1.onScreenMenus[i] is Toolbar toolBar) 302 | { 303 | toolBarTransparency = Helper.Reflection.GetField(toolBar, "transparency", true).GetValue(); 304 | toolBarWidth = toolBar.width / 2; 305 | break; 306 | } 307 | } 308 | 309 | if (!Game1.options.pinToolbarToggle) 310 | alignTop = playerLocalVec.Y > (float)(Game1.viewport.Height / 2 + 64); 311 | 312 | var viewport = Game1.graphics.GraphicsDevice.Viewport; 313 | screenXPos = (int)viewport.Width / 2; 314 | if (alignTop) 315 | screenYPos = screenMargin; 316 | else 317 | screenYPos = (int)viewport.Height - screenMargin - boxSize; 318 | 319 | boxCenterY = screenYPos + boxSize / 2; 320 | boxCenterX = 0; 321 | } 322 | 323 | public int GetCurrentListValue(string currentValue, List list) 324 | { 325 | return list.FindIndex(value => currentValue == value); 326 | } 327 | } 328 | } -------------------------------------------------------------------------------- /FishingAssistant/Component/AutoAction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using StardewModdingAPI; 3 | using StardewValley; 4 | using StardewValley.Menus; 5 | using StardewValley.Objects; 6 | using StardewValley.Tools; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using Object = StardewValley.Object; 11 | 12 | namespace FishingAssistant 13 | { 14 | internal partial class ModEntry : Mod 15 | { 16 | private int defaultAutoCastDelay = 60; 17 | private int defaultAutoClosePopupDelay = 30; 18 | private int defaultAutoLootDelay = 30; 19 | 20 | private int autoCastDelay; 21 | private int autoClosePopupDelay; 22 | private int autoLootDelay; 23 | 24 | private float treasureCatchStep = 0; 25 | private bool catchingTreasure; 26 | 27 | public bool autoEatWhenLowEnergy; 28 | 29 | private enum ModState { Disable, Pause, Idle, Fishing, Loot } 30 | 31 | private ModState modState; 32 | 33 | private bool CheckCurrentPreset(bool automation) 34 | { 35 | if (Config.AutomationPresets == "Custom" && automation || Config.AutomationPresets == "Default") 36 | return true; 37 | return false; 38 | } 39 | 40 | public bool AutoEatFood(bool ignoreCondition) 41 | { 42 | if (!CheckCurrentPreset(Config.EnableAutoEatFood)) 43 | return false; 44 | 45 | if (!Game1.player.isEating && RodIsNotInUse() && Config.EnableAutoEatFood && (ignoreCondition || Game1.player.Stamina <= Game1.player.MaxStamina * ((float)Config.EnergyPrecentToEat / 100))) 46 | { 47 | Object bestItem = null; 48 | foreach (Object item in Game1.player.Items.OfType()) 49 | { 50 | if (item.Edibility <= 0 || (item.Category == -4 && !Config.AllowEatingFish)) 51 | continue; 52 | 53 | if (bestItem == null || bestItem.Edibility / bestItem.salePrice() < item.Edibility / item.salePrice()) 54 | bestItem = item; 55 | } 56 | 57 | if (bestItem != null) 58 | { 59 | Game1.player.eatObject(bestItem); 60 | bestItem.Stack--; 61 | if (bestItem.Stack == 0) 62 | Game1.player.removeItemFromInventory(bestItem); 63 | 64 | AddHUDMessage(2, $"Auto eat : {bestItem.DisplayName}"); 65 | return true; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | private void AutoAttachBait() 72 | { 73 | if (!CheckCurrentPreset(Config.AutoAttachBait)) 74 | return; 75 | 76 | if (RodIsNotInUse() && !Game1.isFestival() && Config.AutoAttachBait) 77 | { 78 | IList items = Game1.player.Items; 79 | 80 | if (Config.AutoAttachBait && fishingRod.upgradeLevel >= 2) 81 | { 82 | // Check the bait slot. 83 | // Case where there is already bait attached. 84 | // We stack the same type of bait onto the existing bait attached to the fishing rod. 85 | if (fishingRod.attachments[0] != null && fishingRod.attachments[0].Stack != fishingRod.attachments[0].maximumStackSize()) 86 | { 87 | foreach (Item item in items) 88 | { 89 | // Category value for bait is -21. 90 | // Source: https://github.com/veywrn/StardewValley/blob/master/StardewValley/Item.cs 91 | if (item?.Category == -21 && item.Name.Equals(fishingRod.attachments[0].Name)) 92 | { 93 | int stackAdd = Math.Min(fishingRod.attachments[0].getRemainingStackSpace(), item.Stack); 94 | fishingRod.attachments[0].Stack += stackAdd; 95 | item.Stack -= stackAdd; 96 | 97 | if (item.Stack == 0) 98 | Game1.player.removeItemFromInventory(item); 99 | 100 | AddHUDMessage(2, I18n.Hud_Message_Auto_Attach_Bait(), item.Name); 101 | } 102 | } 103 | } 104 | // Case where there is no bait attached. 105 | // We simply attach the first instance of bait we see in the inventory onto the fishing rod. 106 | else if (fishingRod.attachments[0] == null) 107 | { 108 | foreach (Item item in items) 109 | { 110 | if (item?.Category == -21) 111 | { 112 | fishingRod.attachments[0] = (Object)item; 113 | Game1.player.removeItemFromInventory(item); 114 | AddHUDMessage(2, I18n.Hud_Message_Auto_Attach_Bait(), item.Name); 115 | break; 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | private void AutoAttachTackles() 124 | { 125 | if (!CheckCurrentPreset(Config.AutoAttachTackles)) 126 | return; 127 | 128 | if (RodIsNotInUse() && !Game1.isFestival() && Config.AutoAttachTackles) 129 | { 130 | IList items = Game1.player.Items; 131 | 132 | // Check the tackle slot. 133 | if (Config.AutoAttachTackles && fishingRod.attachments[1] == null && fishingRod.upgradeLevel == 3) 134 | { 135 | foreach (Item item in items) 136 | { 137 | // Category value for tackle is -22. 138 | // Source: https://github.com/veywrn/StardewValley/blob/master/StardewValley/Item.cs 139 | if (item?.Category == -22) 140 | { 141 | fishingRod.attachments[1] = (Object)item; 142 | Game1.player.removeItemFromInventory(item); 143 | AddHUDMessage(2, I18n.Hud_Message_Auto_Attach_Tackle(), item.Name); 144 | break; 145 | } 146 | } 147 | } 148 | } 149 | } 150 | 151 | /// Auto cast fishing rod if posible by last player facing direction. 152 | private void AutoCastFishingRod() 153 | { 154 | if (!CheckCurrentPreset(Config.AutoCastFishingRod)) 155 | return; 156 | 157 | if (RodIsNotInUse() && !Game1.player.isRidingHorse() && !Game1.isFestival() && Config.AutoCastFishingRod) 158 | { 159 | modState = ModState.Idle; 160 | if (autoCastDelay-- > 0) 161 | return; 162 | 163 | autoCastDelay = defaultAutoCastDelay; 164 | modState = ModState.Fishing; 165 | 166 | //prevent player from exhausted 167 | if (Game1.player.stamina <= (8.0f - (Game1.player.fishingLevel * 0.1f))) 168 | { 169 | if (autoEatWhenLowEnergy && AutoEatFood(ignoreCondition: true)) 170 | return; 171 | 172 | AddHUDMessage(3, I18n.Hud_Message_Low_Stamina()); 173 | modEnable = false; 174 | modState = ModState.Disable; 175 | Game1.playSound("coin"); 176 | return; 177 | } 178 | 179 | if (Game1.player.isInventoryFull()) 180 | { 181 | modEnable = false; 182 | modState = ModState.Disable; 183 | Game1.playSound("coin"); 184 | AddHUDMessage(3, I18n.Hud_Message_Inventory_Full()); 185 | return; 186 | } 187 | 188 | Game1.player.faceDirection(playerFacingDirection); 189 | fishingRod.beginUsing(Game1.currentLocation, playerStandingX, playerStandingY, Game1.player); 190 | modState = ModState.Idle; 191 | } 192 | } 193 | 194 | /// Auto hook fish when fish bite. 195 | private void AutoHook() 196 | { 197 | if (!CheckCurrentPreset(Config.AutoHookFish)) 198 | return; 199 | 200 | //Do nothing if fishing rod already had auto hook enchantment 201 | if (fishingRod.hasEnchantmentOfType()) 202 | return; 203 | 204 | //apply auto hook if mod enable 205 | if (IsRodCasting && Config.AutoHookFish) 206 | autoHook = true; 207 | 208 | // auto hook fish when fish bite 209 | if (autoHook && IsRodCanHook() && !Game1.isFestival()) 210 | { 211 | autoHook = false; 212 | fishingRod.timePerBobberBob = 1f; 213 | fishingRod.timeUntilFishingNibbleDone = FishingRod.maxTimeToNibble; 214 | fishingRod.DoFunction(Game1.player.currentLocation, (int)fishingRod.bobber.X, (int)fishingRod.bobber.Y, 1, Game1.player); 215 | Rumble.rumble(0.95f, 200f); 216 | } 217 | } 218 | 219 | /// Auto play fishing minigame. 220 | private void AutoPlayMiniGame() 221 | { 222 | if (!CheckCurrentPreset(Config.AutoPlayMiniGame)) 223 | return; 224 | 225 | if (Config.AutoPlayMiniGame) 226 | { 227 | modState = ModState.Fishing; 228 | 229 | float fishPos = BarBobberPosition; 230 | float barPosMax = 568 - (BarHeight / 2); 231 | float barPosMin = (BarHeight / 2); 232 | 233 | if (BarDistanceFromCatching >= 1.0f) 234 | { 235 | EndMiniGame(); 236 | } 237 | else if (autoCatchTreasure && BarHasTreasure && !BarTreasureCaught && (BarDistanceFromCatching > 0.85 || catchingTreasure)) 238 | { 239 | catchingTreasure = true; 240 | fishPos = BarTreasurePosition; 241 | 242 | if (!IsTreasureInBar()) 243 | HandleTreasureCatchError(); 244 | } 245 | else if (catchingTreasure && BarDistanceFromCatching < 0.15) 246 | { 247 | catchingTreasure = false; 248 | fishPos = BarBobberPosition; 249 | } 250 | fishPos += 25; 251 | 252 | if (fishPos < barPosMin) 253 | fishPos = barPosMin; 254 | else if (fishPos > barPosMax) 255 | fishPos = barPosMax; 256 | 257 | BarPosition = fishPos - (BarHeight / 2); 258 | } 259 | } 260 | 261 | /// Auto close fish popup when fishing minigame finish 262 | private void AutoCloseFishPopup() 263 | { 264 | if (!CheckCurrentPreset(Config.AutoClosePopup)) 265 | return; 266 | 267 | if (IsRodShowingFish() && !Game1.isFestival() && Config.AutoClosePopup) 268 | { 269 | if (autoClosePopupDelay-- > 0) 270 | return; 271 | 272 | autoClosePopupDelay = defaultAutoClosePopupDelay; 273 | modState = ModState.Fishing; 274 | 275 | Farmer player = Game1.player; 276 | 277 | player.currentLocation.localSound("coin"); 278 | if (!IsRodTreasureCaught) 279 | { 280 | Object item2 = null; 281 | if (RodItemCategory == "Object") 282 | { 283 | item2 = new Object(RodWhichFish, 1, isRecipe: false, -1, RodFishQuality); 284 | if (RodWhichFish == GameLocation.CAROLINES_NECKLACE_ITEM) 285 | { 286 | item2.questItem.Value = true; 287 | } 288 | if (RodWhichFish == 79 || RodWhichFish == 842) 289 | { 290 | item2 = player.currentLocation.tryToCreateUnseenSecretNote(fishingRod.getLastFarmerToUse()); 291 | if (item2 == null) 292 | { 293 | return; 294 | } 295 | } 296 | if (RodCaughtDoubleFish) 297 | { 298 | item2.Stack = 2; 299 | } 300 | } 301 | else if (RodItemCategory == "Furniture") 302 | { 303 | item2 = new Furniture(RodWhichFish, Vector2.Zero); 304 | } 305 | bool cachedFromFishPond = RodFromFishPond; 306 | fishingRod.getLastFarmerToUse().completelyStopAnimatingOrDoingAction(); 307 | fishingRod.doneFishing(fishingRod.getLastFarmerToUse(), !cachedFromFishPond); 308 | if (!Game1.isFestival() && !cachedFromFishPond && RodItemCategory == "Object" && Game1.player.team.specialOrders != null) 309 | { 310 | foreach (SpecialOrder order2 in Game1.player.team.specialOrders) 311 | { 312 | if (order2.onFishCaught != null) 313 | { 314 | order2.onFishCaught(Game1.player, item2); 315 | } 316 | } 317 | } 318 | if (!Game1.isFestival() && !fishingRod.getLastFarmerToUse().addItemToInventoryBool(item2)) 319 | { 320 | Game1.activeClickableMenu = new ItemGrabMenu(new List { item2 }, this).setEssential(essential: true); 321 | } 322 | } 323 | else 324 | { 325 | IsRodFishCaught = false; 326 | IsRodShowingTreasure = true; 327 | player.UsingTool = true; 328 | int stack = 1; 329 | if (RodCaughtDoubleFish) 330 | { 331 | stack = 2; 332 | } 333 | Object item = new Object(RodWhichFish, stack, isRecipe: false, -1, RodFishQuality); 334 | if (Game1.player.team.specialOrders != null) 335 | { 336 | foreach (SpecialOrder order in Game1.player.team.specialOrders) 337 | { 338 | if (order.onFishCaught != null) 339 | { 340 | order.onFishCaught(Game1.player, item); 341 | } 342 | } 343 | } 344 | bool hadroomForfish = fishingRod.getLastFarmerToUse().addItemToInventoryBool(item); 345 | fishingRod.animations.Add(new TemporaryAnimatedSprite("LooseSprites\\Cursors", new Rectangle(64, 1920, 32, 32), 500f, 1, 0, fishingRod.getLastFarmerToUse().Position + new Vector2(-32f, -160f), flicker: false, flipped: false, (float)fishingRod.getLastFarmerToUse().getStandingY() / 10000f + 0.001f, 0f, Color.White, 4f, 0f, 0f, 0f) 346 | { 347 | motion = new Vector2(0f, -0.128f), 348 | timeBasedMotion = true, 349 | endFunction = fishingRod.openChestEndFunction, 350 | extraInfoForEndBehavior = ((!hadroomForfish) ? 1 : 0), 351 | alpha = 0f, 352 | alphaFade = -0.002f 353 | }); 354 | } 355 | 356 | if (hasDisableRequest) 357 | { 358 | hasDisableRequest = false; 359 | modEnable = false; 360 | modState = ModState.Disable; 361 | } 362 | } 363 | } 364 | 365 | /// Loot all item in treasure if treasure is caught during fishing minigame 366 | private void AutoLootTreasure() 367 | { 368 | if (!CheckCurrentPreset(Config.AutoLootTreasure)) 369 | return; 370 | 371 | if (Game1.isFestival() || !Config.AutoLootTreasure || !(Game1.activeClickableMenu is ItemGrabMenu itemGrab) || itemGrab.organizeButton != null || itemGrab.shippingBin) 372 | return; 373 | 374 | IList actualInventory = itemGrab.ItemsToGrabMenu.actualInventory; 375 | 376 | if (autoLootDelay-- > 0) 377 | return; 378 | 379 | autoLootDelay = defaultAutoLootDelay; 380 | modState = ModState.Loot; 381 | if (actualInventory.Count == 0) 382 | { 383 | itemGrab.exitThisMenu(true); 384 | if (hasDisableRequest) 385 | { 386 | hasDisableRequest = false; 387 | modEnable = false; 388 | modState = ModState.Disable; 389 | } 390 | } 391 | else 392 | { 393 | Item obj = ((IEnumerable)actualInventory).First(); 394 | if (obj != null) 395 | { 396 | if (obj.parentSheetIndex == 102) 397 | { 398 | Game1.player.foundArtifact(102, 1); 399 | Game1.playSound("fireball"); 400 | } 401 | else 402 | { 403 | Item inventory = Game1.player.addItemToInventory(obj); 404 | if (inventory != null) 405 | { 406 | modEnable = false; 407 | modState = ModState.Disable; 408 | Game1.playSound("coin"); 409 | AddHUDMessage(3, I18n.Hud_Message_Inventory_Full()); 410 | return; 411 | } 412 | else 413 | Game1.playSound("coin"); 414 | } 415 | } 416 | actualInventory.RemoveAt(0); 417 | autoLootDelay = 10; 418 | } 419 | } 420 | 421 | private void AutoStopFishingOnTime() 422 | { 423 | if (!CheckCurrentPreset(Config.EnableAutoPauseFishing)) 424 | return; 425 | 426 | if (Game1.timeOfDay >= Config.PauseFishingTime * 100 && modEnable && !isForceEnable && Config.EnableAutoPauseFishing) 427 | { 428 | Game1.playSound("coin"); 429 | AddHUDMessage(3, I18n.Hud_Message_Auto_Disable(), Game1.getTimeOfDayString(Game1.timeOfDay)); 430 | 431 | ToggleMod(); 432 | 433 | if (!Game1.IsMultiplayer) 434 | Game1.activeClickableMenu = new GameMenu(); 435 | } 436 | } 437 | } 438 | } -------------------------------------------------------------------------------- /FishingAssistant/I18n.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Diagnostics.CodeAnalysis; 4 | using StardewModdingAPI; 5 | 6 | namespace FishingAssistant 7 | { 8 | /// Get translations from the mod's i18n folder. 9 | /// This is auto-generated from the i18n/default.json file when the T4 template is saved. 10 | [GeneratedCode("TextTemplatingFileGenerator", "1.0.0")] 11 | [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Deliberately named for consistency and to match translation conventions.")] 12 | internal static class I18n 13 | { 14 | /********* 15 | ** Fields 16 | *********/ 17 | /// The mod's translation helper. 18 | private static ITranslationHelper Translations; 19 | 20 | 21 | /********* 22 | ** Public methods 23 | *********/ 24 | /// Construct an instance. 25 | /// The mod's translation helper. 26 | public static void Init(ITranslationHelper translations) 27 | { 28 | I18n.Translations = translations; 29 | } 30 | 31 | /// Get a translation equivalent to "Fishing Assistant". 32 | public static string Mod_Name() 33 | { 34 | return I18n.GetByKey("mod.name"); 35 | } 36 | 37 | /// Get a translation equivalent to "Enable". 38 | public static string Mod_Status_Enable() 39 | { 40 | return I18n.GetByKey("mod.status.enable"); 41 | } 42 | 43 | /// Get a translation equivalent to "Disable". 44 | public static string Mod_Status_Disable() 45 | { 46 | return I18n.GetByKey("mod.status.disable"); 47 | } 48 | 49 | /// Get a translation equivalent to "Left". 50 | public static string General_Position_Left() 51 | { 52 | return I18n.GetByKey("general.position.left"); 53 | } 54 | 55 | /// Get a translation equivalent to "Right". 56 | public static string General_Position_Right() 57 | { 58 | return I18n.GetByKey("general.position.right"); 59 | } 60 | 61 | /// Get a translation equivalent to "Top". 62 | public static string General_Position_Top() 63 | { 64 | return I18n.GetByKey("general.position.top"); 65 | } 66 | 67 | /// Get a translation equivalent to "Bottom". 68 | public static string General_Position_Bottom() 69 | { 70 | return I18n.GetByKey("general.position.bottom"); 71 | } 72 | 73 | /// Get a translation equivalent to "UpperRight". 74 | public static string General_Position_UpperRight() 75 | { 76 | return I18n.GetByKey("general.position.upper-right"); 77 | } 78 | 79 | /// Get a translation equivalent to "UpperLeft". 80 | public static string General_Position_UpperLeft() 81 | { 82 | return I18n.GetByKey("general.position.upper-left"); 83 | } 84 | 85 | /// Get a translation equivalent to "LowerRight". 86 | public static string General_Position_LowerRight() 87 | { 88 | return I18n.GetByKey("general.position.lower-right"); 89 | } 90 | 91 | /// Get a translation equivalent to "LowerLeft". 92 | public static string General_Position_LowerLeft() 93 | { 94 | return I18n.GetByKey("general.position.lower-left"); 95 | } 96 | 97 | /// Get a translation equivalent to "{0} Fishing Assistant.". 98 | public static string Hud_Message_Mod_Toggle() 99 | { 100 | return I18n.GetByKey("hud.message.mod.toggle"); 101 | } 102 | 103 | /// Get a translation equivalent to "{0} Max cast power.". 104 | public static string Hud_Message_Cast_Power() 105 | { 106 | return I18n.GetByKey("hud.message.cast.power"); 107 | } 108 | 109 | /// Get a translation equivalent to "{0} Catch treasure.". 110 | public static string Hud_Message_Catch_Treasure() 111 | { 112 | return I18n.GetByKey("hud.message.catch.treasure"); 113 | } 114 | 115 | /// Get a translation equivalent to "Mod will disable after task is finished.". 116 | public static string Hud_Message_Request_Disable() 117 | { 118 | return I18n.GetByKey("hud.message.request.disable"); 119 | } 120 | 121 | /// Get a translation equivalent to "Pressed {0} again to force enable.". 122 | public static string Hud_Message_Force_Enable() 123 | { 124 | return I18n.GetByKey("hud.message.force.enable"); 125 | } 126 | 127 | /// Get a translation equivalent to "Low energy.". 128 | public static string Hud_Message_Low_Stamina() 129 | { 130 | return I18n.GetByKey("hud.message.low.stamina"); 131 | } 132 | 133 | /// Get a translation equivalent to "Inventory full.". 134 | public static string Hud_Message_Inventory_Full() 135 | { 136 | return I18n.GetByKey("hud.message.inventory.full"); 137 | } 138 | 139 | /// Get a translation equivalent to "Auto disable mod on {0}". 140 | public static string Hud_Message_Auto_Disable() 141 | { 142 | return I18n.GetByKey("hud.message.auto.disable"); 143 | } 144 | 145 | /// Get a translation equivalent to "Saving new config". 146 | public static string Hud_Message_Config_Saved() 147 | { 148 | return I18n.GetByKey("hud.message.config.saved"); 149 | } 150 | 151 | /// Get a translation equivalent to "Auto attach bait : {0}". 152 | public static string Hud_Message_Auto_Attach_Bait() 153 | { 154 | return I18n.GetByKey("hud.message.auto.attach.bait"); 155 | } 156 | 157 | /// Get a translation equivalent to "Auto attach tackle : {0}". 158 | public static string Hud_Message_Auto_Attach_Tackle() 159 | { 160 | return I18n.GetByKey("hud.message.auto.attach.tackle"); 161 | } 162 | 163 | /// Get a translation equivalent to "Control". 164 | public static string Menu_Config_Header_Control() 165 | { 166 | return I18n.GetByKey("menu.config.header.control"); 167 | } 168 | 169 | /// Get a translation equivalent to "Toggle Mod Button". 170 | public static string Menu_Config_Button_Toggle_Mod() 171 | { 172 | return I18n.GetByKey("menu.config.button.toggle.mod"); 173 | } 174 | 175 | /// Get a translation equivalent to "Cast power button". 176 | public static string Menu_Config_Button_Max_Cast_Power() 177 | { 178 | return I18n.GetByKey("menu.config.button.max.cast.power"); 179 | } 180 | 181 | /// Get a translation equivalent to "Catch treasure button". 182 | public static string Menu_Config_Button_Catch_Treasure() 183 | { 184 | return I18n.GetByKey("menu.config.button.catch.treasure"); 185 | } 186 | 187 | /// Get a translation equivalent to "Open menu button". 188 | public static string Menu_Config_Button_Open_Menu() 189 | { 190 | return I18n.GetByKey("menu.config.button.open.menu"); 191 | } 192 | 193 | /// Get a translation equivalent to "Reset". 194 | public static string Menu_Config_Reset_Control() 195 | { 196 | return I18n.GetByKey("menu.config.reset.control"); 197 | } 198 | 199 | /// Get a translation equivalent to "Press New Key...". 200 | public static string Menu_Config_PressNewKey() 201 | { 202 | return I18n.GetByKey("menu.config.press-new-key"); 203 | } 204 | 205 | /// Get a translation equivalent to "General". 206 | public static string Menu_Config_Header_General() 207 | { 208 | return I18n.GetByKey("menu.config.header.general"); 209 | } 210 | 211 | /// Get a translation equivalent to "Mod status position". 212 | public static string Menu_Config_Label_Mod_Status_Position() 213 | { 214 | return I18n.GetByKey("menu.config.label.mod.status.position"); 215 | } 216 | 217 | /// Get a translation equivalent to "Auto pause fishing". 218 | public static string Menu_Config_Label_Auto_Pause_Fishing() 219 | { 220 | return I18n.GetByKey("menu.config.label.auto.pause.fishing"); 221 | } 222 | 223 | /// Get a translation equivalent to "Pause fishing time". 224 | public static string Menu_Config_Label_Pause_Fishing_Time() 225 | { 226 | return I18n.GetByKey("menu.config.label.pause.fishing.time"); 227 | } 228 | 229 | /// Get a translation equivalent to "Auto eat food". 230 | public static string Menu_Config_Label_Auto_Eat_Food() 231 | { 232 | return I18n.GetByKey("menu.config.label.auto.eat.food"); 233 | } 234 | 235 | /// Get a translation equivalent to "Energy to eat". 236 | public static string Menu_Config_Label_Energy_To_Eat_Food() 237 | { 238 | return I18n.GetByKey("menu.config.label.energy.to.eat.food"); 239 | } 240 | 241 | /// Get a translation equivalent to "Allow eating fish". 242 | public static string Menu_Config_Label_Allow_Eat_Fish() 243 | { 244 | return I18n.GetByKey("menu.config.label.allow.eat.fish"); 245 | } 246 | 247 | /// Get a translation equivalent to "Be careful, the legendary fish may be eaten.". 248 | public static string Menu_Config_Etc_Allow_Eat_Fish_Warning() 249 | { 250 | return I18n.GetByKey("menu.config.etc.allow.eat.fish.warning"); 251 | } 252 | 253 | /// Get a translation equivalent to "When low energy". 254 | public static string Menu_Config_Etc_WhenLowEnergy() 255 | { 256 | return I18n.GetByKey("menu.config.etc.when-low-energy"); 257 | } 258 | 259 | /// Get a translation equivalent to "Automation". 260 | public static string Menu_Config_Header_Automation() 261 | { 262 | return I18n.GetByKey("menu.config.header.automation"); 263 | } 264 | 265 | /// Get a translation equivalent to "Automation preset". 266 | public static string Menu_Config_Label_Current_Preset() 267 | { 268 | return I18n.GetByKey("menu.config.label.current.preset"); 269 | } 270 | 271 | /// Get a translation equivalent to "Default". 272 | public static string Menu_Config_Label_Preset_Default() 273 | { 274 | return I18n.GetByKey("menu.config.label.preset.default"); 275 | } 276 | 277 | /// Get a translation equivalent to "Custom". 278 | public static string Menu_Config_Label_Preset_Custom() 279 | { 280 | return I18n.GetByKey("menu.config.label.preset.custom"); 281 | } 282 | 283 | /// Get a translation equivalent to "Disable". 284 | public static string Menu_Config_Label_Preset_Disable() 285 | { 286 | return I18n.GetByKey("menu.config.label.preset.disable"); 287 | } 288 | 289 | /// Get a translation equivalent to "Auto cast fishing rod". 290 | public static string Menu_Config_Label_Automation_Auto_Cast() 291 | { 292 | return I18n.GetByKey("menu.config.label.automation.auto.cast"); 293 | } 294 | 295 | /// Get a translation equivalent to "Auto hook fish". 296 | public static string Menu_Config_Label_Automation_Auto_Hook() 297 | { 298 | return I18n.GetByKey("menu.config.label.automation.auto.hook"); 299 | } 300 | 301 | /// Get a translation equivalent to "Auto play mini-game". 302 | public static string Menu_Config_Label_Automation_Auto_PlayMiniGame() 303 | { 304 | return I18n.GetByKey("menu.config.label.automation.auto.play-mini-game"); 305 | } 306 | 307 | /// Get a translation equivalent to "Auto close popup". 308 | public static string Menu_Config_Label_Automation_Auto_ClosePopup() 309 | { 310 | return I18n.GetByKey("menu.config.label.automation.auto.close-popup"); 311 | } 312 | 313 | /// Get a translation equivalent to "Auto loot treasure". 314 | public static string Menu_Config_Label_Automation_Auto_LootTreasure() 315 | { 316 | return I18n.GetByKey("menu.config.label.automation.auto.loot-treasure"); 317 | } 318 | 319 | /// Get a translation equivalent to "To enable this section. set Automation preset to Custom.". 320 | public static string Menu_Config_Description_Enable_Custom_Automation() 321 | { 322 | return I18n.GetByKey("menu.config.description.enable.custom.automation"); 323 | } 324 | 325 | /// Get a translation equivalent to "Fishing rod". 326 | public static string Menu_Config_Header_Fishing_Rod() 327 | { 328 | return I18n.GetByKey("menu.config.header.fishing.rod"); 329 | } 330 | 331 | /// Get a translation equivalent to "Auto attach bait". 332 | public static string Menu_Config_Label_Auto_Attach_Bait() 333 | { 334 | return I18n.GetByKey("menu.config.label.auto.attach.bait"); 335 | } 336 | 337 | /// Get a translation equivalent to "Auto attach tackle". 338 | public static string Menu_Config_Label_Auto_Attach_Tackle() 339 | { 340 | return I18n.GetByKey("menu.config.label.auto.attach.tackle"); 341 | } 342 | 343 | /// Get a translation equivalent to "Infinite bait". 344 | public static string Menu_Config_Label_Infinite_Bait() 345 | { 346 | return I18n.GetByKey("menu.config.label.infinite.bait"); 347 | } 348 | 349 | /// Get a translation equivalent to "Infinite tackle". 350 | public static string Menu_Config_Label_Infinite_Tackle() 351 | { 352 | return I18n.GetByKey("menu.config.label.infinite.tackle"); 353 | } 354 | 355 | /// Get a translation equivalent to "Enchantment". 356 | public static string Menu_Config_Header_Enchantment() 357 | { 358 | return I18n.GetByKey("menu.config.header.enchantment"); 359 | } 360 | 361 | /// Get a translation equivalent to "Add Auto-Hook enchantment". 362 | public static string Menu_Config_Label_Enchantment_AutoHook() 363 | { 364 | return I18n.GetByKey("menu.config.label.enchantment.auto-hook"); 365 | } 366 | 367 | /// Get a translation equivalent to "Add Efficient enchantment". 368 | public static string Menu_Config_Label_Enchantment_Efficient() 369 | { 370 | return I18n.GetByKey("menu.config.label.enchantment.efficient"); 371 | } 372 | 373 | /// Get a translation equivalent to "Add Master enchantment". 374 | public static string Menu_Config_Label_Enchantment_Master() 375 | { 376 | return I18n.GetByKey("menu.config.label.enchantment.master"); 377 | } 378 | 379 | /// Get a translation equivalent to "Add Preserving enchantment". 380 | public static string Menu_Config_Label_Enchantment_Preserving() 381 | { 382 | return I18n.GetByKey("menu.config.label.enchantment.preserving"); 383 | } 384 | 385 | /// Get a translation equivalent to "Only add enchantment when held". 386 | public static string Menu_Config_Label_Enchantment_WhenHeld() 387 | { 388 | return I18n.GetByKey("menu.config.label.enchantment.when-held"); 389 | } 390 | 391 | /// Get a translation equivalent to "Fishing mini-game". 392 | public static string Menu_Config_Header_MiniGame() 393 | { 394 | return I18n.GetByKey("menu.config.header.mini-game"); 395 | } 396 | 397 | /// Get a translation equivalent to "Always Perfect". 398 | public static string Menu_Config_Label_Always_Perfect() 399 | { 400 | return I18n.GetByKey("menu.config.label.always.perfect"); 401 | } 402 | 403 | /// Get a translation equivalent to "Always catch double fish". 404 | public static string Menu_Config_Label_Always_Catch_Double() 405 | { 406 | return I18n.GetByKey("menu.config.label.always.catch.double"); 407 | } 408 | 409 | /// Get a translation equivalent to "Always found treasure". 410 | public static string Menu_Config_Label_Always_Treasure() 411 | { 412 | return I18n.GetByKey("menu.config.label.always.treasure"); 413 | } 414 | 415 | /// Get a translation equivalent to "Instant fish bite". 416 | public static string Menu_Config_Label_Instant_Fishbite() 417 | { 418 | return I18n.GetByKey("menu.config.label.instant.fishbite"); 419 | } 420 | 421 | /// Get a translation equivalent to "Instant catch fish". 422 | public static string Menu_Config_Label_Instant_Catch_Fish() 423 | { 424 | return I18n.GetByKey("menu.config.label.instant.catch.fish"); 425 | } 426 | 427 | /// Get a translation equivalent to "Instant catch treasure". 428 | public static string Menu_Config_Label_Instant_Catch_Treasure() 429 | { 430 | return I18n.GetByKey("menu.config.label.instant.catch.treasure"); 431 | } 432 | 433 | /// Get a translation equivalent to "Fish difficulty multiplier". 434 | public static string Menu_Config_Label_Fish_Difficulty_Multiplier() 435 | { 436 | return I18n.GetByKey("menu.config.label.fish.difficulty.multiplier"); 437 | } 438 | 439 | /// Get a translation equivalent to "Fish difficulty additive". 440 | public static string Menu_Config_Label_Fish_Difficulty_Additive() 441 | { 442 | return I18n.GetByKey("menu.config.label.fish.difficulty.additive"); 443 | } 444 | 445 | /// Get a translation equivalent to "Value other than this can be set in config file.". 446 | public static string Menu_Config_Description_Fish_Difficulty() 447 | { 448 | return I18n.GetByKey("menu.config.description.fish.difficulty"); 449 | } 450 | 451 | /// Get a translation equivalent to "You can change the on-screen buttons in the config files for the Fishing Assistant and Virtual Keyboard mods.". 452 | public static string Menu_Config_Description_AndroidConfigNote() 453 | { 454 | return I18n.GetByKey("menu.config.description.android-config-note"); 455 | } 456 | 457 | /// Get a translation equivalent to "Fishing information". 458 | public static string Menu_Config_Header_Fish_Info() 459 | { 460 | return I18n.GetByKey("menu.config.header.fish.info"); 461 | } 462 | 463 | /// Get a translation equivalent to "Fish info position". 464 | public static string Menu_Config_Label_Fishinfo_Display_Position() 465 | { 466 | return I18n.GetByKey("menu.config.label.fishinfo.display.position"); 467 | } 468 | 469 | /// Get a translation equivalent to "Show fish name". 470 | public static string Menu_Config_Label_Show_Fish_Name() 471 | { 472 | return I18n.GetByKey("menu.config.label.show.fish.name"); 473 | } 474 | 475 | /// Get a translation equivalent to "Show treasure". 476 | public static string Menu_Config_Label_Show_Treasure() 477 | { 478 | return I18n.GetByKey("menu.config.label.show.treasure"); 479 | } 480 | 481 | /// Get a translation equivalent to "Show uncaught fish". 482 | public static string Menu_Config_Label_Show_Uncaught_Fish() 483 | { 484 | return I18n.GetByKey("menu.config.label.show.uncaught.fish"); 485 | } 486 | 487 | /// Get a translation equivalent to "Show legendary fish". 488 | public static string Menu_Config_Label_Show_Legendaly_Fish() 489 | { 490 | return I18n.GetByKey("menu.config.label.show.legendaly.fish"); 491 | } 492 | 493 | 494 | /********* 495 | ** Private methods 496 | *********/ 497 | /// Get a translation by its key. 498 | /// The translation key. 499 | /// An object containing token key/value pairs. This can be an anonymous object (like new { value = 42, name = "Cranberries" }), a dictionary, or a class instance. 500 | private static Translation GetByKey(string key, object tokens = null) 501 | { 502 | if (I18n.Translations == null) 503 | throw new InvalidOperationException($"You must call {nameof(I18n)}.{nameof(I18n.Init)} from the mod's entry method before reading translations."); 504 | return I18n.Translations.Get(key, tokens); 505 | } 506 | } 507 | } 508 | 509 | -------------------------------------------------------------------------------- /FishingAssistant/Menu/FishingAssistantMenu.cs: -------------------------------------------------------------------------------- 1 | using FishingAssistant.Menu.Option; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using Microsoft.Xna.Framework.Input; 5 | using StardewModdingAPI; 6 | using StardewValley; 7 | using StardewValley.BellsAndWhistles; 8 | using StardewValley.Menus; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | 13 | namespace FishingAssistant.Menu 14 | { 15 | /// An interactive menu for configuring and toggling cheats. 16 | internal class FishingAssistantMenu : IClickableMenu 17 | { 18 | /********* 19 | ** Fields 20 | *********/ 21 | private ModEntry Mod; 22 | private ModConfig Config; 23 | 24 | private readonly List Options = new List(); 25 | private readonly List OptionSlots = new List(); 26 | private ClickableTextureComponent UpArrow; 27 | private ClickableTextureComponent DownArrow; 28 | private ClickableTextureComponent Scrollbar; 29 | private Rectangle ScrollbarRunner; 30 | private ClickableComponent Title; 31 | private const int ItemsPerPage = 7; 32 | 33 | private string HoverText = ""; 34 | private int OptionsSlotHeld = -1; 35 | private int CurrentItemIndex; 36 | private bool IsScrolling; 37 | private bool CanClose; 38 | 39 | /// Whether the mod is running on Android. 40 | private readonly bool IsAndroid = Constants.TargetPlatform == GamePlatform.Android; 41 | 42 | /// Whether the menu was opened in the current tick. 43 | private bool JustOpened = true; 44 | 45 | /********* 46 | ** Public methods 47 | *********/ 48 | 49 | /// Construct an instance. 50 | public FishingAssistantMenu(ModEntry Mod, ModConfig Config) 51 | { 52 | this.Mod = Mod; 53 | this.Config = Config; 54 | 55 | ResetComponents(); 56 | SetOptions(); 57 | } 58 | 59 | private void ResetComponents() 60 | { 61 | // set dimensions 62 | this.width = (this.IsAndroid ? 750 : 800) + IClickableMenu.borderWidth * 2; 63 | this.height = (this.IsAndroid ? 550 : 600) + IClickableMenu.borderWidth * 2; 64 | this.xPositionOnScreen = (Game1.uiViewport.Width / 2) - (this.width /*- (int)(Game1.tileSize * 2.4f)*/) / 2; 65 | this.yPositionOnScreen = (Game1.uiViewport.Height / 2) - (this.height / 2); 66 | 67 | /*if (LocalizedContentManager.CurrentLanguageCode == LocalizedContentManager.LanguageCode.pt) 68 | this.width += 150;*/ 69 | 70 | // show close button on Android 71 | if (this.IsAndroid) 72 | this.initializeUpperRightCloseButton(); 73 | 74 | // add title 75 | this.Title = new ClickableComponent(new Rectangle(this.xPositionOnScreen + this.width / 2, this.yPositionOnScreen, Game1.tileSize * 4, Game1.tileSize), I18n.Mod_Name()); 76 | 77 | // add scroll UI 78 | int scrollbarOffset = Game1.tileSize * (this.IsAndroid ? 1 : 4) / 16; 79 | this.UpArrow = new ClickableTextureComponent("up-arrow", new Rectangle(this.xPositionOnScreen + this.width + scrollbarOffset, this.yPositionOnScreen + Game1.tileSize, 11 * Game1.pixelZoom, 12 * Game1.pixelZoom), "", "", Game1.mouseCursors, new Rectangle(421, 459, 11, 12), Game1.pixelZoom); 80 | this.DownArrow = new ClickableTextureComponent("down-arrow", new Rectangle(this.xPositionOnScreen + this.width + scrollbarOffset, this.yPositionOnScreen + this.height - Game1.tileSize, 11 * Game1.pixelZoom, 12 * Game1.pixelZoom), "", "", Game1.mouseCursors, new Rectangle(421, 472, 11, 12), Game1.pixelZoom); 81 | this.Scrollbar = new ClickableTextureComponent("scrollbar", new Rectangle(this.UpArrow.bounds.X + Game1.pixelZoom * 3, this.UpArrow.bounds.Y + this.UpArrow.bounds.Height + Game1.pixelZoom, 6 * Game1.pixelZoom, 10 * Game1.pixelZoom), "", "", Game1.mouseCursors, new Rectangle(435, 463, 6, 10), Game1.pixelZoom); 82 | this.ScrollbarRunner = new Rectangle(this.Scrollbar.bounds.X, this.UpArrow.bounds.Y + this.UpArrow.bounds.Height + Game1.pixelZoom, this.Scrollbar.bounds.Width, this.height - Game1.tileSize * 2 - this.UpArrow.bounds.Height - Game1.pixelZoom * 2); 83 | this.SetScrollBarToCurrentIndex(); 84 | 85 | // add option slots 86 | this.OptionSlots.Clear(); 87 | for (int i = 0; i < ItemsPerPage; i++) 88 | this.OptionSlots.Add(new ClickableComponent( 89 | new Rectangle(this.xPositionOnScreen + Game1.tileSize / 4, this.yPositionOnScreen + Game1.tileSize * 5 / 4 + Game1.pixelZoom + i * ((this.height - Game1.tileSize * 2) / ItemsPerPage), 90 | this.width - Game1.tileSize / 2, (this.height - Game1.tileSize * 2) / ItemsPerPage + Game1.pixelZoom), string.Concat(i))); 91 | } 92 | 93 | private void SetOptions() 94 | { 95 | this.Options.Clear(); 96 | 97 | #region Control 98 | 99 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_Control())); 100 | 101 | if (this.IsAndroid) 102 | this.AddDescription(I18n.Menu_Config_Description_AndroidConfigNote()); 103 | 104 | this.Options.Add(new KeyListenerOptionElement( 105 | label: I18n.Menu_Config_Button_Toggle_Mod(), 106 | value: Config.EnableModButton, 107 | setValue: key => Config.EnableModButton = key, 108 | slotWidth: OptionSlots[0].bounds.Width, 109 | clearToButton: SButton.F5)); 110 | 111 | this.Options.Add(new KeyListenerOptionElement( 112 | label: I18n.Menu_Config_Button_Max_Cast_Power(), 113 | value: Config.CastPowerButton, 114 | setValue: key => Config.CastPowerButton = key, 115 | slotWidth: OptionSlots[0].bounds.Width, 116 | clearToButton: SButton.F6)); 117 | 118 | this.Options.Add(new KeyListenerOptionElement( 119 | label: I18n.Menu_Config_Button_Catch_Treasure(), 120 | value: Config.CatchTreasureButton, 121 | setValue: key => Config.CatchTreasureButton = key, 122 | slotWidth: OptionSlots[0].bounds.Width, 123 | clearToButton: SButton.F7)); 124 | 125 | this.Options.Add(new KeyListenerOptionElement( 126 | label: I18n.Menu_Config_Button_Open_Menu(), 127 | value: Config.OpenMenuButton, 128 | setValue: key => Config.OpenMenuButton = key, 129 | slotWidth: OptionSlots[0].bounds.Width, 130 | clearToButton: SButton.F8)); 131 | 132 | this.Options.Add(new ButtonOptionElement( 133 | label: I18n.Menu_Config_Reset_Control(), 134 | toggle: this.ResetControls, 135 | slotWidth: OptionSlots[0].bounds.Width)); 136 | 137 | #endregion Control 138 | 139 | #region General 140 | 141 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_General())); 142 | this.Options.Add(new SliderOptionElement( 143 | label: I18n.Menu_Config_Label_Mod_Status_Position(), 144 | value: Mod.GetCurrentListValue(Config.ModStatusDisplayPosition, Mod.ModDisplayPosition), 145 | maxValue: Mod.ModDisplayPosition.Count - 1, 146 | setValue: value => Config.ModStatusDisplayPosition = Mod.ModDisplayPosition[value], 147 | format: value => MenuHelper.GetLocalizationText(Mod.ModDisplayPosition[value]))); 148 | 149 | this.Options.Add(new SliderOptionElement( 150 | label: I18n.Menu_Config_Label_Current_Preset(), 151 | value: Mod.GetCurrentListValue(Config.AutomationPresets, Mod.AutomationPresets), 152 | maxValue: Mod.AutomationPresets.Count - 1, 153 | setValue: value => 154 | { 155 | Config.AutomationPresets = Mod.AutomationPresets[value]; 156 | if (Mod.AutomationPresets[value] == "Default") 157 | Mod.ResetConfigurationToDefault(); 158 | ResetComponents(); 159 | SetOptions(); 160 | }, 161 | format: value => MenuHelper.GetLocalizationText(Mod.AutomationPresets[value]))); 162 | 163 | #endregion General 164 | 165 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_Automation())); 166 | if (Config.AutomationPresets == "Custom") 167 | { 168 | this.Options.Add(new CheckboxOptionElement( 169 | label: I18n.Menu_Config_Label_Auto_Attach_Bait(), 170 | value: Config.AutoAttachBait, 171 | setValue: value => Config.AutoAttachBait = value)); 172 | 173 | this.Options.Add(new CheckboxOptionElement( 174 | label: I18n.Menu_Config_Label_Auto_Attach_Tackle(), 175 | value: Config.AutoAttachTackles, 176 | setValue: value => Config.AutoAttachTackles = value)); 177 | 178 | this.Options.Add(new CheckboxOptionElement( 179 | label: I18n.Menu_Config_Label_Automation_Auto_Cast(), 180 | value: Config.AutoCastFishingRod, 181 | setValue: value => Config.AutoCastFishingRod = value)); 182 | 183 | this.Options.Add(new CheckboxOptionElement( 184 | label: I18n.Menu_Config_Label_Automation_Auto_Hook(), 185 | value: Config.AutoHookFish, 186 | setValue: value => Config.AutoHookFish = value, 187 | disabled: () => Config.AddAutoHookEnchantment)); 188 | 189 | this.Options.Add(new CheckboxOptionElement( 190 | label: I18n.Menu_Config_Label_Automation_Auto_PlayMiniGame(), 191 | value: Config.AutoPlayMiniGame, 192 | setValue: value => Config.AutoPlayMiniGame = value)); 193 | 194 | this.Options.Add(new CheckboxOptionElement( 195 | label: I18n.Menu_Config_Label_Automation_Auto_ClosePopup(), 196 | value: Config.AutoClosePopup, 197 | setValue: value => Config.AutoClosePopup = value)); 198 | 199 | this.Options.Add(new CheckboxOptionElement( 200 | label: I18n.Menu_Config_Label_Automation_Auto_LootTreasure(), 201 | value: Config.AutoLootTreasure, 202 | setValue: value => Config.AutoLootTreasure = value)); 203 | 204 | this.Options.Add(new CheckboxOptionElement( 205 | label: I18n.Menu_Config_Label_Auto_Eat_Food(), 206 | value: Config.EnableAutoEatFood, 207 | setValue: value => Config.EnableAutoEatFood = value)); 208 | 209 | this.Options.Add(new SliderOptionElement( 210 | label: I18n.Menu_Config_Label_Energy_To_Eat_Food(), 211 | value: Config.EnergyPrecentToEat / 5, 212 | minValue: 0, maxValue: 19, 213 | setValue: value => Config.EnergyPrecentToEat = value * 5, 214 | disabled: () => !Config.EnableAutoEatFood, 215 | format: value => value == 0 ? I18n.Menu_Config_Etc_WhenLowEnergy() : string.Format("{0}%", value * 5))); 216 | 217 | this.Options.Add(new CheckboxOptionElement( 218 | label: I18n.Menu_Config_Label_Allow_Eat_Fish(), 219 | value: Config.AllowEatingFish, 220 | setValue: value => Config.AllowEatingFish = value, 221 | disabled: () => !Config.EnableAutoEatFood)); 222 | 223 | this.AddDescription(I18n.Menu_Config_Etc_Allow_Eat_Fish_Warning()); 224 | 225 | this.Options.Add(new CheckboxOptionElement( 226 | label: I18n.Menu_Config_Label_Auto_Pause_Fishing(), 227 | value: Config.EnableAutoPauseFishing, 228 | setValue: value => Config.EnableAutoPauseFishing = value)); 229 | 230 | this.Options.Add(new SliderOptionElement( 231 | label: I18n.Menu_Config_Label_Pause_Fishing_Time(), 232 | value: Config.PauseFishingTime, 233 | minValue: 6, maxValue: 25, 234 | setValue: value => Config.PauseFishingTime = value, 235 | setValueBool: value => Mod.IsForceEnable = value, 236 | disabled: () => !Config.EnableAutoPauseFishing, 237 | format: value => Game1.getTimeOfDayString(value * 100))); 238 | } 239 | else 240 | this.AddDescription(I18n.Menu_Config_Description_Enable_Custom_Automation()); 241 | 242 | #region Fishing Rod 243 | 244 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_Fishing_Rod())); 245 | 246 | this.Options.Add(new CheckboxOptionElement( 247 | label: I18n.Menu_Config_Label_Infinite_Bait(), 248 | value: Config.InfiniteBait, 249 | setValue: value => Config.InfiniteBait = value)); 250 | 251 | this.Options.Add(new CheckboxOptionElement( 252 | label: I18n.Menu_Config_Label_Infinite_Tackle(), 253 | value: Config.InfiniteTackle, 254 | setValue: value => Config.InfiniteTackle = value)); 255 | 256 | #endregion Fishing Rod 257 | 258 | #region Enchantment 259 | 260 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_Enchantment())); 261 | this.Options.Add(new CheckboxOptionElement( 262 | label: I18n.Menu_Config_Label_Enchantment_AutoHook(), 263 | value: Config.AddAutoHookEnchantment, 264 | setValue: value => Config.AddAutoHookEnchantment = value)); 265 | 266 | this.Options.Add(new CheckboxOptionElement( 267 | label: I18n.Menu_Config_Label_Enchantment_Efficient(), 268 | value: Config.AddEfficientEnchantment, 269 | setValue: value => Config.AddEfficientEnchantment = value)); 270 | 271 | this.Options.Add(new CheckboxOptionElement( 272 | label: I18n.Menu_Config_Label_Enchantment_Master(), 273 | value: Config.AddMasterEnchantment, 274 | setValue: value => Config.AddMasterEnchantment = value)); 275 | 276 | this.Options.Add(new CheckboxOptionElement( 277 | label: I18n.Menu_Config_Label_Enchantment_Preserving(), 278 | value: Config.AddPreservingEnchantment, 279 | setValue: value => Config.AddPreservingEnchantment = value)); 280 | 281 | this.Options.Add(new CheckboxOptionElement( 282 | label: I18n.Menu_Config_Label_Enchantment_WhenHeld(), 283 | value: Config.OnlyAddEnchantmentWhenHeld, 284 | setValue: value => Config.OnlyAddEnchantmentWhenHeld = value, 285 | disabled: () => (!Config.AddAutoHookEnchantment && !Config.AddEfficientEnchantment && !Config.AddMasterEnchantment && !Config.AddPreservingEnchantment))); 286 | 287 | #endregion Enchantment 288 | 289 | #region Mini-game 290 | 291 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_MiniGame())); 292 | this.Options.Add(new CheckboxOptionElement( 293 | label: I18n.Menu_Config_Label_Always_Perfect(), 294 | value: Config.AlwaysPerfect, 295 | setValue: value => Config.AlwaysPerfect = value)); 296 | 297 | this.Options.Add(new CheckboxOptionElement( 298 | label: I18n.Menu_Config_Label_Always_Catch_Double(), 299 | value: Config.AlwaysCatchDoubleFish, 300 | setValue: value => Config.AlwaysCatchDoubleFish = value)); 301 | 302 | this.Options.Add(new CheckboxOptionElement( 303 | label: I18n.Menu_Config_Label_Always_Treasure(), 304 | value: Config.AlwaysFindTreasure, 305 | setValue: value => Config.AlwaysFindTreasure = value)); 306 | 307 | this.Options.Add(new CheckboxOptionElement( 308 | label: I18n.Menu_Config_Label_Instant_Fishbite(), 309 | value: Config.InstantFishBite, 310 | setValue: value => Config.InstantFishBite = value)); 311 | 312 | this.Options.Add(new CheckboxOptionElement( 313 | label: I18n.Menu_Config_Label_Instant_Catch_Fish(), 314 | value: Config.InstantCatchFish, 315 | setValue: value => Config.InstantCatchFish = value)); 316 | 317 | this.Options.Add(new CheckboxOptionElement( 318 | label: I18n.Menu_Config_Label_Instant_Catch_Treasure(), 319 | value: Config.InstantCatchTreasure, 320 | setValue: value => Config.InstantCatchTreasure = value)); 321 | 322 | this.Options.Add(new SliderOptionElement( 323 | label: I18n.Menu_Config_Label_Fish_Difficulty_Multiplier(), 324 | value: (int)Config.FishDifficultyMultiplier * 10, 325 | minValue: 0, 326 | maxValue: 20, 327 | setValue: value => Config.FishDifficultyMultiplier = (float)value / 10, 328 | format: value => ((float)value / 10).ToString())); 329 | 330 | AddDescription(I18n.Menu_Config_Description_Fish_Difficulty()); 331 | 332 | this.Options.Add(new SliderOptionElement( 333 | label: I18n.Menu_Config_Label_Fish_Difficulty_Additive(), 334 | value: (int)Config.FishDifficultyAdditive * 10, 335 | minValue: -10, 336 | maxValue: 10, 337 | setValue: value => Config.FishDifficultyAdditive = (float)value, 338 | format: value => ((float)value).ToString())); 339 | 340 | AddDescription(I18n.Menu_Config_Description_Fish_Difficulty()); 341 | 342 | #endregion Mini-game 343 | 344 | #region Fish info 345 | 346 | this.Options.Add(new OptionsElement(I18n.Menu_Config_Header_Fish_Info())); 347 | this.Options.Add(new SliderOptionElement( 348 | label: I18n.Menu_Config_Label_Fishinfo_Display_Position(), 349 | value: Mod.GetCurrentListValue(Config.FishInfoDisplayPosition, Mod.FishInfoDisplayPosition), 350 | maxValue: Mod.FishInfoDisplayPosition.Count - 1, 351 | setValue: value => Config.FishInfoDisplayPosition = Mod.FishInfoDisplayPosition[value], 352 | format: value => MenuHelper.GetLocalizationText(Mod.FishInfoDisplayPosition[value]))); 353 | 354 | this.Options.Add(new CheckboxOptionElement( 355 | label: I18n.Menu_Config_Label_Show_Fish_Name(), 356 | value: Config.ShowFishName, 357 | setValue: value => Config.ShowFishName = value)); 358 | 359 | this.Options.Add(new CheckboxOptionElement( 360 | label: I18n.Menu_Config_Label_Show_Treasure(), 361 | value: Config.ShowTreasure, 362 | setValue: value => Config.ShowTreasure = value)); 363 | 364 | this.Options.Add(new CheckboxOptionElement( 365 | label: I18n.Menu_Config_Label_Show_Uncaught_Fish(), 366 | value: Config.ShowUncaughtFishSpecies, 367 | setValue: value => Config.ShowUncaughtFishSpecies = value)); 368 | 369 | this.Options.Add(new CheckboxOptionElement( 370 | label: I18n.Menu_Config_Label_Show_Legendaly_Fish(), 371 | value: Config.AlwaysShowLegendaryFish, 372 | setValue: value => Config.AlwaysShowLegendaryFish = value)); 373 | 374 | #endregion Fish info 375 | } 376 | 377 | /// Whether controller-style menus should be disabled for this menu. 378 | public override bool overrideSnappyMenuCursorMovementBan() 379 | { 380 | return true; 381 | } 382 | 383 | /// Handle the game window being resized. 384 | /// The previous window bounds. 385 | /// The new window bounds. 386 | public override void gameWindowSizeChanged(Rectangle oldBounds, Rectangle newBounds) 387 | { 388 | this.ResetComponents(); 389 | } 390 | 391 | private void SetScrollBarToCurrentIndex() 392 | { 393 | if (!this.Options.Any()) 394 | return; 395 | this.Scrollbar.bounds.Y = this.ScrollbarRunner.Height / Math.Max(1, this.Options.Count - ItemsPerPage + 1) * this.CurrentItemIndex + this.UpArrow.bounds.Bottom + Game1.pixelZoom; 396 | if (this.CurrentItemIndex != this.Options.Count - ItemsPerPage) 397 | return; 398 | this.Scrollbar.bounds.Y = this.DownArrow.bounds.Y - this.Scrollbar.bounds.Height - Game1.pixelZoom; 399 | } 400 | 401 | /// Handle the player holding the left mouse button. 402 | /// The cursor's X pixel position. 403 | /// The cursor's Y pixel position. 404 | public override void leftClickHeld(int x, int y) 405 | { 406 | if (GameMenu.forcePreventClose) 407 | return; 408 | base.leftClickHeld(x, y); 409 | if (this.IsScrolling) 410 | { 411 | int num = this.Scrollbar.bounds.Y; 412 | this.Scrollbar.bounds.Y = Math.Min(this.yPositionOnScreen + this.height - Game1.tileSize - Game1.pixelZoom * 3 - this.Scrollbar.bounds.Height, Math.Max(y, this.yPositionOnScreen + this.UpArrow.bounds.Height + Game1.pixelZoom * 5)); 413 | this.CurrentItemIndex = Math.Min(this.Options.Count - ItemsPerPage, Math.Max(0, (int)Math.Round((this.Options.Count - ItemsPerPage) * (double)((y - this.ScrollbarRunner.Y) / (float)this.ScrollbarRunner.Height)))); 414 | this.SetScrollBarToCurrentIndex(); 415 | if (num == this.Scrollbar.bounds.Y) 416 | return; 417 | Game1.soundBank.PlayCue("shiny4"); 418 | } 419 | else 420 | { 421 | if (this.OptionsSlotHeld == -1 || this.OptionsSlotHeld + this.CurrentItemIndex >= this.Options.Count) 422 | return; 423 | this.Options[this.CurrentItemIndex + this.OptionsSlotHeld].leftClickHeld(x - this.OptionSlots[this.OptionsSlotHeld].bounds.X, y - this.OptionSlots[this.OptionsSlotHeld].bounds.Y); 424 | } 425 | } 426 | 427 | /// Handle the player pressing a keyboard button. 428 | /// The key that was pressed. 429 | public override void receiveKeyPress(Keys key) 430 | { 431 | SButton openMenuKey = Config.OpenMenuButton; 432 | bool isExitKey = Game1.options.menuButton.Contains(new InputButton(key)) || (openMenuKey.TryGetKeyboard(out Keys exitKey) && key == exitKey); 433 | if (isExitKey && this.readyToClose() && this.CanClose && !GameMenu.forcePreventClose) 434 | { 435 | Game1.exitActiveMenu(); 436 | Game1.soundBank.PlayCue("bigDeSelect"); 437 | return; 438 | } 439 | 440 | this.CanClose = true; 441 | 442 | if (this.OptionsSlotHeld == -1 || this.OptionsSlotHeld + this.CurrentItemIndex >= this.Options.Count) 443 | return; 444 | this.Options[this.CurrentItemIndex + this.OptionsSlotHeld].receiveKeyPress(key); 445 | } 446 | 447 | /// Handle the player scrolling the mouse wheel. 448 | /// The scroll direction. 449 | public override void receiveScrollWheelAction(int direction) 450 | { 451 | if (GameMenu.forcePreventClose) 452 | return; 453 | base.receiveScrollWheelAction(direction); 454 | if (direction > 0 && this.CurrentItemIndex > 0) 455 | this.UpArrowPressed(); 456 | else 457 | { 458 | if (direction >= 0 || this.CurrentItemIndex >= Math.Max(0, this.Options.Count - ItemsPerPage)) 459 | return; 460 | this.DownArrowPressed(); 461 | } 462 | } 463 | 464 | /// Handle the player releasing the left mouse button. 465 | /// The cursor's X pixel position. 466 | /// The cursor's Y pixel position. 467 | public override void releaseLeftClick(int x, int y) 468 | { 469 | if (GameMenu.forcePreventClose) 470 | return; 471 | base.releaseLeftClick(x, y); 472 | if (this.OptionsSlotHeld != -1 && this.OptionsSlotHeld + this.CurrentItemIndex < this.Options.Count) 473 | this.Options[this.CurrentItemIndex + this.OptionsSlotHeld].leftClickReleased(x - this.OptionSlots[this.OptionsSlotHeld].bounds.X, y - this.OptionSlots[this.OptionsSlotHeld].bounds.Y); 474 | this.OptionsSlotHeld = -1; 475 | this.IsScrolling = false; 476 | } 477 | 478 | private void DownArrowPressed() 479 | { 480 | this.DownArrow.scale = this.DownArrow.baseScale; 481 | ++this.CurrentItemIndex; 482 | this.SetScrollBarToCurrentIndex(); 483 | } 484 | 485 | private void UpArrowPressed() 486 | { 487 | this.UpArrow.scale = this.UpArrow.baseScale; 488 | --this.CurrentItemIndex; 489 | this.SetScrollBarToCurrentIndex(); 490 | } 491 | 492 | /// Handle the player clicking the left mouse button. 493 | /// The cursor's X pixel position. 494 | /// The cursor's Y pixel position. 495 | /// Whether to play a sound if needed. 496 | public override void receiveLeftClick(int x, int y, bool playSound = true) 497 | { 498 | if (GameMenu.forcePreventClose) 499 | return; 500 | base.receiveLeftClick(x, y, playSound); 501 | 502 | if (this.DownArrow.containsPoint(x, y) && this.CurrentItemIndex < Math.Max(0, this.Options.Count - ItemsPerPage)) 503 | { 504 | this.DownArrowPressed(); 505 | Game1.soundBank.PlayCue("shwip"); 506 | } 507 | else if (this.UpArrow.containsPoint(x, y) && this.CurrentItemIndex > 0) 508 | { 509 | this.UpArrowPressed(); 510 | Game1.soundBank.PlayCue("shwip"); 511 | } 512 | else if (this.Scrollbar.containsPoint(x, y)) 513 | this.IsScrolling = true; 514 | else if (!this.DownArrow.containsPoint(x, y) && x > this.xPositionOnScreen + this.width && (x < this.xPositionOnScreen + this.width + Game1.tileSize * 2 && y > this.yPositionOnScreen) && y < this.yPositionOnScreen + this.height) 515 | { 516 | this.IsScrolling = true; 517 | this.leftClickHeld(x, y); 518 | this.releaseLeftClick(x, y); 519 | } 520 | this.CurrentItemIndex = Math.Max(0, Math.Min(this.Options.Count - ItemsPerPage, this.CurrentItemIndex)); 521 | for (int index = 0; index < this.OptionSlots.Count; ++index) 522 | { 523 | if (this.OptionSlots[index].bounds.Contains(x, y) && this.CurrentItemIndex + index < this.Options.Count && this.Options[this.CurrentItemIndex + index].bounds.Contains(x - this.OptionSlots[index].bounds.X, y - this.OptionSlots[index].bounds.Y - 5)) 524 | { 525 | this.Options[this.CurrentItemIndex + index].receiveLeftClick(x - this.OptionSlots[index].bounds.X, y - this.OptionSlots[index].bounds.Y + 5); 526 | this.OptionsSlotHeld = index; 527 | break; 528 | } 529 | } 530 | } 531 | 532 | public override void draw(SpriteBatch spriteBatch) 533 | { 534 | if (!Game1.options.showMenuBackground) 535 | spriteBatch.Draw(Game1.fadeToBlackRect, Game1.graphics.GraphicsDevice.Viewport.Bounds, Color.Black * 0.4f); 536 | 537 | Game1.drawDialogueBox(this.xPositionOnScreen, this.yPositionOnScreen, this.width, this.height, false, true); 538 | SpriteText.drawStringWithScrollCenteredAt(spriteBatch, this.Title.name, this.Title.bounds.X, this.Title.bounds.Y); 539 | for (int index = 0; index < this.OptionSlots.Count; ++index) 540 | { 541 | if (this.CurrentItemIndex >= 0 && this.CurrentItemIndex + index < this.Options.Count) 542 | this.Options[this.CurrentItemIndex + index].draw(spriteBatch, this.OptionSlots[index].bounds.X, this.OptionSlots[index].bounds.Y + 5); 543 | } 544 | if (!GameMenu.forcePreventClose) 545 | { 546 | this.UpArrow.draw(spriteBatch); 547 | this.DownArrow.draw(spriteBatch); 548 | if (this.Options.Count > ItemsPerPage) 549 | { 550 | drawTextureBox(spriteBatch, Game1.mouseCursors, new Rectangle(403, 383, 6, 6), this.ScrollbarRunner.X, this.ScrollbarRunner.Y, this.ScrollbarRunner.Width, this.ScrollbarRunner.Height, Color.White, 4f, drawShadow: false); 551 | this.Scrollbar.draw(spriteBatch); 552 | } 553 | } 554 | if (this.HoverText != "") 555 | IClickableMenu.drawHoverText(spriteBatch, this.HoverText, Game1.smallFont); 556 | 557 | if (!Game1.options.hardwareCursor && !this.IsAndroid) 558 | spriteBatch.Draw(Game1.mouseCursors, new Vector2(Game1.getOldMouseX(), Game1.getOldMouseY()), Game1.getSourceRectForStandardTileSheet(Game1.mouseCursors, Game1.options.gamepadControls ? 44 : 0, 16, 16), Color.White, 0f, Vector2.Zero, Game1.pixelZoom + Game1.dialogueButtonScale / 150f, SpriteEffects.None, 1f); 559 | 560 | // reinitialize the UI to fix Android pinch-zoom scaling issues 561 | if (this.JustOpened) 562 | { 563 | this.JustOpened = false; 564 | if (this.IsAndroid) this.ResetComponents(); 565 | } 566 | } 567 | 568 | /// Add descriptive text that may extend onto multiple lines if it's too long. 569 | /// The text to render. 570 | private void AddDescription(string text) 571 | { 572 | // get text lines 573 | int maxWidth = this.width - Game1.tileSize - 10; 574 | 575 | foreach (string originalLine in text.Replace("\r\n", "\n").Split('\n')) 576 | { 577 | string line = ""; 578 | foreach (string word in originalLine.Split(' ')) 579 | { 580 | if (line == "") 581 | line = word; 582 | else if (Game1.smallFont.MeasureString(line + " " + word).X <= maxWidth) 583 | line += " " + word; 584 | else 585 | { 586 | this.Options.Add(new DescriptionElement(line)); 587 | line = word; 588 | } 589 | } 590 | if (line != "") 591 | this.Options.Add(new DescriptionElement(line)); 592 | } 593 | } 594 | 595 | /// Reset all controls to their default value. 596 | private void ResetControls() 597 | { 598 | var config = this.Config; 599 | 600 | config.EnableModButton = ModConfig.Defaults.EnableModButton; 601 | config.CastPowerButton = ModConfig.Defaults.CastPowerButton; 602 | config.CatchTreasureButton = ModConfig.Defaults.CatchTreasureButton; 603 | config.OpenMenuButton = ModConfig.Defaults.OpenMenuButton; 604 | 605 | Game1.soundBank.PlayCue("bigDeSelect"); 606 | 607 | this.SetOptions(); 608 | } 609 | } 610 | } --------------------------------------------------------------------------------