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); }
//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(); } }
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(); } }