private static void GraphicsEvents_OnPreRenderHudEvent(object sender, EventArgs e) { if (!Context.IsWorldReady) { return; } if (Game1.eventUp) { return; } if (!allowInfoBubbleToRender) { return; } //per the advice of Ento, abort if the player is in an event if (Game1.CurrentEvent != null) { return; } //something may have gone wrong if this is null, maybe there's no save data? if (Game1.player != null) { //get the player's current item Item heldItem = Game1.player.CurrentItem; //player is holding item if (heldItem != null) { //get the item's ID int heldItemID = heldItem.parentSheetIndex; //abort any transmutation event for blacklisted items or items that for whatever reason can't exist in world. if (blackListedItemIDs.Contains(heldItemID) || !heldItem.canBeDropped()) { return; } //get the transmutation value, it's based on what it's worth to the player, including profession bonuses. This affects both cost and value. int actualValue = ((StardewValley.Object)heldItem).sellToStorePrice(); int transmuteCost = (int)Math.Ceiling(Alchemy.GetTransmutationMarkupPercentage() * actualValue); int liquidateValue = (int)Math.Floor(Alchemy.GetLiquidationValuePercentage() * actualValue); float staminaDrain = (float)Math.Round(Alchemy.GetStaminaCostForTransmutation(actualValue), 2); float luckyChance = (float)Math.Round(Alchemy.GetLuckyTransmuteChance() * 100, 2); float reboundChance = (float)Math.Round(Alchemy.GetReboundChance(false, false) * 100, 2); int reboundDamage = Alchemy.GetReboundDamage(actualValue); int xPos = -15; int yPos = 0;// Game1.viewport.Height / 2 - 200; int xSize = 240; int ySize = 320; int dialogPositionMarkerX = xPos + 40; int dialogPositionMarkerY = yPos + 100; string cost = $"Make -{transmuteCost.ToString()}g"; string value = $"Melt +{liquidateValue.ToString()}g"; string luck = $"Luck {luckyChance.ToString()}%"; string stam = $"Stam -{staminaDrain.ToString()}"; string rebound = $"Fail {reboundChance.ToString()}%"; string damage = $"HP -{reboundDamage.ToString()}"; int rowSpacing = 30; Game1.drawDialogueBox(xPos, yPos, xSize, ySize, false, true, (string)null, false); Game1.spriteBatch.DrawString(Game1.smallFont, cost, new Microsoft.Xna.Framework.Vector2(dialogPositionMarkerX, dialogPositionMarkerY), Game1.textColor); dialogPositionMarkerY += rowSpacing; Game1.spriteBatch.DrawString(Game1.smallFont, value, new Microsoft.Xna.Framework.Vector2(dialogPositionMarkerX, dialogPositionMarkerY), Game1.textColor); dialogPositionMarkerY += rowSpacing; Game1.spriteBatch.DrawString(Game1.smallFont, luck, new Microsoft.Xna.Framework.Vector2(dialogPositionMarkerX, dialogPositionMarkerY), Game1.textColor); dialogPositionMarkerY += rowSpacing; Game1.spriteBatch.DrawString(Game1.smallFont, stam, new Microsoft.Xna.Framework.Vector2(dialogPositionMarkerX, dialogPositionMarkerY), Game1.textColor); dialogPositionMarkerY += rowSpacing; Game1.spriteBatch.DrawString(Game1.smallFont, rebound, new Microsoft.Xna.Framework.Vector2(dialogPositionMarkerX, dialogPositionMarkerY), Game1.textColor); dialogPositionMarkerY += rowSpacing; Game1.spriteBatch.DrawString(Game1.smallFont, damage, new Microsoft.Xna.Framework.Vector2(dialogPositionMarkerX, dialogPositionMarkerY), Game1.textColor); dialogPositionMarkerY += rowSpacing; } } }