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();
            }
        }
Exemple #2
0
        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();
            }
        }