diff --git a/Ornithologists Guild.sln b/Ornithologists Guild.sln index 70bef60..73fcc27 100644 --- a/Ornithologists Guild.sln +++ b/Ornithologists Guild.sln @@ -1,30 +1,20 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 25.0.1703.8 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrnithologistsGuild", "OrnithologistsGuild\OrnithologistsGuild.csproj", "{CEF6B6C8-5644-4667-A031-196A395EEB3E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrnithologistsGuild.CP", "OrnithologistsGuild.CP\OrnithologistsGuild.CP.csproj", "{26BFD301-759D-4C15-954B-26EA70CA7019}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {259E4387-0EC9-480C-AF1B-25568BF7477F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {259E4387-0EC9-480C-AF1B-25568BF7477F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {259E4387-0EC9-480C-AF1B-25568BF7477F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {259E4387-0EC9-480C-AF1B-25568BF7477F}.Release|Any CPU.Build.0 = Release|Any CPU {CEF6B6C8-5644-4667-A031-196A395EEB3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CEF6B6C8-5644-4667-A031-196A395EEB3E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CEF6B6C8-5644-4667-A031-196A395EEB3E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CEF6B6C8-5644-4667-A031-196A395EEB3E}.Release|Any CPU.Build.0 = Release|Any CPU - {26BFD301-759D-4C15-954B-26EA70CA7019}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {26BFD301-759D-4C15-954B-26EA70CA7019}.Debug|Any CPU.Build.0 = Debug|Any CPU - {26BFD301-759D-4C15-954B-26EA70CA7019}.Release|Any CPU.ActiveCfg = Release|Any CPU - {26BFD301-759D-4C15-954B-26EA70CA7019}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OrnithologistsGuild.CP/OrnithologistsGuild.CP.csproj b/OrnithologistsGuild.CP/OrnithologistsGuild.CP.csproj deleted file mode 100644 index 6f23a51..0000000 --- a/OrnithologistsGuild.CP/OrnithologistsGuild.CP.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net6.0 - /Users/ivy/Applications/Stardew Valley Development.app/Contents/MacOS - [CP] Ornithologists Guild - - - - - - - - - - - - - - - PreserveNewest - - - \ No newline at end of file diff --git a/OrnithologistsGuild.CP/i18n/default.json b/OrnithologistsGuild.CP/i18n/default.json index 1d84d33..9a13c35 100644 --- a/OrnithologistsGuild.CP/i18n/default.json +++ b/OrnithologistsGuild.CP/i18n/default.json @@ -87,5 +87,5 @@ "map.OrnithologistsGuild.strings.29": "\"Dirty feeders and baths make sick birds!\" the document proclaims before prescribing a rigorous cleaning regimen. \"Clean every two weeks, no less!\"", "event.OrnithologistsGuild_Kyle_SpellNSpeak.1": "~ H-E-L-L-O ~$6", "event.OrnithologistsGuild_Kyle_SpellNSpeak.2": "~ MY NAME IS K-Y-L-E ~$7", - "event.OrnithologistsGuild_Kyle_SpellNSpeak.3": "*battery fizzling out*$10", + "event.OrnithologistsGuild_Kyle_SpellNSpeak.3": "*battery fizzling out*$10" } \ No newline at end of file diff --git a/OrnithologistsGuild.CP/manifest.json b/OrnithologistsGuild.CP/manifest.json index 9fd9721..ba5ddc6 100644 --- a/OrnithologistsGuild.CP/manifest.json +++ b/OrnithologistsGuild.CP/manifest.json @@ -15,7 +15,7 @@ }, { "UniqueID": "Ivy.OrnithologistsGuild", - "MinimumVersion": "1.9.2" + "MinimumVersion": "1.9.4" } ], "RequiredForEveryone": true, diff --git a/OrnithologistsGuild/Content/ContentManager.cs b/OrnithologistsGuild/Content/ContentManager.cs index c5d75fb..80918b5 100644 --- a/OrnithologistsGuild/Content/ContentManager.cs +++ b/OrnithologistsGuild/Content/ContentManager.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI; using StardewModdingAPI.Events; +using StardewValley.GameData; namespace OrnithologistsGuild.Content { @@ -29,7 +32,87 @@ private static void Content_AssetRequested(object sender, AssetRequestedEventArg { e.LoadFromModFile("assets/binoculars.png", AssetLoadPriority.Low); } + // mail & triggers + // this can be done in content patcher ofc, kept here for i18n key. + if (e.Name.IsEquivalentTo("Data/Mail")) + { + e.Edit(Edit_DataMail); + } + if (e.Name.IsEquivalentTo("Data/TriggerActions")) + { + e.Edit(Edit_DataTriggerActions); + } } + + private static void Edit_DataMail(IAssetData data) + { + var mail = data.AsDictionary().Data; + // give lifelist + mail["Mods_Ivy_OrnithologistsGuild_Introduction"] = $"{I18n.Mail_Introduction()}%item id {Constants.LIFE_LIST_FQID} %% %item conversationTopic Ivy_OrnithologistGuild_Introduction 14 %% [#]{I18n.Mail_Introduction_Title()}"; + // 1 bird identified, mixed seeds + mail["Mods_Ivy_OrnithologistsGuild_LifeList1"] = $"{I18n.Mail_LifeList1()}%item id (O)770 5 %% [#]{I18n.Mail_LifeList1_Title()}"; + // 3 birds identified, corn + mail["Mods_Ivy_OrnithologistsGuild_LifeList3"] = $"{I18n.Mail_LifeList3()}%item id (O)270 5 %% [#]{I18n.Mail_LifeList3_Title()}"; + // 5 birds identified, sunflower seeds + mail["Mods_Ivy_OrnithologistsGuild_LifeList5"] = $"{I18n.Mail_LifeList5()}%item id (O)431 5 %% [#]{I18n.Mail_LifeList5_Title()}"; + // 7 birds identified, salmonberries + mail["Mods_Ivy_OrnithologistsGuild_LifeList7"] = $"{I18n.Mail_LifeList7()}%item id (O)296 5 %% [#]{I18n.Mail_LifeList7_Title()}"; + // all bird identified, golden egg + mail["Mods_Ivy_OrnithologistsGuild_LifeListAll"] = $"{I18n.Mail_LifeListAll()}%item id (O)928 %% %item conversationTopic Ivy_OrnithologistGuild_LifeListAll 14 %% [#]{I18n.Mail_LifeListAll_Title()}"; + } + + private static void Edit_DataTriggerActions(IAssetData data) + { + string modId = ModEntry.Instance.ModManifest.UniqueID; + var tActs = data.GetData>(); + // send intro mail once, immediately on day start/mod install + tActs.Add(new() + { + Id = $"{modId}_Mail_Introduction", + Trigger = "DayStarted", + Condition = "DATE_RANGE spring 5 1", + Action = "AddMail Current Mods_Ivy_OrnithologistsGuild_Introduction Now", + // set MarkActionApplied=true to do this only once, this is the default value + //MarkActionApplied = true + }); + // send identified count mails at end of day (for tomorrow), when goal is reached + tActs.Add(new() + { + Id = $"{modId}_Mail_LifeList1", + Trigger = "DayEnding", + Condition = "Ivy.OrnithologistsGuild_IDENTIFIED_AT_LEAST 1", + Action = "AddMail Current Mods_Ivy_OrnithologistsGuild_LifeList1", + }); + tActs.Add(new() + { + Id = $"{modId}_Mail_LifeList3", + Trigger = "DayEnding", + Condition = "Ivy.OrnithologistsGuild_IDENTIFIED_AT_LEAST 3", + Action = "AddMail Current Mods_Ivy_OrnithologistsGuild_LifeList3", + }); + tActs.Add(new() + { + Id = $"{modId}_Mail_LifeList5", + Trigger = "DayEnding", + Condition = "Ivy.OrnithologistsGuild_IDENTIFIED_AT_LEAST 5", + Action = "AddMail Current Mods_Ivy_OrnithologistsGuild_LifeList5", + }); + tActs.Add(new() + { + Id = $"{modId}_Mail_LifeList7", + Trigger = "DayEnding", + Condition = "Ivy.OrnithologistsGuild_IDENTIFIED_AT_LEAST 7", + Action = "AddMail Current Mods_Ivy_OrnithologistsGuild_LifeList7", + }); + tActs.Add(new() + { + Id = $"{modId}_Mail_LifeListAll", + Trigger = "DayEnding", + Condition = "Ivy.OrnithologistsGuild_IDENTIFIED_ALL", + Action = "AddMail Current Mods_Ivy_OrnithologistsGuild_LifeListAll", + }); + } + } } diff --git a/OrnithologistsGuild/Game/GameStateQueries.cs b/OrnithologistsGuild/Game/GameStateQueries.cs new file mode 100644 index 0000000..aaa16e4 --- /dev/null +++ b/OrnithologistsGuild/Game/GameStateQueries.cs @@ -0,0 +1,52 @@ +using OrnithologistsGuild.Content; +using StardewModdingAPI; +using StardewValley; +using StardewValley.Delegates; + +namespace OrnithologistsGuild +{ + /// + /// Add game state queries for use in conditions. + /// + public static class GameStateQueries + { + private static IMonitor Monitor; + + public static void Initialize(string modId, IMonitor monitor) + { + Monitor = monitor; + + GameStateQuery.Register($"{modId}_IDENTIFIED_AT_LEAST", IDENTIFIED_AT_LEAST); + GameStateQuery.Register($"{modId}_IDENTIFIED_ALL", IDENTIFIED_ALL); + } + + /// + /// Check if the player has identified at least (>=) N birdies + /// Usage: Ivy.OrnithologistsGuild_IDENTIFIED_AT_LEAST \ + /// /// + /// GSQ query, expected to have 2 items + /// GSQ context, unused + /// + public static bool IDENTIFIED_AT_LEAST(string[] query, GameStateQueryContext context) + { + if (!ArgUtility.TryGetInt(query, 1, out int identify, out string error, "int identify")) + { + Monitor.Log(error, LogLevel.Error); + return false; + } + return (SaveDataManager.SaveData?.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount ?? 0) >= identify; + } + + /// + /// Check if the player has identified at least (>=) N birdies + /// Usage: Ivy.OrnithologistsGuild_IDENTIFIED_ALL + /// /// + /// GSQ query, expected to have 2 items + /// GSQ context, unused + /// + public static bool IDENTIFIED_ALL(string[] query, GameStateQueryContext context) + { + return (SaveDataManager.SaveData?.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount ?? 0) >= ContentPackManager.BirdieDefs.Count; + } + } +} \ No newline at end of file diff --git a/OrnithologistsGuild/Game/Mail.cs b/OrnithologistsGuild/Game/Mail.cs deleted file mode 100644 index e808e2f..0000000 --- a/OrnithologistsGuild/Game/Mail.cs +++ /dev/null @@ -1,72 +0,0 @@ -using StardewValley; -using MailFrameworkMod; -using System.Collections.Generic; -using StardewModdingAPI.Utilities; -using OrnithologistsGuild.Content; - -namespace OrnithologistsGuild -{ - public class Mail - { - public static void Initialize() - { - MailRepository.SaveLetter(new Letter( - "Mods_Ivy_OrnithologistsGuild_Introduction", - // Adds conversation topic "Ivy_OrnithologistGuild_Introduction" for 14 days - $"{I18n.Mail_Introduction()} %item conversationTopic Ivy_OrnithologistGuild_Introduction 14 %%", - new List { ItemRegistry.Create(Constants.LIFE_LIST_FQID, 1) }, - (l) => - !Game1.player.mailReceived.Contains(l.Id) && - (SDate.From(Game1.Date) >= new SDate(5, "spring", 1) || - SaveDataManager.SaveData.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.Count > 0), - (l) => Game1.player.mailReceived.Add(l.Id))); - - MailRepository.SaveLetter(new Letter( - "Mods_Ivy_OrnithologistsGuild_LifeList1", - I18n.Mail_LifeList1(), - new List { ItemRegistry.Create("(O)770" /* Mixed Seeds */, 5) }, - (l) => - !Game1.player.mailReceived.Contains(l.Id) && - SaveDataManager.SaveData.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount > 0, - (l) => Game1.player.mailReceived.Add(l.Id))); - - MailRepository.SaveLetter(new Letter( - "Mods_Ivy_OrnithologistsGuild_LifeList3", - I18n.Mail_LifeList3(), - new List { ItemRegistry.Create("(O)270" /* Corn */, 5) }, - (l) => - !Game1.player.mailReceived.Contains(l.Id) && - SaveDataManager.SaveData.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount >= 3, - (l) => Game1.player.mailReceived.Add(l.Id))); - - MailRepository.SaveLetter(new Letter( - "Mods_Ivy_OrnithologistsGuild_LifeList5", - I18n.Mail_LifeList5(), - new List { ItemRegistry.Create("(O)431" /* Sunflower Seeds */, 5) }, - (l) => - !Game1.player.mailReceived.Contains(l.Id) && - SaveDataManager.SaveData.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount >= 5, - (l) => Game1.player.mailReceived.Add(l.Id))); - - MailRepository.SaveLetter(new Letter( - "Mods_Ivy_OrnithologistsGuild_LifeList7", - I18n.Mail_LifeList7(), - new List { ItemRegistry.Create("(O)296" /* Salmonberries */, 5) }, - (l) => - !Game1.player.mailReceived.Contains(l.Id) && - SaveDataManager.SaveData.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount >= 7, - (l) => Game1.player.mailReceived.Add(l.Id))); - - MailRepository.SaveLetter(new Letter( - "Mods_Ivy_OrnithologistsGuild_LifeListAll", - // Adds conversation topic "Ivy_OrnithologistGuild_LifeListAll" for 14 days - $"{I18n.Mail_LifeListAll()} %item conversationTopic Ivy_OrnithologistGuild_LifeListAll 14 %%", - new List { ItemRegistry.Create("(O)928" /* Golden Egg */, 1) }, - (l) => - !Game1.player.mailReceived.Contains(l.Id) && - SaveDataManager.SaveData.ForPlayer(Game1.player.UniqueMultiplayerID).LifeList.IdentifiedCount >= ContentPackManager.BirdieDefs.Count, - (l) => Game1.player.mailReceived.Add(l.Id))); - } - } -} - diff --git a/OrnithologistsGuild/ModEntry.cs b/OrnithologistsGuild/ModEntry.cs index 3ddea5c..2ab3f58 100644 --- a/OrnithologistsGuild/ModEntry.cs +++ b/OrnithologistsGuild/ModEntry.cs @@ -7,8 +7,8 @@ using StardewModdingAPI; using StardewModdingAPI.Events; using StardewValley; -using StardewValley.Internal; - +using StardewValley.Internal; + namespace OrnithologistsGuild { /// The mod entry point. @@ -31,6 +31,7 @@ public override void Entry(IModHelper helper) Helper.Events.Input.ButtonPressed += Input_ButtonPressed; SaveDataManager.Initialize(); + GameStateQueries.Initialize(ModManifest.UniqueID, Monitor); } private void Input_ButtonPressed(object sender, ButtonPressedEventArgs e) @@ -51,10 +52,10 @@ private void GameLoop_SaveLoaded(object sender, SaveLoadedEventArgs e) MigrateLegacyItems(); SaveDataManager.Load(); - Mail.Initialize(); NestManager.Initialize(); - if (ConfigManager.Config.LogMissingBiomes) { + if (ConfigManager.Config.LogMissingBiomes) + { foreach (var location in StardewValley.Game1.locations) { var biomes = location.GetBiomes(); @@ -80,7 +81,8 @@ private void MigrateLegacyItems() { "(O)Ivy_OrnithologistsGuild_ProBinoculars", Constants.BINOCULARS_PRO_FQID } }; - StardewValley.Internal.ForEachItemHelper.ForEachItemInWorld(delegate (in ForEachItemContext ctx) { + StardewValley.Internal.ForEachItemHelper.ForEachItemInWorld(delegate (in ForEachItemContext ctx) + { if (LegacyItemMigrations.TryGetValue(ctx.Item.QualifiedItemId, out var newQualifiedItemId)) { @@ -95,7 +97,8 @@ private void MigrateLegacyItems() try { ctx.RemoveItem(); - } catch (Exception ex2) + } + catch (Exception ex2) { Monitor.Log($"Removing broken {ctx.Item.QualifiedItemId} failed. Please manually remove the broken item.\n{ex2}", LogLevel.Error); } @@ -117,7 +120,8 @@ private void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) return Game1.player.currentLocation.GetBiomes(); }); // Player name for PowerUp e.g. `Ivy` -> `I-V-Y` - CP.RegisterToken(ModManifest, "PowerUpPlayerName", () => { + CP.RegisterToken(ModManifest, "PowerUpPlayerName", () => + { string[] GetValue(string playerName) { return new[] { string.Join('-', playerName.ToUpper().ToCharArray()) }; diff --git a/OrnithologistsGuild/ObjectPatches.Binoculars.cs b/OrnithologistsGuild/ObjectPatches.Binoculars.cs index 43847ef..de81767 100644 --- a/OrnithologistsGuild/ObjectPatches.Binoculars.cs +++ b/OrnithologistsGuild/ObjectPatches.Binoculars.cs @@ -13,7 +13,8 @@ namespace OrnithologistsGuild { - public partial class ObjectPatches { + public partial class ObjectPatches + { private record BincoularsAnimation(int Elapsed, string CurrentToolId); private const int ANIMATE_DURATION = 750; @@ -39,43 +40,39 @@ public static void drawHairAndAccesories_Postfix(FarmerRenderer __instance, Spri var sourceRect = new Rectangle(binoculars.CurrentParentTileIndex * 16, 0, 16, 16); - if (Utilities.TryGetNonPublicFieldValue(__instance, "positionOffset", out Vector2 positionOffset) && - Utilities.TryGetNonPublicFieldValue(__instance, "rotationAdjustment", out Vector2 rotationAdjustment)) + switch (facingDirection) { - switch (facingDirection) - { - case 0: - break; - case 1: - sourceRect.Offset(0, 16); - b.Draw( - binocularsTexture.Value, - position + origin + positionOffset + rotationAdjustment + - new Vector2( - featureXOffsetPerFrame[currentFrame] * 4, - 4 + featureYOffsetPerFrame[currentFrame] * 4 + __instance.heightOffset.Value + 20), - sourceRect, overrideColor, rotation, origin, 4f * scale, SpriteEffects.None, GetLayerDepth(layerDepth, accessoryLayer)); - break; - case 2: - b.Draw( - binocularsTexture.Value, - position + origin + positionOffset + rotationAdjustment + - new Vector2( - featureXOffsetPerFrame[currentFrame] * 4, - 8 + featureYOffsetPerFrame[currentFrame] * 4 + __instance.heightOffset.Value + 24), - sourceRect, overrideColor, rotation, origin, 4f * scale, SpriteEffects.None, GetLayerDepth(layerDepth, accessoryLayer)); - break; - case 3: - sourceRect.Offset(0, 16); - b.Draw( - binocularsTexture.Value, - position + origin + positionOffset + rotationAdjustment + - new Vector2( - -featureXOffsetPerFrame[currentFrame] * 4, - 4 + featureYOffsetPerFrame[currentFrame] * 4 + __instance.heightOffset.Value + 20), - sourceRect, overrideColor, rotation, origin, 4f * scale, SpriteEffects.FlipHorizontally, GetLayerDepth(layerDepth, accessoryLayer)); - break; - } + case 0: + break; + case 1: + sourceRect.Offset(0, 16); + b.Draw( + binocularsTexture.Value, + position + origin + __instance.positionOffset + __instance.rotationAdjustment + + new Vector2( + featureXOffsetPerFrame[currentFrame] * 4, + 4 + featureYOffsetPerFrame[currentFrame] * 4 + __instance.heightOffset.Value + 20), + sourceRect, overrideColor, rotation, origin, 4f * scale, SpriteEffects.None, GetLayerDepth(layerDepth, accessoryLayer)); + break; + case 2: + b.Draw( + binocularsTexture.Value, + position + origin + __instance.positionOffset + __instance.rotationAdjustment + + new Vector2( + featureXOffsetPerFrame[currentFrame] * 4, + 8 + featureYOffsetPerFrame[currentFrame] * 4 + __instance.heightOffset.Value + 24), + sourceRect, overrideColor, rotation, origin, 4f * scale, SpriteEffects.None, GetLayerDepth(layerDepth, accessoryLayer)); + break; + case 3: + sourceRect.Offset(0, 16); + b.Draw( + binocularsTexture.Value, + position + origin + __instance.positionOffset + __instance.rotationAdjustment + + new Vector2( + -featureXOffsetPerFrame[currentFrame] * 4, + 4 + featureYOffsetPerFrame[currentFrame] * 4 + __instance.heightOffset.Value + 20), + sourceRect, overrideColor, rotation, origin, 4f * scale, SpriteEffects.FlipHorizontally, GetLayerDepth(layerDepth, accessoryLayer)); + break; } } } @@ -140,7 +137,7 @@ public static void Farmer_draw_Postfix(Farmer __instance, SpriteBatch b) animation = animation with { Elapsed = animation.Elapsed + Game1.currentGameTime.ElapsedGameTime.Milliseconds }; currentAnimations[__instance.UniqueMultiplayerID] = animation; - + if (animation.CurrentToolId != __instance.CurrentTool?.QualifiedItemId) { // Animation stopped due to switching tools @@ -263,7 +260,8 @@ private static void IdentifyBirdies(GameLocation location, Farmer who, Binocular else if (nest.Stage == NestStage.EggsHatched) lines.Add($"{I18n.Items_Binoculars_NestStateEggsHatched()} {idPart}"); else if (nest.Stage == NestStage.Fledged) lines.Add($"{I18n.Items_Binoculars_NestStateFledged()} {idPart}"); - if (isIdentified) { + if (isIdentified) + { // Birdie identified var contentPack = birdieDef.ContentPackDef.ContentPack; var commonNameString = contentPack.Translation.Get($"birdie.{id}.commonName"); diff --git a/OrnithologistsGuild/OrnithologistsGuild.csproj b/OrnithologistsGuild/OrnithologistsGuild.csproj index 8feaf3d..fb80120 100644 --- a/OrnithologistsGuild/OrnithologistsGuild.csproj +++ b/OrnithologistsGuild/OrnithologistsGuild.csproj @@ -1,5 +1,4 @@ - net6.0 /Users/ivy/Applications/Stardew Valley.app/Contents/MacOS @@ -10,83 +9,24 @@ Release;Debug - - - - - AfterBuild - bash ${SolutionDir}/generate_zip.sh - ${SolutionDir}/ - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - lib\ContentPatcher.dll - - - lib\MailFrameworkMod.dll - False - + + - - PreserveNewest - - - PreserveNewest - PreserveNewest - - PreserveNewest - - - PreserveNewest - PreserveNewest - - PreserveNewest - + \ No newline at end of file diff --git a/OrnithologistsGuild/Utilities/IContentPatcherApi.cs b/OrnithologistsGuild/Utilities/IContentPatcherApi.cs new file mode 100644 index 0000000..a2ea252 --- /dev/null +++ b/OrnithologistsGuild/Utilities/IContentPatcherApi.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using StardewModdingAPI; +using StardewModdingAPI.Events; + +namespace ContentPatcher; + +/// The Content Patcher API which other mods can access. +public interface IContentPatcherAPI +{ + /********* + ** Accessors + *********/ + /// Whether the conditions API is initialized and ready for use. + /// Due to the Content Patcher lifecycle, the conditions API becomes available roughly two ticks after the event. + bool IsConditionsApiReady { get; } + + /********* + ** Methods + *********/ + /// Get a set of managed conditions which are matched against Content Patcher's internal context. + /// The manifest of the mod parsing the conditions (see in your entry class). + /// The conditions to parse, in the same format as When blocks in Content Patcher content packs. + /// The format version for which to parse conditions, used to ensure forward compatibility with future Content Patcher versions. See Format in the Content Patcher token documentation. + /// + /// The unique IDs of mods whose custom tokens to allow in the . You don't need to list the mod identified by , mods listed as a required dependency in the , or mods identified by a HasMod condition in the . + /// NOTE: this is meant to prevent mods from breaking if a player doesn't have a required mod installed. You shouldn't simply list all installed mods, and parsing conditions will still fail if a mod isn't installed regardless of the listed mod IDs. + /// + IManagedConditions ParseConditions( + IManifest manifest, + IDictionary rawConditions, + ISemanticVersion formatVersion, + string[] assumeModIds = null + ); + + /// Register a simple token. + /// The manifest of the mod defining the token (see in your entry class). + /// The token name. This only needs to be unique for your mod; Content Patcher will prefix it with your mod ID automatically, like YourName.ExampleMod/SomeTokenName. + /// A function which returns the current token value. If this returns a null or empty list, the token is considered unavailable in the current context and any patches or dynamic tokens using it are disabled. + void RegisterToken(IManifest mod, string name, Func> getValue); +} diff --git a/OrnithologistsGuild/Utilities/IManagedConditions.cs b/OrnithologistsGuild/Utilities/IManagedConditions.cs new file mode 100644 index 0000000..34a1df6 --- /dev/null +++ b/OrnithologistsGuild/Utilities/IManagedConditions.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace ContentPatcher; + +/// A set of parsed conditions linked to the Content Patcher context. These conditions are per-screen, so the result depends on the screen that's active when calling the members. +public interface IManagedConditions +{ + /********* + ** Accessors + *********/ + /// Whether the conditions were parsed successfully (regardless of whether they're in scope currently). + [MemberNotNullWhen(false, nameof(IManagedConditions.ValidationError))] + bool IsValid { get; } + + /// If is false, an error phrase indicating why the conditions failed to parse, formatted like this: 'seasonz' isn't a valid token name; must be one of <token list>. If the conditions are valid, this is null. + string ValidationError { get; } + + /// Whether the conditions' tokens are all valid in the current context. For example, this would be false if the conditions use Season and a save isn't loaded yet. + bool IsReady { get; } + + /// Whether is true, and the conditions all match in the current context. + bool IsMatch { get; } + + /// Whether may change depending on the context. For example, Season is mutable since it depends on the in-game season. HasMod is not mutable, since it can't change after the game is launched. + bool IsMutable { get; } + + /********* + ** Methods + *********/ + /// Update the conditions based on Content Patcher's current context for every active screen. It's safe to call this as often as you want, but it has no effect if the Content Patcher context hasn't changed since you last called it. + /// Returns the screens for which changed value. + IEnumerable UpdateContext(); + + /// If is false, analyze the conditions/context and get a human-readable reason phrase explaining why the conditions don't match the context. For example: conditions don't match: season. If the conditions do match, this returns null. + string GetReasonNotMatched(); +} diff --git a/OrnithologistsGuild/Utilities/Utilities.cs b/OrnithologistsGuild/Utilities/Utilities.cs index 9ab5ed3..dc993a9 100644 --- a/OrnithologistsGuild/Utilities/Utilities.cs +++ b/OrnithologistsGuild/Utilities/Utilities.cs @@ -52,7 +52,8 @@ public static string GetLocaleSeparator() { var locale = ModEntry.Instance.Helper.Translation.Locale; return $"{System.Globalization.CultureInfo.GetCultureInfo(locale).TextInfo.ListSeparator} "; - } catch + } + catch { return ", "; } @@ -69,19 +70,6 @@ public static Vector2 XY(Vector3 value) return new Vector2(value.X, value.Y); } - public static bool TryGetNonPublicFieldValue(TInstance instance, string fieldName, out TValue value) - { - FieldInfo privateFieldInfo = typeof(TInstance).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); - if (privateFieldInfo != null) - { - value = (TValue)privateFieldInfo.GetValue(instance); - return true; - } - - value = default; - return false; - } - public static Texture2D CensorTexture(Texture2D texture) { // Get the texture data diff --git a/OrnithologistsGuild/i18n/default.json b/OrnithologistsGuild/i18n/default.json index ba14f03..fbb2c85 100644 --- a/OrnithologistsGuild/i18n/default.json +++ b/OrnithologistsGuild/i18n/default.json @@ -14,12 +14,6 @@ "config.aboutKyle.title": "About Kyle", "config.aboutKyle.content": "Kyle the raven was inspired by Kyle, a rescued raven I met at The Raptors in Duncan, BC, Canada. He loves to explore, paint, and meet new people.", "config.noBreakOrJam.title": "Do not break or jam binoculars", - "mail.Introduction": "Dear @, ^^Your grandmother, an avid birder of many years, left you this. We hope you find a way to continue her tradition this spring. ^^Good luck, and watch for bird's nests! ^The Pelican Town Ornithologist's Guild", - "mail.LifeList1": "Dear @, ^^Congrats, you've identified your first bird! We think these may be of some help. ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", - "mail.LifeList3": "Dear @, ^^Three birds identified! You're on a roll. Leave some of this delicious corn for the birds! ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", - "mail.LifeList5": "Dear @, ^^Five birds identified is quite the milestone. If only these Sunflower Seeds were hulled... ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", - "mail.LifeList7": "Dear @, ^^Seven birds identified!? You're becoming an expert! Perhaps the frugivores in your life will enjoy this. ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", - "mail.LifeListAll": "Dear @, ^^You've identified every bird Pelican Town has to offer! Your grandmother left you this as a reward for a job well done. She would be very proud of the birder you've become. < ^^Until next time, ^The Pelican Town Ornithologist's Guild", "items.LifeList.title": "{{playerName}}'s Life List", "items.LifeList.totalSighted": "{{count}} {{birds}} sighted", "items.LifeList.totalIdentified": "{{count}} {{birds}} identified", @@ -44,5 +38,19 @@ "items.Binoculars.nestStateFledged": "A nest! By the looks of it, the babies recently fledged!", "items.Binoculars.lifeList": "I should open my Life List to read more about them.", "items.AntiqueBinoculars.message": "The ancient binoculars fell apart in your hands.", - "items.JojaBinoculars.message": "The cheaply made binoculars jammed when you tried to focus them." + "items.JojaBinoculars.message": "The cheaply made binoculars jammed when you tried to focus them.", + // mail data + "mail.Introduction": "Dear @, ^^Your grandmother, an avid birder of many years, left you this. We hope you find a way to continue her tradition this spring. ^^Good luck, and watch for bird's nests! ^The Pelican Town Ornithologist's Guild", + "mail.LifeList1": "Dear @, ^^Congrats, you've identified your first bird! We think these may be of some help. ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", + "mail.LifeList3": "Dear @, ^^Three birds identified! You're on a roll. Leave some of this delicious corn for the birds! ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", + "mail.LifeList5": "Dear @, ^^Five birds identified is quite the milestone. If only these Sunflower Seeds were hulled... ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", + "mail.LifeList7": "Dear @, ^^Seven birds identified!? You're becoming an expert! Perhaps the frugivores in your life will enjoy this. ^^Good luck and happy birding, ^The Pelican Town Ornithologist's Guild", + "mail.LifeListAll": "Dear @, ^^You've identified every bird Pelican Town has to offer! Your grandmother left you this as a reward for a job well done. She would be very proud of the birder you've become. < ^^Until next time, ^The Pelican Town Ornithologist's Guild", + // mail titles, appear in the mail collection screen + "mail.Introduction.title": "Ornithologist's Guild: Introduction", + "mail.LifeList1.title": "Ornithologist's Guild: First Bird", + "mail.LifeList3.title": "Ornithologist's Guild: Three Birds", + "mail.LifeList5.title": "Ornithologist's Guild: Five Birds", + "mail.LifeList7.title": "Ornithologist's Guild: Seven Birds", + "mail.LifeListAll.title": "Ornithologist's Guild: All Birds" } \ No newline at end of file diff --git a/OrnithologistsGuild/manifest.json b/OrnithologistsGuild/manifest.json index 2d82785..6ff92d1 100644 --- a/OrnithologistsGuild/manifest.json +++ b/OrnithologistsGuild/manifest.json @@ -13,9 +13,6 @@ { "UniqueID": "Pathoschild.ContentPatcher", "MinimumVersion": "2.0.0" - }, - { - "UniqueID": "DIGUS.MailFrameworkMod" } ], "RequiredForEveryone": true diff --git a/generate_zip.sh b/generate_zip.sh deleted file mode 100755 index 1cd0bd3..0000000 --- a/generate_zip.sh +++ /dev/null @@ -1,24 +0,0 @@ -#/bin/bash - -pwd=$(pwd) - -# # Move release ZIPs -mkdir /tmp/og_release -find ./*/bin/Release -iname '*Ornithologists Guild*.zip' -exec mv "{}" /tmp/og_release \; - -cd /tmp/og_release - -# # Get original filename -fn=$(find . -iname 'O*.zip' -exec echo "{}" \;) - -# # Extract release ZIPs -find . -iname '*.zip' -exec unzip "{}" -d /tmp/og_release \; -rm /tmp/og_release/*.zip -chmod -R 755 . - -# # Copy final zip -zip -r "$fn" ./* -mv "$fn" "$pwd/" - -# # Clean up -rm -rf /tmp/og_release \ No newline at end of file