public static void MonsterDrop_Postfix(GameLocation __instance, Monster monster, int x, int y, Farmer who) { try { if (who == Game1.player) { // Make the monster drop an augmentor if you're lucky double Chance = MachineAugmentorsMod.UserConfig.MonsterLootSettings.GetAugmentorDropChance(__instance, monster, out double BaseChance, out double LocationMultiplier, out double ExpMultiplier, out double HPMultiplier); bool Success = Augmentor.Randomizer.NextDouble() <= Chance; string LogMessage; if (Success) { int SpawnDirection = Augmentor.Randomizer.Next(4); int NumTypes = Enum.GetValues(typeof(AugmentorType)).Length; AugmentorType Type = (AugmentorType)Augmentor.Randomizer.Next(NumTypes); int Quantity = Augmentor.RollDice(0.1) ? 2 : 1; Game1.createItemDebris(Augmentor.CreateInstance(Type, Quantity), Game1.player.getStandingPosition(), SpawnDirection, null, -1); 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.###")); } else { 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.###")); } #if DEBUG MachineAugmentorsMod.ModInstance.Monitor.Log(LogMessage, LogLevel.Debug); #else MachineAugmentorsMod.ModInstance.Monitor.Log(LogMessage, LogLevel.Trace); #endif } } catch (Exception ex) { MachineAugmentorsMod.ModInstance.Monitor.Log(string.Format("Unhandled Error in {0}:\n{1}", nameof(MonsterDrop_Postfix), ex), LogLevel.Error); } }
private void RegisterConsoleCommands() { List <string> ValidTypes = Enum.GetValues(typeof(AugmentorType)).Cast <AugmentorType>().Select(x => x.ToString()).ToList(); //Possible TODO: Add translation support for this command string CommandName = "player_addaugmentor"; string CommandHelp = string.Format("Adds augmentors of the given AugmentorType into your inventory.\n" + "Arguments: <AugmentorType> <Quantity>\n" + "Example: {0} EfficiencyAugmentor 6\n\n" + "Valid values for <AugmentorType>: {1}", CommandName, string.Join(", ", ValidTypes), string.Join(", ", ValidTypes)); Helper.ConsoleCommands.Add(CommandName, CommandHelp, (string Name, string[] Args) => { if (Game1.player.isInventoryFull()) { Monitor.Log("Unable to execute command: Inventory is full!", LogLevel.Alert); } else if (Args.Length < 2) { Monitor.Log("Unable to execute command: Required arguments missing!", LogLevel.Alert); } else { string TypeName = Args[0]; if (!Enum.TryParse(TypeName, out AugmentorType AugmentorType)) { Monitor.Log(string.Format("Unable to execute command: <AugmentorType> \"{0}\" is not valid. Expected valid values: {1}", TypeName, string.Join(", ", ValidTypes)), LogLevel.Alert); } else { if (!int.TryParse(Args[1], out int Quantity)) { Monitor.Log(string.Format("Unable to execute command: could not parse an integer from \"{0}\".", Args[1]), LogLevel.Alert); } else { Augmentor SpawnedItem = Augmentor.CreateInstance(AugmentorType, Quantity); Game1.player.addItemToInventory(SpawnedItem); } } } }); }
public static void Draw_Postfix(Object __instance, SpriteBatch spriteBatch, int x, int y, float alpha) { try { // Draw the augmentors that are attached to this machine, if any if (PlacedAugmentorsManager.Instance != null) { if (PlacedAugmentorsManager.Instance.TryFindAugmentedTile(__instance, x, y, out AugmentedTile AT)) { List <AugmentorType> Types = AT.GetAugmentorQuantities().Where(KVP => KVP.Value > 0).Select(KVP => KVP.Key).ToList(); Augmentor.DrawIconsOnTile(spriteBatch, Types, x, y, 0.6f); } } } catch (Exception ex) { MachineAugmentorsMod.ModInstance.Monitor.Log(string.Format("Unhandled Error in {0}:\n{1}", nameof(Draw_Postfix), ex), LogLevel.Error); } }
private void Display_RenderedWorld(object sender, StardewModdingAPI.Events.RenderedWorldEventArgs e) { if (Game1.activeClickableMenu == null && PlacedAugmentorsManager.Instance != null) { GameLocation CurrentLocation = Game1.player.currentLocation; bool IsHoveringPlacedObject = CurrentLocation.Objects.TryGetValue(HoveredTile, out Object HoveredObject); if (IsHoveringPlacedObject) { Dictionary <AugmentorType, int> AttachedAugmentors = PlacedAugmentorsManager.Instance.GetAugmentorQuantities(CurrentLocation.NameOrUniqueName, HoveredTile); bool HasAttachedAugmentors = AttachedAugmentors.Any(x => x.Value > 0); if (MachineInfo.TryGetMachineInfo(HoveredObject, out MachineInfo MI)) { SpriteFont DefaultFont = Game1.dialogueFont; // Draw a tooltip showing what effects the held item will have when attached to this machine if (Game1.player.CurrentItem is Augmentor HeldAugmentor && HeldAugmentor.IsAugmentable(HoveredObject)) { AttachedAugmentors.TryGetValue(HeldAugmentor.AugmentorType, out int CurrentAttachedQuantity); double CurrentEffect = CurrentAttachedQuantity <= 0 ? Augmentor.GetDefaultEffect(HeldAugmentor.AugmentorType) : Augmentor.ComputeEffect(HeldAugmentor.AugmentorType, CurrentAttachedQuantity, MI.RequiresInput); double NewEffectSingle = Augmentor.ComputeEffect(HeldAugmentor.AugmentorType, CurrentAttachedQuantity + 1, MI.RequiresInput); double NewEffectAll = Augmentor.ComputeEffect(HeldAugmentor.AugmentorType, CurrentAttachedQuantity + HeldAugmentor.Stack, MI.RequiresInput); // Example of desired tooltip: // ------------------------------------------- // | [Icon] Time to process: | // |-----------------------------------------| // | Current Effect: 95.5% | // | New Effect: 92.2% (1) / 81.1% (4) | // ------------------------------------------- int Padding = 28; int MarginAfterIcon = 10; float LabelTextScale = 0.75f; float ValueTextScale = 1.0f; // Compute sizes of each row so we know how big the tooltip is, and can horizontally center the header and other rows // Compute size of header int HeaderHorizontalPadding = 4; Augmentor.TryGetIconDetails(HeldAugmentor.AugmentorType, out Texture2D IconTexture, out Rectangle IconSourcePosition, out float IconScale, out SpriteEffects IconEffects); Vector2 IconSize = new Vector2(IconSourcePosition.Width * IconScale, IconSourcePosition.Height * IconScale); string HeaderText = string.Format("{0}:", HeldAugmentor.GetEffectDescription()); Vector2 HeaderTextSize = DefaultFont.MeasureString(HeaderText) * LabelTextScale; float HeaderRowWidth = IconSize.X + MarginAfterIcon + HeaderTextSize.X + HeaderHorizontalPadding * 2; float HeaderRowHeight = Math.Max(IconSize.Y, HeaderTextSize.Y); // Compute size of horizontal separator int HorizontalSeparatorHeight = 6; int HorizontalSeparatorMargin = 8; // Compute size of the labels before the effect values int MarginAfterLabel = 8; string CurrentEffectLabel = string.Format("{0}:", Translate("CurrentEffectLabel")); Vector2 CurrentEffectLabelSize = DefaultFont.MeasureString(CurrentEffectLabel) * LabelTextScale; string NewEffectLabel = string.Format("{0}:", Translate("NewEffectLabel")); Vector2 NewEffectLabelSize = DefaultFont.MeasureString(NewEffectLabel) * LabelTextScale; float EffectLabelWidth = Math.Max(CurrentEffectLabelSize.X, NewEffectLabelSize.X); Vector2 EffectLabelSize = new Vector2(EffectLabelWidth, CurrentEffectLabelSize.Y + NewEffectLabelSize.Y); // Compute size of the effect values string CurrentEffectValue = string.Format("{0}% ({1})", (CurrentEffect * 100.0).ToString("0.##"), CurrentAttachedQuantity); Vector2 CurrentEffectValueSize = DrawHelpers.MeasureStringWithSpecialNumbers(CurrentEffectValue, ValueTextScale, 0.0f); string NewEffectValue; if (HeldAugmentor.Stack > 1) { NewEffectValue = string.Format("{0}% ({1}) / {2}% ({3})", (NewEffectSingle * 100.0).ToString("0.##"), (CurrentAttachedQuantity + 1), (NewEffectAll * 100.0).ToString("0.##"), (CurrentAttachedQuantity + HeldAugmentor.Stack)); } else { NewEffectValue = string.Format("{0}% ({1})", (NewEffectSingle * 100.0).ToString("0.##"), (CurrentAttachedQuantity + 1)); } Vector2 NewEffectValueSize = DrawHelpers.MeasureStringWithSpecialNumbers(NewEffectValue, ValueTextScale, 0.0f); Vector2 EffectContentSize = new Vector2(EffectLabelWidth + MarginAfterLabel + Math.Max(CurrentEffectValueSize.X, NewEffectValueSize.X), Math.Max(CurrentEffectLabelSize.Y, CurrentEffectValueSize.Y) + Math.Max(NewEffectLabelSize.Y, NewEffectValueSize.Y)); // Compute total size of tooltip, draw the background Vector2 ToolTipSize = new Vector2(Padding * 2 + Math.Max(HeaderRowWidth, EffectContentSize.X), Padding + HeaderRowHeight + HorizontalSeparatorMargin + HorizontalSeparatorHeight + HorizontalSeparatorMargin + EffectContentSize.Y + Padding); Point ToolTipTopleft = DrawHelpers.GetTopleftPosition(new Point((int)ToolTipSize.X, (int)ToolTipSize.Y), MouseScreenPosition, 100); DrawHelpers.DrawBox(e.SpriteBatch, new Rectangle(ToolTipTopleft.X, ToolTipTopleft.Y, (int)ToolTipSize.X, (int)ToolTipSize.Y)); float CurrentY = ToolTipTopleft.Y + Padding; // Draw the header float HeaderStartX = ToolTipTopleft.X + (ToolTipSize.X - HeaderRowWidth) / 2.0f; Vector2 IconPosition = new Vector2(HeaderStartX, CurrentY + (HeaderRowHeight - IconSize.Y) / 2.0f); e.SpriteBatch.Draw(IconTexture, IconPosition, IconSourcePosition, Color.White, 0f, Vector2.Zero, IconScale, IconEffects, 1f); Vector2 HeaderTextPosition = new Vector2(HeaderStartX + IconSize.X + MarginAfterIcon, CurrentY + (HeaderRowHeight - HeaderTextSize.Y) / 2.0f); e.SpriteBatch.DrawString(DefaultFont, HeaderText, HeaderTextPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); CurrentY += HeaderRowHeight + HorizontalSeparatorMargin; // Draw the horizontal separator DrawHelpers.DrawHorizontalSeparator(e.SpriteBatch, ToolTipTopleft.X + Padding, (int)CurrentY, (int)(ToolTipSize.X - 2 * Padding), HorizontalSeparatorHeight); CurrentY += HorizontalSeparatorHeight + HorizontalSeparatorMargin; // Draw the current effect Vector2 CurrentEffectLabelPosition = new Vector2(ToolTipTopleft.X + Padding + (EffectLabelWidth - CurrentEffectLabelSize.X), CurrentY); Vector2 CurrentEffectValuePosition = new Vector2(ToolTipTopleft.X + Padding + EffectLabelWidth + MarginAfterLabel, CurrentY); e.SpriteBatch.DrawString(DefaultFont, CurrentEffectLabel, CurrentEffectLabelPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, CurrentEffectValuePosition, CurrentEffectValue, ValueTextScale, Color.White); CurrentY += Math.Max(CurrentEffectLabelSize.Y, CurrentEffectValueSize.Y); // Draw the new effect Vector2 NewEffectLabelPosition = new Vector2(ToolTipTopleft.X + Padding + (EffectLabelWidth - NewEffectLabelSize.X), CurrentY); Vector2 NewEffectValuePosition = new Vector2(ToolTipTopleft.X + Padding + EffectLabelWidth + MarginAfterLabel, CurrentY); e.SpriteBatch.DrawString(DefaultFont, NewEffectLabel, NewEffectLabelPosition, Color.Black, 0.0f, Vector2.Zero, LabelTextScale, SpriteEffects.None, 1.0f); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, NewEffectValuePosition, NewEffectValue, ValueTextScale, Color.White); } // Draw a tooltip showing what effects are currently applied to this machine else if (HasAttachedAugmentors) { int Padding = 28; int MarginAfterIcon = 10; // Compute the size of each icon Dictionary <AugmentorType, Vector2> IconSizes = new Dictionary <AugmentorType, Vector2>(); foreach (KeyValuePair <AugmentorType, int> KVP in AttachedAugmentors.Where(x => x.Value > 0)) { Augmentor.TryGetIconDetails(KVP.Key, out Texture2D IconTexture, out Rectangle IconSourcePosition, out float IconScale, out SpriteEffects IconEffects); Vector2 IconSize = new Vector2(IconSourcePosition.Width * IconScale, IconSourcePosition.Height * IconScale); IconSizes.Add(KVP.Key, IconSize); } float IconColumnWidth = IconSizes.Values.Max(x => x.Y); // Compute the size of each row (each row shows the effect of a type of augmentor that has been applied to this machine) Dictionary <AugmentorType, Vector2> RowSizes = new Dictionary <AugmentorType, Vector2>(); foreach (KeyValuePair <AugmentorType, int> KVP in AttachedAugmentors.Where(x => x.Value > 0)) { double CurrentEffect = Augmentor.ComputeEffect(KVP.Key, KVP.Value, MI.RequiresInput); string Text = string.Format("{0}% ({1})", (CurrentEffect * 100.0).ToString("0.#"), KVP.Value); Vector2 TextSize = DrawHelpers.MeasureStringWithSpecialNumbers(Text, 1.0f, 4.0f); float RowWidth = IconColumnWidth + MarginAfterIcon + TextSize.X; float RowHeight = Math.Max(IconSizes[KVP.Key].Y, TextSize.Y); RowSizes.Add(KVP.Key, new Vector2(RowWidth, RowHeight)); } // Compute total size of tooltip, draw the background Vector2 ToolTipSize = new Vector2(Padding * 2 + RowSizes.Values.Max(x => x.X), Padding * 2 + RowSizes.Values.Sum(x => x.Y)); Point ToolTipTopleft = DrawHelpers.GetTopleftPosition(new Point((int)ToolTipSize.X, (int)ToolTipSize.Y), MouseScreenPosition, 100); DrawHelpers.DrawBox(e.SpriteBatch, new Rectangle(ToolTipTopleft.X, ToolTipTopleft.Y, (int)ToolTipSize.X, (int)ToolTipSize.Y)); float CurrentY = ToolTipTopleft.Y + Padding; // Draw each row float RowStartX = ToolTipTopleft.X + Padding; foreach (KeyValuePair <AugmentorType, int> KVP in AttachedAugmentors.Where(x => x.Value > 0)) { float CurrentX = RowStartX; float RowHeight = RowSizes[KVP.Key].Y; // Draw the icon Augmentor.TryGetIconDetails(KVP.Key, out Texture2D IconTexture, out Rectangle IconSourcePosition, out float IconScale, out SpriteEffects IconEffects); Vector2 IconSize = IconSizes[KVP.Key]; Vector2 IconPosition = new Vector2(CurrentX + (IconColumnWidth - IconSize.X) / 2.0f, CurrentY + (RowHeight - IconSize.Y) / 2.0f); e.SpriteBatch.Draw(IconTexture, IconPosition, IconSourcePosition, Color.White, 0f, Vector2.Zero, IconScale, IconEffects, 1f); CurrentX += IconColumnWidth + MarginAfterIcon; // Draw the value double CurrentEffect = Augmentor.ComputeEffect(KVP.Key, KVP.Value, MI.RequiresInput); string Text = string.Format("{0}% ({1})", (CurrentEffect * 100.0).ToString("0.#"), KVP.Value); Vector2 TextSize = DrawHelpers.MeasureStringWithSpecialNumbers(Text, 1.0f, 0.0f); Vector2 TextPosition = new Vector2(CurrentX, CurrentY + (RowHeight - TextSize.Y) / 2.0f); DrawHelpers.DrawStringWithSpecialNumbers(e.SpriteBatch, TextPosition, Text, 1.0f, Color.White); CurrentY += RowHeight; } //Maybe also show MinutesUntilReady if it's not ReadyForHarvest? } } } }
private void RegisterConsoleCommands() { List <string> ValidTypes = Enum.GetValues(typeof(AugmentorType)).Cast <AugmentorType>().Select(x => x.ToString()).ToList(); //Possible TODO: Add translation support for this command string CommandName = "player_addaugmentor"; string CommandHelp = string.Format("Adds augmentors of the given AugmentorType into your inventory.\n" + "Arguments: <AugmentorType> <Quantity>\n" + "Example: {0} EfficiencyAugmentor 6\n\n" + "Valid values for <AugmentorType>: {1}", CommandName, string.Join(", ", ValidTypes), string.Join(", ", ValidTypes)); Helper.ConsoleCommands.Add(CommandName, CommandHelp, (string Name, string[] Args) => { if (Game1.player.isInventoryFull()) { Monitor.Log("Unable to execute command: Inventory is full!", LogLevel.Alert); } else if (Args.Length < 2) { Monitor.Log("Unable to execute command: Required arguments missing!", LogLevel.Alert); } else { string TypeName = Args[0]; if (!Enum.TryParse(TypeName, out AugmentorType AugmentorType)) { Monitor.Log(string.Format("Unable to execute command: <AugmentorType> \"{0}\" is not valid. Expected valid values: {1}", TypeName, string.Join(", ", ValidTypes)), LogLevel.Alert); } else { if (!int.TryParse(Args[1], out int Quantity)) { Monitor.Log(string.Format("Unable to execute command: could not parse an integer from \"{0}\".", Args[1]), LogLevel.Alert); } else { Augmentor SpawnedItem = Augmentor.CreateInstance(AugmentorType, Quantity); Game1.player.addItemToInventory(SpawnedItem); } } } }); //Possible TODO: Add translation support for this command CommandName = "machine_augmentors_reload_config"; CommandHelp = "Reloads configuration settings from this mod's config.json file. Normally this file's settings are only loaded once when the game is started." + " Use this command if you've made changes to the config during this game session."; Helper.ConsoleCommands.Add(CommandName, CommandHelp, (string Name, string[] Args) => { try { LoadUserConfig(); Monitor.Log("config.json settings were successfully reloaded.", LogLevel.Alert); } catch (Exception ex) { Monitor.Log(string.Format("Machine Augmentors: Unhandled error while executing command: {0}", ex.Message), LogLevel.Error); } }); }
private void Display_MenuChanged(object sender, StardewModdingAPI.Events.MenuChangedEventArgs e) { // Add Augmentors items to the shop's stock if (e.NewMenu is ShopMenu NewShop && IsTravellingMerchantShop(NewShop)) { if (TodaysStock == null) { TodaysStock = new Dictionary <ISalable, int[]>(); // Pick the Augmentor types that will be sold today int NumTypes = Augmentor.WeightedRound(UserConfig.ShopSettings.NumAugmentorTypesInShop); List <AugmentorConfig> ChosenTypes = new List <AugmentorConfig>(); List <AugmentorConfig> RemainingTypes = new List <AugmentorConfig>(UserConfig.AugmentorConfigs); while (ChosenTypes.Count < NumTypes && RemainingTypes.Any()) { int TotalWeight = RemainingTypes.Sum(x => x.ShopAppearanceWeight); int ChosenWeight = Augmentor.Randomizer.Next(TotalWeight); // EX: If the remaining types had weights = { 3, 6, 2 }, picks random number from 0 to 10 inclusive. // Then the first is selected if ChosenWeight is 0-2 (3/11 chance), second is selected if 3-8 (6/11 chance), third is selected if 9-10 (2/11 chance) int CurrentSum = 0; for (int i = 0; i < RemainingTypes.Count; i++) { AugmentorConfig CurrentConfig = RemainingTypes[i]; CurrentSum += CurrentConfig.ShopAppearanceWeight; if (ChosenWeight < CurrentSum) { ChosenTypes.Add(CurrentConfig); RemainingTypes.RemoveAt(i); break; } } } // Add each type to today's stock foreach (AugmentorConfig Config in ChosenTypes) { // Compute price double BasePrice = Config.BasePrice * UserConfig.GlobalPriceMultiplier; double Price = BasePrice; if (UserConfig.ShopSettings.PriceDeviationRolls > 0) { double MinMultiplier = 1.0 - UserConfig.ShopSettings.PriceDeviation; double MaxMultiplier = 1.0 + UserConfig.ShopSettings.PriceDeviation; double Multiplier = Enumerable.Range(0, UserConfig.ShopSettings.PriceDeviationRolls).Select(x => Augmentor.GetRandomNumber(MinMultiplier, MaxMultiplier)).Average(); Price = Math.Round(BasePrice * Multiplier, MidpointRounding.AwayFromZero); } // Compute quantity double BaseQuantityInStock = UserConfig.ShopSettings.BaseQuantityInStock; double YearMultiplier = 1.0 + (UserConfig.ShopSettings.YearShopStockMultiplierBonus * (Game1.Date.Year - 1)); double DesiredValue = BaseQuantityInStock * Config.ShopStockMultiplier * YearMultiplier; int QuantityInStock = Math.Max(1, Augmentor.WeightedRound(DesiredValue)); Augmentor SellableInstance = Augmentor.CreateInstance(Config.AugmentorType, 1); TodaysStock.Add(SellableInstance, new int[] { (int)Price, QuantityInStock }); } } // Add today's stock to the shop if (TodaysStock.Any()) { Dictionary <ISalable, int[]> Stock = NewShop.itemPriceAndStock; foreach (KeyValuePair <ISalable, int[]> Item in TodaysStock) { if (Item.Value[1] > 0 && !Stock.ContainsKey(Item.Key)) { Stock.Add(Item.Key, Item.Value); } } NewShop.setItemPriceAndStock(Stock); } } }