private static void Multiplayer_PeerContextReceived(object sender, PeerContextReceivedEventArgs e) { // Whenever a new client connects to the host, force the NetStrings that store bag data to get re-loaded on the other clients if (Context.IsMainPlayer) { ItemBag.GetAllBags(false).ForEach(x => x.Resync()); // I have no idea why, but it seems like the items in the connected farmhand's inventory are getting replaced with new instances after I've synchronized the current instances. // So to maintain bags in their inventory, send the data to the peer using IMultiplayerEvents.SendMessage/IMultiplayerEvents.ModMessageReceived. // This will force the connected client to overwrite their inventory with the correct data. Farmer ConnectedFarmer = Game1.getAllFarmers().First(x => x.UniqueMultiplayerID == e.Peer.PlayerID); Dictionary <int, string> PendingSync = new Dictionary <int, string>(); for (int i = 0; i < ConnectedFarmer.Items.Count; i++) { Item Item = ConnectedFarmer.Items[i]; if (Item != null && Item is ItemBag Bag && Bag.TrySerializeToString(out string DataString, out Exception Error)) { PendingSync.Add(i, DataString); } } if (PendingSync.Any()) { Helper.Multiplayer.SendMessage(PendingSync, ForceResyncCommandType, new string[] { ItemBagsMod.ModUniqueId }, new long[] { ConnectedFarmer.UniqueMultiplayerID }); } Helper.Multiplayer.SendMessage("", OnConnectedCommandType, new string[] { ItemBagsMod.ModUniqueId }, new long[] { ConnectedFarmer.UniqueMultiplayerID }); } }
public static void MonsterDrop_Postfix(GameLocation __instance, Monster monster, int x, int y, Farmer who) { try { MonsterLootSettings LootSettings = MonsterLootSettings; if (who.UniqueMultiplayerID == Game1.player.UniqueMultiplayerID && LootSettings.CanReceiveBagsAsDrops) { // Roll chance at receiving an ItemBag double Chance = LootSettings.GetItemBagDropChance(__instance, monster, out double BaseChance, out double LocationMultiplier, out double ExpMultiplier, out double HPMultiplier); bool Success = Randomizer.NextDouble() <= Chance; string LogMessage; if (Success) { if (MonsterLootSettings.LogDropChancesToConsole) { LogMessage = string.Format("Succeeded drop chance: Location = {0}, monster.ExperienceGained = {1}, monster.MaxHealth = {2}\n" + "BaseChance = {3} ({4}%), LocationMultiplier = {5} (+{6}%), ExpMultiplier = {7}, HPMultiplier = {8} (+{9}%), TotalChance = {10} ({11}%)", __instance.Name, monster.ExperienceGained, monster.MaxHealth, BaseChance, (BaseChance * 100.0).ToString("0.##"), LocationMultiplier, ((LocationMultiplier - 1.0) * 100.0).ToString("0.##"), ExpMultiplier.ToString("#.####"), HPMultiplier, ((HPMultiplier - 1.0) * 100.0).ToString("0.##"), Chance, (Chance * 100.0).ToString("0.###")); Monitor.Log(LogMessage, LogLevel.Info); } int SpawnDirection = Randomizer.Next(4); List <ItemBag> OwnedBags = ItemBag.GetAllBags(true); // Compute the odds of receiving each type of bag bool CanReceiveRucksack = LootSettings.RucksackDropSettings.SizeWeights.Any(size => size.Value > 0); int RucksackWeight = CanReceiveRucksack ? LootSettings.RucksackDropSettings.TypeWeight : 0; bool CanReceiveOmniBag = LootSettings.OmniBagDropSettings.SizeWeights.Any(size => size.Value > 0); int OmniBagWeight = CanReceiveOmniBag ? LootSettings.OmniBagDropSettings.TypeWeight : 0; bool CanReceiveBundleBag = LootSettings.BundleBagDropSettings.SizeWeights.Any(size => BundleBag.ValidSizes.Contains(size.Key) && size.Value > 0 && !IsSizeObsolete(OwnedBags, BundleBag.BundleBagTypeId, size.Key)); int BundleBagWeight = CanReceiveBundleBag ? LootSettings.BundleBagDropSettings.TypeWeight : 0; bool CanReceiveStandardBag = LootSettings.StandardBagDropSettings.SizeWeights.Any(size => size.Value > 0); int StandardBagWeight = CanReceiveStandardBag ? LootSettings.StandardBagDropSettings.TypeWeight : 0; int TotalTypeWeight = RucksackWeight + OmniBagWeight + BundleBagWeight + StandardBagWeight; if (TotalTypeWeight > 0) { ItemBag ChosenBag; // Pick the type of bag to spawn (Rucksack, OmniBag, BundleBag or a standard BoundedBag) int ChosenTypeWeight = Randomizer.Next(0, TotalTypeWeight); if (ChosenTypeWeight < RucksackWeight) { ContainerSize CurrentSize = GetWeightedRandomSize(LootSettings.RucksackDropSettings.SizeWeights); // Try to force a non-obsolete bag to spawn if (RollDice(LootSettings.ForceNewBagTypeChance)) { List <ContainerSize> ValidSizes = LootSettings.RucksackDropSettings.SizeWeights.Where(size => size.Value > 0).Select(size => size.Key).ToList(); ContainerSize MaxSize = ValidSizes.DefaultIfEmpty(ContainerSize.Small).Max(); while (CurrentSize < MaxSize && IsSizeObsolete(OwnedBags, Rucksack.RucksackTypeId, CurrentSize)) { CurrentSize = ValidSizes.Where(size => size > CurrentSize).OrderBy(size => size).First(); } } // Spawn a Rucksack ChosenBag = new Rucksack(CurrentSize, false); } else if (ChosenTypeWeight < RucksackWeight + OmniBagWeight) { ContainerSize CurrentSize = GetWeightedRandomSize(LootSettings.OmniBagDropSettings.SizeWeights); // Try to force a non-obsolete bag to spawn if (RollDice(LootSettings.ForceNewBagTypeChance)) { List <ContainerSize> ValidSizes = LootSettings.OmniBagDropSettings.SizeWeights.Where(size => size.Value > 0).Select(size => size.Key).ToList(); ContainerSize MaxSize = ValidSizes.DefaultIfEmpty(ContainerSize.Small).Max(); while (CurrentSize < MaxSize && IsSizeObsolete(OwnedBags, OmniBag.OmniBagTypeId, CurrentSize)) { CurrentSize = ValidSizes.Where(size => size > CurrentSize).OrderBy(size => size).First(); } } // Spawn an OmniBag ChosenBag = new OmniBag(CurrentSize); } else if (ChosenTypeWeight < RucksackWeight + OmniBagWeight + BundleBagWeight) { // Spawn a BundleBag ContainerSize Size = GetWeightedRandomSize(LootSettings.BundleBagDropSettings.SizeWeights.Where(size => BundleBag.ValidSizes.Contains(size.Key) && !IsSizeObsolete(OwnedBags, BundleBag.BundleBagTypeId, size.Key))); ChosenBag = new BundleBag(Size, true); } else { ContainerSize CurrentSize = GetWeightedRandomSize(LootSettings.StandardBagDropSettings.SizeWeights); // Get all standard BagTypes that are available in the chosen size List <BagType> StandardTypes = ItemBagsMod.BagConfig.BagTypes.Where(type => type.SizeSettings.Any(sizeCfg => sizeCfg.Size == CurrentSize)).ToList(); // Try to force a non-obsolete bag to spawn if (RollDice(LootSettings.ForceNewBagTypeChance)) { StandardTypes.RemoveAll(type => IsSizeObsolete(OwnedBags, type.Id, CurrentSize)); // If all bag types were obsolete, then keep incrementing the size until we find a non-obsolete bag to spawn if (!StandardTypes.Any()) { List <ContainerSize> ValidSizes = LootSettings.StandardBagDropSettings.SizeWeights.Where(size => size.Value > 0).Select(size => size.Key).ToList(); ContainerSize MaxSize = ValidSizes.DefaultIfEmpty(ContainerSize.Small).Max(); while (CurrentSize < MaxSize && !StandardTypes.Any()) { CurrentSize = ValidSizes.Where(size => size > CurrentSize).OrderBy(size => size).First(); StandardTypes = ItemBagsMod.BagConfig.BagTypes.Where(type => type.SizeSettings.Any(sizeCfg => sizeCfg.Size == CurrentSize) && !IsSizeObsolete(OwnedBags, type.Id, CurrentSize)).ToList(); } } } if (StandardTypes.Any()) { // Spawn a standard BoundedBag int ChosenTypeIndex = Randomizer.Next(StandardTypes.Count); ChosenBag = new BoundedBag(StandardTypes[ChosenTypeIndex], CurrentSize, false); } else { ChosenBag = null; } } if (ChosenBag != null) { Game1.createItemDebris(ChosenBag, Game1.player.getStandingPosition(), SpawnDirection, null, -1); } } } else if (MonsterLootSettings.LogDropChancesToConsole) { LogMessage = string.Format("Failed drop chance: Location = {0}, monster.ExperienceGained = {1}, monster.MaxHealth = {2}\n" + "BaseChance = {3} ({4}%), LocationMultiplier = {5} (+{6}%), ExpMultiplier = {7}, HPMultiplier = {8} (+{9}%), TotalChance = {10} ({11}%)", __instance.Name, monster.ExperienceGained, monster.MaxHealth, BaseChance, (BaseChance * 100.0).ToString("0.##"), LocationMultiplier, ((LocationMultiplier - 1.0) * 100.0).ToString("0.##"), ExpMultiplier.ToString("#.####"), HPMultiplier, ((HPMultiplier - 1.0) * 100.0).ToString("0.##"), Chance, (Chance * 100.0).ToString("0.###")); Monitor.Log(LogMessage, LogLevel.Info); } } } catch (Exception ex) { Monitor.Log(string.Format("Unhandled Error in {0}:\n{1}", nameof(MonsterDrop_Postfix), ex), LogLevel.Error); } }
private static void OnJsonAssetsIdsFixed(IJsonAssetsAPI API, BagConfig Target) { try { ItemBagsMod.ModdedItems.ImportModdedItems(API, ItemBagsMod.BagConfig); if (ItemBagsMod.TemporaryModdedBagTypes.Any()) { Dictionary <string, int> AllBigCraftableIds = new Dictionary <string, int>(); foreach (System.Collections.Generic.KeyValuePair <int, string> KVP in Game1.bigCraftablesInformation) { string ObjectName = KVP.Value.Split('/').First(); if (!AllBigCraftableIds.ContainsKey(ObjectName)) { AllBigCraftableIds.Add(ObjectName, KVP.Key); } } Dictionary <string, int> AllObjectIds = new Dictionary <string, int>(); foreach (System.Collections.Generic.KeyValuePair <int, string> KVP in Game1.objectInformation) { string ObjectName = KVP.Value.Split('/').First(); if (!AllObjectIds.ContainsKey(ObjectName)) { AllObjectIds.Add(ObjectName, KVP.Key); } } IDictionary <string, int> JABigCraftableIds = API.GetAllBigCraftableIds(); IDictionary <string, int> JAObjectIds = API.GetAllObjectIds(); // Now that JsonAssets has finished loading the modded items, go through each one, and convert the items into StoreableBagItems (which requires an Id instead of just a Name) foreach (System.Collections.Generic.KeyValuePair <ModdedBag, BagType> KVP in ItemBagsMod.TemporaryModdedBagTypes) { foreach (BagSizeConfig SizeCfg in KVP.Value.SizeSettings) { HashSet <string> FailedItemNames = new HashSet <string>(); List <StoreableBagItem> Items = new List <StoreableBagItem>(); foreach (ModdedItem DesiredItem in KVP.Key.Items.Where(x => x.Size <= SizeCfg.Size)) { StoreableBagItem Item = DesiredItem.ToStoreableBagItem(JABigCraftableIds, JAObjectIds, AllBigCraftableIds, AllObjectIds); if (Item == null) { FailedItemNames.Add(DesiredItem.Name); } else { Items.Add(Item); } } SizeCfg.Items = Items; if (FailedItemNames.Any()) { int MaxNamesShown = 5; string MissingItems = string.Format("{0}{1}", string.Join(", ", FailedItemNames.Take(MaxNamesShown)), FailedItemNames.Count <= MaxNamesShown ? "" : string.Format(" + {0} more", (FailedItemNames.Count - MaxNamesShown))); string WarningMsg = string.Format("Warning - {0} items could not be found for modded bag '{1} {2}'. Missing Items: {3}", FailedItemNames.Count, SizeCfg.Size.ToString(), KVP.Key.BagName, MissingItems); ItemBagsMod.ModInstance.Monitor.Log(WarningMsg, LogLevel.Warn); } } } ItemBag.GetAllBags(true).ForEach(x => x.OnJsonAssetsItemIdsFixed(API, true)); } } catch (Exception ex) { ItemBagsMod.ModInstance.Monitor.Log(string.Format("Failed to import modded bags. Error: {0}", ex.Message), LogLevel.Error); } }
private void Display_MenuChanged(object sender, MenuChangedEventArgs e) { // Refresh completed Bundles in the community center if (e.OldMenu != null && e.OldMenu is JunimoNoteMenu) { CommunityCenterBundles.Instance = new CommunityCenterBundles(); } if (e.NewMenu is ShopMenu SM) { // Determine if the shop menu belongs to one of our managed shops bool IsModifiableShop = false; BagShop BagShop = BagShop.Pierre; if (SM.portraitPerson?.Name != null) { //TODO test if the Stardew Valley Expanded shops like Isaac/Sophia/Alesia have non-null values for ShopMenu.portraitPerson.Name if (Enum.TryParse(SM.portraitPerson.Name, out BagShop)) { IsModifiableShop = true; } } else if (SM.storeContext != null) { if (SM.storeContext.Equals("Forest", StringComparison.CurrentCultureIgnoreCase)) { if (SM.onPurchase?.GetMethodInfo().Name == "onTravelingMerchantShopPurchase") // nameof(Utility.onTravelingMerchantShopPurchase) { BagShop = BagShop.TravellingCart; } else { BagShop = BagShop.HatMouse; } IsModifiableShop = true; } else if (SM.storeContext.Equals("Town", StringComparison.CurrentCultureIgnoreCase) && SM.potraitPersonDialogue != null && SM.potraitPersonDialogue.Contains("Khadija")) { BagShop = BagShop.Khadija; IsModifiableShop = true; } } // Add Bag items to the shop's stock if (IsModifiableShop) { Dictionary <ISalable, int[]> Stock = SM.itemPriceAndStock; bool ShouldModifyStock = true; if (BagShop == BagShop.Clint) { // Assume user is viewing Clint tool upgrades if the stock doesn't contain Coal if (!Stock.Any(x => x.Key is Object Obj && Obj.Name.Equals("Coal", StringComparison.CurrentCultureIgnoreCase))) { ShouldModifyStock = false; } } if (ShouldModifyStock) { bool HasChangedStock = false; List <ItemBag> OwnedBags = UserConfig.HideObsoleteBagsFromShops ? ItemBag.GetAllBags(true) : new List <ItemBag>(); // Add Bounded Bags to stock foreach (BagType Type in BagConfig.BagTypes) { foreach (BagSizeConfig SizeCfg in Type.SizeSettings) { bool IsSoldByShop = UserConfig.IsSizeVisibleInShops(SizeCfg.Size) && SizeCfg.Sellers.Contains(BagShop); #if DEBUG //IsSoldByShop = true; #endif if (IsSoldByShop) { bool IsObsolete = false; if (UserConfig.HideObsoleteBagsFromShops) { IsObsolete = OwnedBags.Any(x => x is BoundedBag BB && BB.TypeInfo == Type && (int)BB.Size > (int)SizeCfg.Size); } if (!IsObsolete) { BoundedBag SellableInstance = new BoundedBag(Type, SizeCfg.Size, false); int Price = SellableInstance.GetPurchasePrice(); #if DEBUG //Price = 1 + (int)SizeCfg.Size; #endif Stock.Add(SellableInstance, new int[] { Price, ShopMenu.infiniteStock }); HasChangedStock = true; } } } } // Add Bundle Bags to stock foreach (BundleBagSizeConfig SizeCfg in UserConfig.BundleBagSettings) { ContainerSize Size = SizeCfg.Size; if (BundleBag.ValidSizes.Contains(Size) && SizeCfg.Sellers.Contains(BagShop) && UserConfig.IsSizeVisibleInShops(Size)) { bool IsObsolete = false; if (UserConfig.HideObsoleteBagsFromShops) { IsObsolete = OwnedBags.Any(x => x is BundleBag BB && (int)BB.Size > (int)Size); } if (!IsObsolete) { BundleBag BundleBag = new BundleBag(Size, true); int Price = BundleBag.GetPurchasePrice(); Stock.Add(BundleBag, new int[] { Price, ShopMenu.infiniteStock }); HasChangedStock = true; } } } // Add Rucksacks to stock foreach (RucksackSizeConfig SizeCfg in UserConfig.RucksackSettings) { ContainerSize Size = SizeCfg.Size; if (SizeCfg.Sellers.Contains(BagShop) && UserConfig.IsSizeVisibleInShops(Size)) { bool IsObsolete = false; if (UserConfig.HideObsoleteBagsFromShops) { IsObsolete = OwnedBags.Any(x => x is Rucksack RS && (int)RS.Size > (int)Size); } if (!IsObsolete) { Rucksack Rucksack = new Rucksack(Size, false, AutofillPriority.Low); int Price = Rucksack.GetPurchasePrice(); Stock.Add(Rucksack, new int[] { Price, ShopMenu.infiniteStock }); HasChangedStock = true; } } } // Add Omni Bags to stock foreach (OmniBagSizeConfig SizeCfg in UserConfig.OmniBagSettings) { ContainerSize Size = SizeCfg.Size; if (SizeCfg.Sellers.Contains(BagShop) && UserConfig.IsSizeVisibleInShops(Size)) { bool IsObsolete = false; if (UserConfig.HideObsoleteBagsFromShops) { IsObsolete = OwnedBags.Any(x => x is OmniBag OB && (int)OB.Size > (int)Size); } if (!IsObsolete) { OmniBag OmniBag = new OmniBag(Size); int Price = OmniBag.GetPurchasePrice(); Stock.Add(OmniBag, new int[] { Price, ShopMenu.infiniteStock }); HasChangedStock = true; } } } if (HasChangedStock) { SM.setItemPriceAndStock(Stock); } } } } }