public void UpdateLocalStockWithSyncedQuanitities(SynchedShop shop, Dictionary <ISalable, int[]> localStock, Dictionary <string, Func <bool> > conditionalItemFilters = null) { List <Item> itemsToRemove = new List <Item>(); NetStringDictionary <int, NetInt> sharedStock = getSharedStock(shop); if (getLastDayUpdated(shop) != Game1.Date.TotalDays) { setLastDayUpdated(shop, Game1.Date.TotalDays); sharedStock.Clear(); foreach (Item item5 in localStock.Keys) { string itemString3 = Utility.getStandardDescriptionFromItem(item5, 1); sharedStock.Add(itemString3, localStock[item5][1]); if (sharedStock[itemString3] != int.MaxValue) { item5.Stack = sharedStock[itemString3]; } } } else { itemsToRemove.Clear(); foreach (Item item4 in localStock.Keys) { string itemString2 = Utility.getStandardDescriptionFromItem(item4, 1); if (sharedStock.ContainsKey(itemString2) && sharedStock[itemString2] > 0) { localStock[item4][1] = sharedStock[itemString2]; if (sharedStock[itemString2] != int.MaxValue) { item4.Stack = sharedStock[itemString2]; } } else { itemsToRemove.Add(item4); } } foreach (Item item3 in itemsToRemove) { localStock.Remove(item3); } } itemsToRemove.Clear(); if (conditionalItemFilters != null) { foreach (Item item2 in localStock.Keys) { string itemString = Utility.getStandardDescriptionFromItem(item2, 1); if (conditionalItemFilters.ContainsKey(itemString) && !conditionalItemFilters[itemString]()) { itemsToRemove.Add(item2); } } foreach (Item item in itemsToRemove) { localStock.Remove(item); } } }
public void OnItemPurchased(SynchedShop shop, ISalable item, int amount) { NetStringDictionary <int, NetInt> sharedStock = getSharedStock(shop); string itemString = Utility.getStandardDescriptionFromItem(item as Item, 1); if (sharedStock.ContainsKey(itemString) && sharedStock[itemString] != int.MaxValue && (!(item is Object) || !(item as Object).IsRecipe)) { sharedStock[itemString] -= amount; } }
private void LoadRecipes(IContentPack contentPack, IEnumerable <ContentPackDataInfo> sources) { var recipes = (from source in sources from entry in source.Content.Recipes select new { Source = source, Data = entry }).ToArray(); // Create each recipe foreach (var recipe in recipes) { // Create each ingredient IRecipePart[] ingredients = recipe.Data.Ingredients.Select(ConvertPart).ToArray(); // Create each result IRecipePart[] results = recipe.Data.Results.Select(ConvertPart).ToArray(); // Create the recipe ISprite sprite = results.FirstOrDefault()?.Sprite ?? ingredients.FirstOrDefault()?.Sprite ?? throw new InvalidOperationException("Unable to create a sprite for a recipe."); string recipeName = this._api.Items.RegisterCraftingRecipe(new ModRecipe(this._api.TranslationHelper, sprite, results, ingredients, recipe.Data.Name, recipe.Data.IsCooking)); // TODO: Add conditions when the recipe can be added this._api.Owner.Helper.Events.GameLoop.SaveLoaded += (sender, args) => { // Get the target dictionary NetStringDictionary <int, NetInt> targetDictionary = recipe.Data.IsCooking ? Game1.player.craftingRecipes : Game1.player.cookingRecipes; // Add the recipe to the target if (!targetDictionary.ContainsKey(recipeName)) { targetDictionary.Add(recipeName, 0); } }; this._api.Owner.Monitor.Log($" - Added recipe: {recipeName}, Ingredients: {ingredients.Length}, Results: {results.Length}", LogLevel.Debug); } IRecipePart ConvertPart(RecipeData.Part part) { return(part.Match <RecipeData.Part, IRecipePart>() .When <RecipeData.ModPart>(modPart => { if (!this._api.Items.Delegator.TryParseKey(modPart.Id, out ItemKey key)) { key = new ItemKey(contentPack.Manifest, modPart.Id); } return new ModRecipePart(this._api, key, part.Quantity); }) .When <RecipeData.GamePart>(gamePart => gamePart.Type == RecipeData.ItemType.OBJECT, gamePart => new SObjectRecipePart(this._api, gamePart.Id, gamePart.Quantity)) .When <RecipeData.GamePart>(gamePart => gamePart.Type == RecipeData.ItemType.BIGCRAFTABLE, gamePart => new BigCraftableRecipePart(this._api, gamePart.Id, gamePart.Quantity)) .When <RecipeData.GamePart>(gamePart => gamePart.Type == RecipeData.ItemType.WEAPON, gamePart => new WeaponRecipePart(this._api, gamePart.Id, gamePart.Quantity)) .When <RecipeData.GamePart>(gamePart => gamePart.Type == RecipeData.ItemType.HAT, gamePart => new HatRecipePart(this._api, gamePart.Id, gamePart.Quantity)) .ElseThrow(_ => new InvalidOperationException($"Unknown part type: {part.GetType().FullName}"))); } }
/// <summary> /// Takes a list of recipes and check if the player has learned it from the given list of recipes ( either cooking or crafting ) /// All spaces in given recipe names should be replaced with `-` because spaces are used as a delimiter (i.e. Seafoam Pudding should be entered as Seafoam-Pudding ) /// </summary> private bool CheckHasRecipe(string[] conditionParams, NetStringDictionary <int, NetInt> craftingRecipes) { for (int i = 1; i < conditionParams.Length; i++) { if (!(from rec in craftingRecipes.Keys select rec.Replace(" ", "-")).Contains(conditionParams[i])) { return(false); } } return(true); }
/// <summary>Allows generic interactions with NPCs in <see cref="MovieTheater"/> under certain conditions.</summary> /// <param name="__instance">The <see cref="MovieTheater"/> instance.</param> /// <param name="tileLocation">The tile targeted by this action.</param> /// <param name="viewport">The viewport of the player performing this action.</param> /// <param name="who">The player performing this action.</param> /// <param name="____playerInvitedPatrons">The list of NPCs invited by the player. Non-public field for this <see cref="MovieTheater"/> instance.</param> /// <param name="____characterGroupLookup">The list of NPC patrons currently present. Non-public field for this <see cref="MovieTheater"/> instance.</param> /// <param name="__result">The result of the original method.</param> private static void MovieTheater_checkAction(MovieTheater __instance, Location tileLocation, xTile.Dimensions.Rectangle viewport, Farmer who, NetStringDictionary <int, NetInt> ____playerInvitedPatrons, NetStringDictionary <bool, NetBool> ____characterGroupLookup, ref bool __result) { try { if (__result == true) //if the original method return true { PropertyValue action = null; __instance.map.GetLayer("Buildings").PickTile(new Location(tileLocation.X * 64, tileLocation.Y * 64), viewport.Size)?.Properties.TryGetValue("Action", out action); //get this tile's Action property if it exists if (action == null) //if this tile does NOT have an Action property { Microsoft.Xna.Framework.Rectangle tileRect = new Microsoft.Xna.Framework.Rectangle(tileLocation.X * 64, tileLocation.Y * 64, 64, 64); //get this tile's pixel area foreach (NPC npc in __instance.characters) //for each NPC in this location { if (npc.isVillager() && npc.GetBoundingBox().Intersects(tileRect)) //if this NPC is a villager and targeted by this action { string npcName = npc.Name; if (____playerInvitedPatrons.ContainsKey(npcName) == false && ____characterGroupLookup.ContainsKey(npcName) == false) //if this NPC is NOT here as a patron (i.e. does not have patron-specific dialogue or behavior) { __result = npc.checkAction(who, __instance); //check action on this NPC (i.e. talk/give gifts/etc) and override the result return; } } } } } } catch (Exception ex) { Monitor.LogOnce($"Harmony patch \"{nameof(HarmonyPatch_MovieTheaterNPCs)}\" has encountered an error. Postfix \"{nameof(MovieTheater_checkAction)}\" might malfunction or revert to default behavior. Full error message: \n{ex.ToString()}", LogLevel.Error); return; //run the original method } }
/*** * Modified from StardewValley.CraftingPage.clickCraftingRecipe ***/ /// <summary> /// Crafts the recipe. /// </summary> /// <param name="recipe">The recipe to craft.</param> /// <param name="playSound">If set to <c>true</c> play sound.</param> private void CraftRecipe(CraftingRecipe recipe, bool playSound = true) { List <Ingredient> ingredients = SelectIngredients(recipe); int avgQuality = 0; int totalIngredients = 0; foreach (Ingredient ingredient in ingredients) { SObject selectedItem = ingredient.Item as SObject; avgQuality += selectedItem.Quality * selectedItem.Stack; totalIngredients += selectedItem.Stack; } avgQuality /= totalIngredients; if (avgQuality == 3) { avgQuality = 4; } Util.Monitor.VerboseLog($"Crafting {recipe.DisplayName} (quality {avgQuality})"); Item item = recipe.createItem(); if (item is SObject obj) { obj.Quality = avgQuality; } Game1.player.checkForQuestComplete(null, -1, -1, item, null, 2, -1); if (heldItem == null) { heldItem = item; foreach (Ingredient ingredient in ingredients) { ingredient.Consume(); } if (playSound) { Game1.playSound("coin"); } } else if (heldItem.canStackWith(item) && heldItem.Stack + recipe.numberProducedPerCraft - 1 < heldItem.maximumStackSize()) { heldItem.Stack += recipe.numberProducedPerCraft; foreach (Ingredient ingredient in ingredients) { ingredient.Consume(); } if (playSound) { Game1.playSound("coin"); } } if (!cooking && Game1.player.craftingRecipes.ContainsKey(recipe.name)) { NetStringDictionary <int, NetInt> craftingRecipes = Game1.player.craftingRecipes; string name = recipe.name; craftingRecipes[name] += recipe.numberProducedPerCraft; } if (cooking) { Game1.player.cookedRecipe(heldItem.parentSheetIndex); } if (!cooking) { Game1.stats.checkForCraftingAchievements(); } else { Game1.stats.checkForCookingAchievements(); } if (Game1.options.gamepadControls && heldItem != null && Game1.player.couldInventoryAcceptThisItem(heldItem)) { Game1.player.addItemToInventoryBool(heldItem, false); heldItem = null; } }
/* * Modded method */ private void clickCraftingRecipe(ClickableTextureComponent c, bool playSound = true) { List <Ingredient> ingredients = SelectIngredients(pagesOfCraftingRecipes[currentCraftingPage][c]); int avgQuality = 0; int totalIngredients = 0; foreach (Ingredient ingredient in ingredients) { SObject selectedItem = ingredient.ItemList[ingredient.Index] as SObject; avgQuality += selectedItem.Quality * selectedItem.Stack; totalIngredients += selectedItem.Stack; } avgQuality /= totalIngredients; if (avgQuality == 3) { avgQuality = 4; } Item item = pagesOfCraftingRecipes[currentCraftingPage][c].createItem(); if (item is SObject obj) { obj.Quality = avgQuality; } Game1.player.checkForQuestComplete(null, -1, -1, item, null, 2, -1); if (heldItem == null) { heldItem = item; foreach (Ingredient ingredient in ingredients) { Item usedItem = ingredient.ItemList[ingredient.Index]; if (usedItem.Stack - ingredient.Amount <= 0) { ingredient.ItemList[ingredient.Index] = null; } else { usedItem.Stack -= ingredient.Amount; } } if (playSound) { Game1.playSound("coin"); } } else if (heldItem.canStackWith(item) && heldItem.Stack + pagesOfCraftingRecipes[currentCraftingPage][c].numberProducedPerCraft - 1 < heldItem.maximumStackSize()) { heldItem.Stack += pagesOfCraftingRecipes[currentCraftingPage][c].numberProducedPerCraft; foreach (Ingredient ingredient in ingredients) { Item usedItem = ingredient.ItemList[ingredient.Index]; if (usedItem.Stack - ingredient.Amount <= 0) { ingredient.ItemList[ingredient.Index] = null; } else { usedItem.Stack -= ingredient.Amount; } } if (playSound) { Game1.playSound("coin"); } } if (!cooking && Game1.player.craftingRecipes.ContainsKey(pagesOfCraftingRecipes[currentCraftingPage][c].name)) { NetStringDictionary <int, NetInt> craftingRecipes = Game1.player.craftingRecipes; string name = pagesOfCraftingRecipes[currentCraftingPage][c].name; craftingRecipes[name] += pagesOfCraftingRecipes[currentCraftingPage][c].numberProducedPerCraft; } if (cooking) { Game1.player.cookedRecipe(heldItem.parentSheetIndex); } if (!cooking) { Game1.stats.checkForCraftingAchievements(); } else { Game1.stats.checkForCookingAchievements(); } if (Game1.options.gamepadControls && heldItem != null && Game1.player.couldInventoryAcceptThisItem(heldItem)) { Game1.player.addItemToInventoryBool(heldItem, false); heldItem = null; } }