public static List <string> AddNewAvailableRecipes() { List <string> newVariants = new List <string>(); for (int i = 0; i < ModEntry.ItemDefinitions.Count; ++i) { string variantKey = ModEntry.ItemDefinitions.Keys.ElementAt(i); string itemName = OutdoorPot.GetNameFromVariantKey(variantKey); if (Game1.player.craftingRecipes.ContainsKey(itemName) || string.IsNullOrEmpty(ModEntry.ItemDefinitions[variantKey].RecipeConditions) || !Game1.player.eventsSeen.Contains(ModEntry.EventRootId)) { continue; } int eventID = ModEntry.EventRootId + i; string eventKey = $"{eventID.ToString()}/{ModEntry.ItemDefinitions[variantKey].RecipeConditions}"; int precondition = Game1.getFarm().checkEventPrecondition(eventKey); if (precondition != -1) { newVariants.Add(variantKey); Game1.player.craftingRecipes.Add(itemName, 0); } } return(newVariants); }
public static void AddDefaultRecipes() { List <string> recipesToAdd = new List <string>(); int[] eventsSeen = Game1.player.eventsSeen.ToArray(); string precondition = $"{ModEntry.EventRootId}/{ModEntry.EventData[0]["Conditions"]}"; int rootEventReady = Game1.getFarm().checkEventPrecondition(precondition); bool hasOrWillSeeRootEvent = eventsSeen.Contains(ModEntry.EventRootId) || rootEventReady != -1; for (int i = 0; i < ModEntry.ItemDefinitions.Count; ++i) { string variantKey = ModEntry.ItemDefinitions.Keys.ElementAt(i); string craftingRecipeName = OutdoorPot.GetNameFromVariantKey(variantKey: variantKey); bool isAlreadyKnown = Game1.player.craftingRecipes.ContainsKey(craftingRecipeName); bool isDefaultRecipe = ModEntry.ItemDefinitions[variantKey].RecipeIsDefault; bool isInitialEventRecipe = string.IsNullOrEmpty(ModEntry.ItemDefinitions[variantKey].RecipeConditions); bool shouldAdd = ModEntry.Config.RecipesAlwaysAvailable || isDefaultRecipe || (hasOrWillSeeRootEvent && isInitialEventRecipe); if (!isAlreadyKnown && shouldAdd) { recipesToAdd.Add(craftingRecipeName); } } if (recipesToAdd.Count > 0) { Log.T($"Adding {recipesToAdd.Count} default recipes:{recipesToAdd.Aggregate(string.Empty, (str, s) => $"{str}{Environment.NewLine}{s}")}"); for (int i = 0; i < recipesToAdd.Count; ++i) { Game1.player.craftingRecipes.Add(recipesToAdd[i], 0); } } }
public void Edit <T>(IAssetData asset) { /********* * Local data *********/ if (asset.AssetNameEquals(GameContentEventDataPath)) { var events = ((Newtonsoft.Json.Linq.JArray)asset .AsDictionary <string, object>() .Data["Events"]) .ToObject <List <Dictionary <string, string> > >(); // Events are populated with preset tokens and script dialogues depending on game locale. // Root event tokenisation for (int i = 0; i < events.Count; ++i) { // Format event script with event NPC name, as well as their dialogue strings string[] args = new string[] { events[i]["Who"] } .Concat(new int[] { 1, 2, 3, 4 } .Select(j => Translations.GetTranslation($"event.{i}.dialogue.{j}"))) .ToArray(); events[i]["Script"] = string.Format( format: events[i]["Script"], args: args); events[i]["Conditions"] = string.Format( format: events[i]["Conditions"], events[i]["Who"]); } Log.T($"Loaded {events.Count} event(s).{Environment.NewLine}Root event: {events[0]["Where"]}/{events[0]["Conditions"]}"); ModEntry.EventData = events; return; } /******** * Game data ********/ int id = OutdoorPot.BaseParentSheetIndex; if (asset.AssetNameEquals(Path.Combine("Data", "BigCraftablesInformation"))) { if (ModEntry.ItemDefinitions == null) { return; } string[] fields; var data = asset.AsDictionary <int, string>().Data; // Set or reset the item ID for the generic object to some first best available index id = OutdoorPot.BaseParentSheetIndex = data.Keys.Max() + 2; string name, description; // Patch generic object entry into bigcraftables file, including display name and description from localisations file name = Translations.GetTranslation("item.name"); description = Translations.GetTranslation("item.description.default"); fields = data.First().Value.Split('/'); // Use existing data as a template; most fields are common or unused fields[0] = OutdoorPot.GenericName; fields[4] = description; fields[8] = name; data[id] = string.Join("/", fields); // Patch in dummy object entries after generic object entry for (int i = 1; i < ModEntry.ItemDefinitions.Count; ++i) { ItemDefinition d = ModEntry.ItemDefinitions[ModEntry.ItemDefinitions.Keys.ElementAt(i)]; name = Translations.GetNameTranslation(data: d); fields = data[id].Split('/'); fields[4] = description; fields[8] = name; data[id + i] = string.Join("/", fields); } // Don't remove the generic craftable from data lookup, since it's used later for crafting recipes and defaults return; } if (asset.AssetNameEquals(Path.Combine("Data", "CraftingRecipes"))) { if (ModEntry.ItemDefinitions == null || id < 0) { return; } // As above for the craftables dictionary, the recipes dictionary needs to have // our varieties patched in to have them appear. // Since all objects share a single ParentSheetIndex, each crafting recipe will normally // only produce a generic/wooden object. // This is handled in HarmonyPatches.CraftingPage_ClickCraftingRecipe_Prefix, which // is also needed to produce an OutdoorPot instance rather than a StardewValley.Object. // Add crafting recipes for all object variants var data = asset.AsDictionary <string, string>().Data; foreach (KeyValuePair <string, ItemDefinition> idAndFields in ModEntry.ItemDefinitions) { string[] newFields = new string[] { // Crafting ingredients: ItemDefinition.ParseRecipeIngredients(data: idAndFields.Value), // Unused field: "blue berry", // Crafted item ID and quantity: $"{OutdoorPot.BaseParentSheetIndex} {idAndFields.Value.RecipeCraftedCount}", // Recipe is bigCraftable: "true", // Recipe conditions (we ignore these): "blue berry", // Recipe display name: Translations.GetNameTranslation(data: idAndFields.Value) }; data[OutdoorPot.GetNameFromVariantKey(idAndFields.Key)] = string.Join("/", newFields); } return; } if (asset.AssetName.StartsWith(Path.Combine("Data", "Events")) && Path.GetFileNameWithoutExtension(asset.AssetName) is string where) { // Patch our event data into whatever location happens to match the one specified. // Event tokenisation is handled in the Edit block for GameContentEventDataPath. if (ModEntry.EventData != null && ModEntry.EventData.FirstOrDefault(e => e["Where"] == where) is Dictionary <string, string> eventData) { string key = $"{ModEntry.EventRootId}{ModEntry.EventData.IndexOf(eventData)}/{eventData["Conditions"]}"; asset.AsDictionary <string, string>().Data[key] = eventData["Script"]; } return; } }