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(""); }
//sets up the basic structure of either transmute event, since they have some common ground private static void HandleEitherTransmuteEvent(string keyPressed) { // save is loaded if (Context.IsWorldReady) { //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(); //try to transmute [copy] the item if (keyPressed.ToString() == instance.Config.TransmuteKey) { Alchemy.HandleTransmuteEvent(heldItem, actualValue); } //try to liquidate the item [sell for gold] if (keyPressed.ToString() == instance.Config.LiquidateKey) { Alchemy.HandleLiquidateEvent(heldItem, actualValue); } //try to normalize the item [make all items of a different quality one quality and exchange any remainder for gold] if (keyPressed.ToString() == instance.Config.NormalizeKey) { Alchemy.HandleNormalizeEvent(heldItem, actualValue); } } } } }
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); }
public static bool HandleTransmuteEvent(Item heldItem, int actualValue) { // if the recipes list doesn't contain the item you're holding, you can't transmute that. var recipes = EquivalentExchange.GetTransmutationFormulas(); // sorted recipe list var validRecipes = recipes.GetRecipesForOutput(heldItem.parentSheetIndex); if (validRecipes.Count == 0) { return(true); } // use more sorting magic to find a potential recipe from the player's inventory // prioritize the recipes by their input costs (cheapest inputs are prioritized by the function GetRecipesForOutput) var optimalRecipe = validRecipes.FindBestRecipe(Game1.player); // something has stopped us from finding a valid recipe. The player either doesn't have the necessary items // or doesn't have the energy to do the transmutation. if (optimalRecipe == null) { return(true); } var breakRepeaterLoop = false; Alchemy.HandleAlchemyEnergyDeduction(optimalRecipe.GetEnergyCost(), false); if (optimalRecipe.GetEnergyCost() > EquivalentExchange.CurrentEnergy) { breakRepeaterLoop = true; } Alchemy.IncreaseTotalTransmuteValue((int)Math.Floor(Math.Max(1D, optimalRecipe.GetEnergyCost()))); Util.TakeItemFromPlayer(optimalRecipe.InputId, optimalRecipe.GetInputCost(), Game1.player); Item spawnedItem = heldItem.getOne(); spawnedItem.Stack = optimalRecipe.GetOutputQuantity(); Util.GiveItemToPlayer((StardewValley.Object)spawnedItem, Game1.player); SoundUtil.PlayMagickySound(); return(breakRepeaterLoop); }
public static void DoPostRenderHudEvent() { if (Game1.activeClickableMenu != null) { return; } Type t = Type.GetType("ExperienceBars.Mod, ExperienceBars"); int currentAlchemyLevel = EquivalentExchange.instance.currentPlayerData.AlchemyLevel; int currentAlchemyExperience = EquivalentExchange.instance.currentPlayerData.AlchemyExperience; int x = 10; int y = (int)Util.GetStaticField(t, "expBottom"); int previousExperienceRequired = 0, nextExperienceRequired = 1; if (currentAlchemyLevel == 0) { nextExperienceRequired = Alchemy.GetAlchemyExperienceNeededForNextLevel(); } else if (currentAlchemyLevel != 10) { previousExperienceRequired = Alchemy.GetAlchemyExperienceNeededForLevel(currentAlchemyLevel - 1); nextExperienceRequired = Alchemy.GetAlchemyExperienceNeededForLevel(currentAlchemyLevel); } int progressTowardCurrentLevel = currentAlchemyExperience - previousExperienceRequired; int experienceGapForCurrentLevel = nextExperienceRequired - previousExperienceRequired; float progressBarPercentage = (float)progressTowardCurrentLevel / experienceGapForCurrentLevel; if (currentAlchemyLevel == 10) { progressBarPercentage = -1; } object[] args = new object[] { x, y, alchemySkillIcon, new Rectangle(0, 0, 16, 16), currentAlchemyLevel, progressBarPercentage, new Color(196, 79, 255), }; Util.CallStaticMethod(t, "renderSkillBar", args); Util.SetStaticField(t, "expBottom", y + 40); }
//handles draining energy/stamina on successful transmute public static void HandleAlchemyEnergyDeduction(double energyCost, bool isForcedStaminaDrain) { double remainingStaminaCost = energyCost; // if the stamina drain is "forced" it means you can't pay for this transaction with energy // the *entire* cost goes to stamina. if (!isForcedStaminaDrain) { //if you have any alkahestry energy, it will try to use as much as it can double alkahestryCost = (double)Math.Min(EquivalentExchange.CurrentEnergy, energyCost); //and deduct that from whatever stamina cost might be left over (which may be all of it) remainingStaminaCost -= alkahestryCost; Alchemy.ReduceAlkahestryEnergy(alkahestryCost); } Game1.player.Stamina -= (float)remainingStaminaCost; EquivalentExchange.AddAlchemyExperience((int)Math.Floor(Math.Max(energyCost, 1D))); }
//command to give yourself experience for debug purposes primarily private void GiveAlchemyExperience(object sender, string[] args) { if (args.Length != 1) { Log.info("Command format: giveAlchemyExp <amount>"); return; } int amt = 0; try { amt = Convert.ToInt32(args[0]); } catch (Exception e) { Log.error("Bad experience amount."); return; } Alchemy.AddAlchemyExperience(amt); Log.info("Added " + amt + " alchemy experience."); }
private static void HandleToolTransmuteConsequence(float cost) { Alchemy.HandleAlchemyEnergyDeduction(GetToolTransmutationEnergyCost(EquivalentExchange.AlchemyLevel, cost), false); Alchemy.HandleAlchemyEnergyDeduction(GetToolTransmutationStaminaCost(EquivalentExchange.AlchemyLevel, cost), true); Alchemy.IncreaseTotalTransmuteValue((int)Math.Floor(cost)); }
//this was almost entirely stolen from spacechase0 with very little contribution on my part. internal static void HandleToolTransmute(Tool tool) { int alchemyLevel = (EquivalentExchange.IsShiftKeyPressed() ? 0 : Alchemy.GetToolTransmuteRadius()); int toolLevel = tool.UpgradeLevel; //set last user to dodge a null pointer var toolPlayerFieldReflector = tool.GetType().GetField("lastUser", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); toolPlayerFieldReflector.SetValue(tool, Game1.player); Point hitLocation = GetMouseHitLocation(); GameLocation location = Game1.player.currentLocation; bool performedAction = false; //getting this out of the way, helps with easily determining tool types bool isScythe = tool is MeleeWeapon && tool.Name.ToLower().Contains("scythe"); bool isAxe = tool is StardewValley.Tools.Axe; bool isPickaxe = tool is StardewValley.Tools.Pickaxe; bool isHoe = tool is StardewValley.Tools.Hoe; bool isWateringCan = tool is StardewValley.Tools.WateringCan; for (int xOffset = -alchemyLevel; xOffset <= alchemyLevel; xOffset++) { for (int yOffset = -alchemyLevel; yOffset <= alchemyLevel; yOffset++) { if (!isScythe) { if (!IsCapableOfWithstandingToolTransmuteCost(Game1.player, 2F)) { return; } } Vector2 offsetPosition = new Vector2(xOffset + hitLocation.X, yOffset + hitLocation.Y); if (location.objects.ContainsKey(offsetPosition)) { if (isAxe || isScythe || isPickaxe || isHoe) { var snapshotPlayerExperience = Game1.player.experiencePoints; performedAction = DoToolFunction(location, Game1.player, tool, (int)offsetPosition.X, (int)offsetPosition.Y); RestorePlayerExperience(snapshotPlayerExperience); if (performedAction && !isScythe) { HandleToolTransmuteConsequence(2F); } } } else if (location.terrainFeatures.ContainsKey(offsetPosition)) { //a terrain feature, rather than a tool check, might respond to the tool TerrainFeature terrainFeature = location.terrainFeatures[offsetPosition]; //don't break stumps unless the player is in precision mode. if (terrainFeature is Tree && isAxe && (!(terrainFeature as Tree).stump || EquivalentExchange.IsShiftKeyPressed())) { Netcode.NetArray <int, Netcode.NetInt> snapshotPlayerExperience = Game1.player.experiencePoints; //trees get removed automatically performedAction = DoToolFunction(location, Game1.player, tool, (int)offsetPosition.X, (int)offsetPosition.Y); RestorePlayerExperience(snapshotPlayerExperience); if (performedAction) { HandleToolTransmuteConsequence(2F); } } else if (terrainFeature is Grass && location is Farm && isScythe) { int oldHay = (location as Farm).piecesOfHay; var snapshotPlayerExperience = Game1.player.experiencePoints; if (terrainFeature.performToolAction(tool, 0, offsetPosition, location)) { location.terrainFeatures.Remove(offsetPosition); //HandleToolTransmuteConsequence(); Scythe transmute is special and doesn't cost anything, but you don't get experience. performedAction = true; } RestorePlayerExperience(snapshotPlayerExperience); //hay get! spawn the sprite animation for acquisition of hay if (oldHay < (location as Farm).piecesOfHay) { SpawnHayAnimationSprite(location, offsetPosition, Game1.player); } } else if (terrainFeature is HoeDirt && isWateringCan && (tool as WateringCan).WaterLeft > 0) { //state of 0 is unwatered. if ((terrainFeature as HoeDirt).state != 1) { var snapshotPlayerExperience = Game1.player.experiencePoints; terrainFeature.performToolAction(tool, 0, offsetPosition, location); RestorePlayerExperience(snapshotPlayerExperience); (tool as WateringCan).WaterLeft = (tool as WateringCan).WaterLeft - 1; SpawnWateringCanAnimationSprite(location, offsetPosition); HandleToolTransmuteConsequence(2F); performedAction = true; } } else if (isPickaxe && terrainFeature is HoeDirt) { var snapshotPlayerExperience = Game1.player.experiencePoints; performedAction = DoToolFunction(location, Game1.player, tool, (int)offsetPosition.X, (int)offsetPosition.Y); RestorePlayerExperience(snapshotPlayerExperience); if (performedAction) { HandleToolTransmuteConsequence(2F); } } } else if ((isPickaxe || isAxe)) { ICollection <ResourceClump> largeResourceClusters = null; if (location is Farm) { largeResourceClusters = (NetCollection <ResourceClump>)location.GetType().GetField("resourceClumps", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetValue(location); } else if (location is MineShaft) { largeResourceClusters = (NetObjectList <ResourceClump>)location.GetType().GetField("resourceClumps", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetValue(location); } else if (location is Woods) { largeResourceClusters = (location as Woods).stumps; } DoLargeResourceClusterAction(largeResourceClusters, tool, offsetPosition, performedAction); } else if (isHoe) { var snapshotPlayerExperience = Game1.player.experiencePoints; performedAction = DoToolFunction(location, Game1.player, tool, (int)offsetPosition.X, (int)offsetPosition.Y); RestorePlayerExperience(snapshotPlayerExperience); if (performedAction) { HandleToolTransmuteConsequence(2F); } } } } if (performedAction) { SoundUtil.PlayMagickySound(); } }
//sets up the basic structure of either transmute event, since they have some common ground private static void HandleEitherTransmuteEvent(string keyPressed) { // save is loaded if (Context.IsWorldReady) { //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; //alchemy energy can be used to execute a complex tool action if a tool is in hand. if (heldItem is StardewValley.Tool && keyPressed.ToString() == instance.Config.TransmuteKey) { Tool itemTool = heldItem as Tool; bool isScythe = itemTool is MeleeWeapon && itemTool.Name.ToLower().Contains("scythe"); bool isAxe = itemTool is Axe; bool isPickaxe = itemTool is Pickaxe; bool isHoe = itemTool is Hoe; bool isWateringCan = itemTool is WateringCan; bool canDoToolAlchemy = isScythe || isAxe || isPickaxe || isHoe || isWateringCan; if (canDoToolAlchemy) { Alchemy.HandleToolTransmute(itemTool); } } //try to normalize the item [make all items of a different quality one quality and exchange any remainder for gold] if (keyPressed.ToString() == instance.Config.NormalizeKey) { Alchemy.HandleNormalizeEvent(heldItem); return; } //abort any transmutation event for blacklisted items or items that for whatever reason can't exist in world. if (!GetTransmutationFormulas().HasItem(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(); //try to transmute [copy] the item if (keyPressed.ToString() == instance.Config.TransmuteKey) { var shouldBreakOutOfRepeater = Alchemy.HandleTransmuteEvent(heldItem, actualValue); if (shouldBreakOutOfRepeater && !brokeRepeaterDueToNoEnergy) { brokeRepeaterDueToNoEnergy = true; instance.heldCounter = 1; instance.updateTickCount = AUTO_REPEAT_UPDATE_RATE_REFRESH * 2; } } } } } }
private void SpaceEvents_ShowNightEndMenus(object sender, EventArgsShowNightEndMenus e) { //the new day hook seems to be inconsistent, so this is a full restore at the end of the night. Alchemy.RestoreAlkahestryEnergyForNewDay(); }
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(); } }
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(); } }