public static void HandleLiquidateEvent(Item heldItem, int actualValue) { //if the player is holding only one item, don't let them transmute it to money unless they're holding shift. if (heldItem.Stack == 1 && !EquivalentExchange.IsShiftKeyPressed()) { return; } //placeholder for determining if the transmute occurs, so it knows to play a sound. bool didTransmuteOccur = false; //placeholder for determining if the transmute rebounds, so it knows to play a different sound. bool didTransmuteFail = false; //if the transmute did fail, this preserves the damage so we can apply it in one cycle, otherwise batches look weird af int reboundDamageTaken = 0; //needed for some profession effects bool isItemWorthLessThanOnePercentOfMoney = (Game1.player.money * 0.01F > actualValue); //stamina cost is overridden for conduits if money is > 100x the item's value. double staminaCost = (isItemWorthLessThanOnePercentOfMoney && Game1.player.professions.Contains(Professions.Conduit)) ? 0D : Alchemy.GetStaminaCostForTransmutation(actualValue); //if the player lacks the stamina to execute a transmute, abort if (Game1.player.Stamina <= staminaCost) { return; } //if we fail this check, it's because a rebound would kill the player. //if the rebound chance is zero, this check will automatically pass. if (!Alchemy.CanSurviveRebound(actualValue, isItemWorthLessThanOnePercentOfMoney, true)) { return; } //if we fail this check, transmutation will fail this cycle. //this is our "rebound check" if (Alchemy.DidPlayerFailReboundCheck(isItemWorthLessThanOnePercentOfMoney, true)) { reboundDamageTaken += Alchemy.GetReboundDamage(actualValue); didTransmuteFail = true; } //the conduit profession makes it so that the transmutation succeeds anyway, after taking damage. if (Game1.player.professions.Contains((int)Professions.Sage) || !didTransmuteFail) { //if we reached this point transmutation will succeed didTransmuteOccur = true; Alchemy.HandleStaminaDeduction(staminaCost, false); //we floor the math here because we don't want weirdly divergent values based on stack count - the rate is fixed regardless of quantity //this occurs at the expense of rounding - liquidation is lossy. int liquidationValue = (int)Math.Floor(Alchemy.GetLiquidationValuePercentage() * actualValue); int totalValue = liquidationValue; Game1.player.Money += totalValue; Game1.player.reduceActiveItemByOne(); //the percentage of experience you get is increased by the lossiness of the transmute //as you increase in levels, this amount diminishes to a minimum of 1. double experienceValueCoefficient = 1D - Alchemy.GetLiquidationValuePercentage(); int experienceValue = (int)Math.Floor(Math.Sqrt(experienceValueCoefficient * actualValue / 10 + 1)); Alchemy.AddAlchemyExperience(experienceValue); //a transmute (at least one) happened, play the cash money sound if (didTransmuteOccur && !didTransmuteFail) { SoundUtil.PlayMoneySound(); } } //a rebound occurred, apply the damage and also play the ouchy sound. if (didTransmuteFail) { Alchemy.TakeDamageFromRebound(reboundDamageTaken); SoundUtil.PlayReboundSound(); } }
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; } } }
public static void HandleTransmuteEvent(Item heldItem, int actualValue) { //cost of a single item, multiplied by the cost multiplier below int transmutationCost = (int)Math.Ceiling(Alchemy.GetTransmutationMarkupPercentage() * actualValue); //nor should totalCost of a single cycle int totalCost = transmutationCost; //placeholder for determining if the transmute occurs, so it knows to play a sound. bool didTransmuteOccur = false; //placeholder for determining if the transmute rebounds, so it knows to play a different sound. bool didTransmuteFail = false; //if the transmute did fail, this preserves the damage so we can apply it in one cycle, otherwise batches look weird af int reboundDamageTaken = 0; //needed for some profession effects bool isItemWorthLessThanOnePercentOfMoney = (Game1.player.money * 0.01F > actualValue); //stamina cost is overridden for conduits if money is > 100x the item's value. double staminaCost = (isItemWorthLessThanOnePercentOfMoney && Game1.player.professions.Contains(Professions.Conduit)) ? 0D : Alchemy.GetStaminaCostForTransmutation(actualValue); //loop for each transmute-cycle attempt if (Game1.player.money >= totalCost) { //if the player lacks the stamina to execute a transmute, abort if (Game1.player.Stamina <= staminaCost) { return; } //if we fail this check, it's because a rebound would kill the player. //if the rebound chance is zero, this check will automatically pass. if (!Alchemy.CanSurviveRebound(actualValue, isItemWorthLessThanOnePercentOfMoney, false)) { return; } //if we fail this check, transmutation will fail this cycle. //this is our "rebound check" if (Alchemy.DidPlayerFailReboundCheck(isItemWorthLessThanOnePercentOfMoney, false)) { reboundDamageTaken += Alchemy.GetReboundDamage(actualValue); didTransmuteFail = true; //the conduit profession makes it so that the transmutation succeeds anyway, after taking damage. } if (Game1.player.professions.Contains((int)Professions.Sage) || !didTransmuteFail) { didTransmuteOccur = true; Alchemy.HandleStaminaDeduction(staminaCost, true); Game1.player.Money -= totalCost; Item spawnedItem = heldItem.getOne(); Game1.createItemDebris(spawnedItem, Game1.player.getStandingPosition(), Game1.player.FacingDirection, (GameLocation)null); //the percentage of experience you get is increased by the lossiness of the transmute //as you increase in levels, this amount diminishes to a minimum of 1. double experienceValueCoefficient = Alchemy.GetTransmutationMarkupPercentage() - 1D; int experienceValue = (int)Math.Floor(Math.Sqrt(experienceValueCoefficient * actualValue / 10 + 1)); Alchemy.AddAlchemyExperience(experienceValue); } } //a transmute (at least one) happened, play the magicky sound if (didTransmuteOccur && !didTransmuteFail) { SoundUtil.PlayMagickySound(); } //a rebound occurred, apply the damage and also play the ouchy sound. if (didTransmuteFail) { Alchemy.TakeDamageFromRebound(reboundDamageTaken); SoundUtil.PlayReboundSound(); } }