public static string GetProfessionDescription(int whichProfession) { switch (whichProfession) { case Professions.Shaper: return("Lucky transmutes affected by daily luck twice as much (1-25% is now 2-50%)."); case Professions.Sage: return($"When you rebound, you still take damage but you succeed anyway."); case Professions.Transmuter: double nextCoefficientCost = Alchemy.GetTransmutationMarkupPercentage(10) * 100D; return($"Transmutation (item creation only) is twice as lucky."); case Professions.Adept: return($"Leyline proximity increases your lucky rate up to 15%."); case Professions.Aurumancer: double nextCoefficientValue = Alchemy.GetLiquidationValuePercentage(10) * 100D; return($"Liquidation of items worth less than 1% of your current money can't rebound."); case Professions.Conduit: return($"Transmutation or liquidation worth less than 1% of your money doesn't cost stamina."); } return(""); }
public List <string> GetExtraInfoForLevel(int whichLevel, int luckLevel) { double nextCoefficientCost = (Alchemy.GetTransmutationMarkupPercentage(whichLevel) - Alchemy.TRANSMUTATION_BONUS_PER_LEVEL) * 100D; double nextCoefficientValue = (Alchemy.GetLiquidationValuePercentage(whichLevel) + Alchemy.LIQUIDATION_BONUS_PER_LEVEL) * 100D; string coefficientCost = $"Cost: {nextCoefficientCost.ToString()}% Value: {nextCoefficientValue.ToString()}%"; double luckyTransmuteMinimum = ((Alchemy.GetLuckyTransmuteChanceWithoutDailyOrProfessionBonuses(whichLevel, luckLevel) + 0.01) * 100); double luckyTransmuteMaximum = ((Alchemy.GetLuckyTransmuteChanceWithoutDailyOrProfessionBonuses(whichLevel, luckLevel) + Alchemy.LUCK_NORMALIZATION_FOR_FREE_TRANSMUTES) * 100); string luckyTransmuteChance = $"Lucky transmutes: {luckyTransmuteMinimum.ToString()}-{luckyTransmuteMaximum.ToString()}% Stamina drain -{ ((1 - Alchemy.GetAlchemyStaminaCostSkillMultiplierForLevel(whichLevel)) * 100).ToString() }%"; string distanceFromTowerImpact = $"Leyline distance negated by { (whichLevel) }."; List <string> extraInfoList = new List <string>(); extraInfoList.Add(coefficientCost); extraInfoList.Add(luckyTransmuteChance); extraInfoList.Add(distanceFromTowerImpact); return(extraInfoList); }
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 HandleNormalizeEvent(Item heldItem, int actualValue) { //get the id of the item the player is holding int itemID = heldItem.parentSheetIndex; //if it's a blacklisted item, abort. if (EquivalentExchange.blackListedItemIDs.Contains(itemID)) { return; } //declare vars to remember how many items of each quality the player has. float normalQuality = 0; int silverQuality = 0; int goldQuality = 0; int iridiumQuality = 0; //search the inventory for items of the same type foreach (Item inventoryItem in Game1.player.items) { if (inventoryItem == null) { continue; } if (inventoryItem.parentSheetIndex != itemID) { continue; } //if the item can't be cast as an object, abort. StardewValley.Object itemObject = inventoryItem as StardewValley.Object; if (itemObject == null) { return; } switch (itemObject.quality) { case 0: normalQuality += itemObject.Stack; break; case 1: silverQuality += itemObject.Stack; break; case 2: goldQuality += itemObject.Stack; break; case 4: iridiumQuality += itemObject.Stack; break; default: break; } } //destroy all the items while (Game1.player.hasItemInInventory(itemID, 1)) { Game1.player.removeFirstOfThisItemFromInventory(itemID); } //calculate the normalized value of all qualities normalQuality += (5F / 4F) * silverQuality; normalQuality += (3F / 2F) * goldQuality; normalQuality += (2F) * iridiumQuality; float remainder = normalQuality % 1F; normalQuality -= normalQuality % 1F; StardewValley.Object newItemObject = new StardewValley.Object(itemID, 1); //the remainder is liquidated, and the liquidation factor of your skill level is applied. remainder *= newItemObject.sellToStorePrice() * (float)Alchemy.GetLiquidationValuePercentage(); while (normalQuality > 0) { newItemObject = new StardewValley.Object(itemID, 1); if (Game1.player.couldInventoryAcceptThisItem((Item)newItemObject)) { Game1.player.addItemToInventory((Item)newItemObject); } else { Game1.createItemDebris((Item)newItemObject, Game1.player.getStandingPosition(), Game1.player.FacingDirection, (GameLocation)null); } normalQuality--; } //floored, any excess is truncated. Sorry math. Game1.player.Money += (int)Math.Floor(remainder); }
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(); } }