private static KeyValuePair <StardewObject, int> ChangeBerryBushHarvest(StardewObject item, Vector2 tile, GameLocation location) { if (item == null || (item.ParentSheetIndex != 296 && item.ParentSheetIndex != 410)) { return(new KeyValuePair <StardewObject, int>(item, 0)); } int expAmount = 0; double chance = mod.Config.BerryBushChanceToGetXP / 100.0; if (mod.Config.AutomationHarvestsGrantXP && Game1.random.NextDouble() < chance) { expAmount = mod.Config.BerryBushXPAmount; } if (mod.Config.BerryBushQuality) { item.Quality = ForageFantasy.DetermineForageQuality(Game1.player); } else { item.Quality = Game1.MasterPlayer.professions.Contains(Farmer.botanist) ? StardewObject.bestQuality : StardewObject.lowQuality; } return(new KeyValuePair <StardewObject, int>(item, expAmount)); }
public static void RewardMushroomBoxExp(ForageFantasy mod, Farmer player) { if (mod.Config.MushroomXPAmount > 0) { player.gainExperience(2, mod.Config.MushroomXPAmount); } }
private static int DetermineTreeQuality(ForageFantasy mod, Tree tree) { string moddata; tree.modData.TryGetValue($"{mod.ModManifest.UniqueID}/treeAge", out moddata); if (!string.IsNullOrEmpty(moddata)) { int age = int.Parse(moddata); bool useMonths = mod.Config.TapperQualityOptions == 3; int timeForLevelUp = useMonths ? 28 : 28 * 4; if (age < timeForLevelUp) { return(0); } else if (age < timeForLevelUp * 2) { return(1); } else if (age < timeForLevelUp * 3) { return(2); } else { return(4); } } return(0); }
public static void RewardTapperExp(ForageFantasy mod, Farmer player) { if (mod.Config.TapperXPAmount > 0) { player.gainExperience(2, mod.Config.TapperXPAmount); } }
public static bool PatchMushroomBoxMachineOutput(ref object __instance) { try { if (mod.Config.AutomationHarvestsGrantXP) { TapperAndMushroomQualityLogic.RewardMushroomBoxExp(mod); } var mushroomBox = mod.Helper.Reflection.GetProperty <StardewObject>(__instance, "Machine").GetValue(); if (!mod.Config.MushroomBoxQuality) { mushroomBox.heldObject.Value.quality.Value = 0; } else { mushroomBox.heldObject.Value.quality.Value = ForageFantasy.DetermineForageQuality(Game1.player); } return(true); } catch (Exception e) { mod.ErrorLog("There was an exception in a patch", e); return(true); } }
// reset every end of the day so we don't accidentally save and then permanently edit a crop, if the mod gets uninstalled public static void ResetGrapes(ForageFantasy mod) { if (!Context.IsMainPlayer || !AreGrapeJsonModsInstalled(mod)) { return; } ReplaceGrapeStarterDrop(398); }
public static void RewardBerryXP(ForageFantasy mod) { double chance = mod.Config.BerryBushChanceToGetXP / 100.0; if (mod.Config.BerryBushXPAmount > 0 && Game1.random.NextDouble() < chance) { Game1.player.gainExperience(2, mod.Config.BerryBushXPAmount); } }
public static bool PatchTapperAndMushroomQuality(ref StardewObject __instance, ref Farmer who, ref bool justCheckingForActivity) { try { if (!justCheckingForActivity && __instance != null && __instance.minutesUntilReady <= 0 && __instance.heldObject != null && __instance.heldObject.Value != null) { if (TapperAndMushroomQualityLogic.IsTapper(__instance)) { TapperAndMushroomQualityLogic.RewardTapperExp(mod); if (mod.Config.TapperQualityOptions <= 0 && mod.Config.TapperQualityOptions > 4) { __instance.heldObject.Value.quality.Value = 0; return(true); } TerrainFeature terrain; who.currentLocation.terrainFeatures.TryGetValue(__instance.TileLocation, out terrain); if (terrain != null && terrain is Tree tree) { __instance.heldObject.Value.quality.Value = TapperAndMushroomQualityLogic.DetermineTapperQuality(mod, who, __instance, tree); } else { __instance.heldObject.Value.quality.Value = 0; } return(true); } if (TapperAndMushroomQualityLogic.IsMushroomBox(__instance)) { TapperAndMushroomQualityLogic.RewardMushroomBoxExp(mod); if (!mod.Config.MushroomBoxQuality) { __instance.heldObject.Value.quality.Value = 0; } else { __instance.heldObject.Value.quality.Value = ForageFantasy.DetermineForageQuality(who); } return(true); } } return(true); } catch (Exception e) { mod.ErrorLog("There was an exception in a patch", e); return(true); } }
public static void SetDropToNewGrapes(ForageFantasy mod) { if (!Context.IsMainPlayer || !AreGrapeJsonModsInstalled(mod)) { return; } if (TryToGetGrapeID(mod, out int res)) { ReplaceGrapeStarterDrop(res); } }
public static int DetermineTapperQuality(ForageFantasy mod, Farmer player, Tree tree) { int option = mod.Config.TapperQualityOptions; if (option is 1 or 2) { // has tapper profession or it's not required if (!mod.Config.TapperQualityRequiresTapperPerk || player.professions.Contains(Farmer.tapper)) { return(ForageFantasy.DetermineForageQuality(player, mod.Config.TapperQualityOptions == 1)); } }
private static KeyValuePair <StardewObject, int> ChangeMushroomHarvest(StardewObject item, Vector2 tile, GameLocation location) { if (item == null) { return(new KeyValuePair <StardewObject, int>(item, 0)); } int expAmount = mod.Config.AutomationHarvestsGrantXP ? mod.Config.MushroomXPAmount : 0; item.Quality = mod.Config.MushroomBoxQuality ? ForageFantasy.DetermineForageQuality(Game1.MasterPlayer) : StardewObject.lowQuality; return(new KeyValuePair <StardewObject, int>(item, expAmount)); }
public static void Setup(ForageFantasy forageFantasy) { mod = forageFantasy; IDeluxeGrabberReduxApi api = mod.Helper.ModRegistry.GetApi <IDeluxeGrabberReduxApi>("ferdaber.DeluxeGrabberRedux"); if (api == null) { return; } api.GetBerryBushHarvest += ChangeBerryBuchHarvest; api.GetMushroomHarvest += ChangeMushroomHarvest; }
public static void IncreaseTreeAge(ForageFantasy mod, Tree tree) { string moddata; tree.modData.TryGetValue($"{mod.ModManifest.UniqueID}/treeAge", out moddata); if (!string.IsNullOrEmpty(moddata)) { int age = int.Parse(moddata); tree.modData[$"{mod.ModManifest.UniqueID}/treeAge"] = (age + 1).ToString(); } else { tree.modData[$"{mod.ModManifest.UniqueID}/treeAge"] = 1.ToString(); } }
public static void VerifyConfigValues(ForageFantasyConfig config, ForageFantasy mod) { bool invalidConfig = false; if (config.TapperQualityOptions < 0 || config.TapperQualityOptions > 4) { invalidConfig = true; config.TapperQualityOptions = 0; } if (config.BerryBushChanceToGetXP < 0) { invalidConfig = true; config.BerryBushChanceToGetXP = 0; } if (config.BerryBushChanceToGetXP > 100) { invalidConfig = true; config.BerryBushChanceToGetXP = 100; } if (config.BerryBushXPAmount < 0) { invalidConfig = true; config.BerryBushXPAmount = 0; } if (config.TapperXPAmount < 0) { invalidConfig = true; config.TapperXPAmount = 0; } if (config.MushroomXPAmount < 0) { invalidConfig = true; config.MushroomXPAmount = 0; } if (invalidConfig) { mod.DebugLog("At least one config value was out of range and was reset."); mod.Helper.WriteConfig(config); } }
public static void IncreaseTreeAges(ForageFantasy mod) { if (!Context.IsMainPlayer) { return; } foreach (var location in Game1.locations) { foreach (var terrainfeature in location.terrainFeatures.Pairs) { if (terrainfeature.Value is Tree tree) { IncreaseTreeAge(mod, tree); } } } }
public static bool TryAddItemToPlayerInventory_Pre(ref Farmer player, ref Item item, ref StardewObject container) { try { if (TapperAndMushroomQualityLogic.IsMushroomBox(container)) { if (mod.Config.MushroomBoxQuality) { (item as StardewObject).Quality = ForageFantasy.DetermineForageQuality(player); } } return(true); } catch (Exception e) { mod.ErrorLog("There was an exception in a patch", e); return(true); } }
private static bool TryToGetGrapeID(ForageFantasy mod, out int id) { if (!AreGrapeJsonModsInstalled(mod)) { id = 0; return(false); } var api = mod.Helper.ModRegistry.GetApi <IJsonAssetsApi>("spacechase0.JsonAssets"); if (api == null) { id = 0; return(false); } id = api.GetObjectId("Fine Grape"); return(true); }
public static void ChangeBundle(ForageFantasy mod) { if (!mod.Config.CommonFiddleheadFern) { return; } Dictionary <string, string> bundleData = Game1.netWorldState.Value.BundleData; // Summer Foraging string key = "Crafts Room/14"; string[] bundle = bundleData[key].Split('/'); if (!bundle[2].Contains("259 1 0")) { bundle[2] += " 259 1 0"; } bundleData[key] = string.Join("/", bundle); }
public static int DetermineTapperQuality(ForageFantasy mod, Farmer player, StardewObject o, Tree tree) { int option = mod.Config.TapperQualityOptions; if (option == 1 || option == 2) { // has tapper profession or it's not required if (!mod.Config.TapperQualityRequiresTapperPerk || player.professions.Contains(Farmer.tapper)) { return(ForageFantasy.DetermineForageQuality(player, mod.Config.TapperQualityOptions == 1)); } } else if (option == 3 || option == 4) { // quality increase once a year return(DetermineTreeQuality(mod, tree)); } // tapper perk required but doesn't have it or invalid option return(0); }
public static void ChangeBerryQualityAndGiveExp(Bush bush, ForageFantasy mod) { int shakeOff; string season = (bush.overrideSeason.Value == -1) ? Game1.GetSeasonForLocation(bush.currentLocation) : Utility.getSeasonNameFromNumber(bush.overrideSeason.Value); switch (season) { case "spring": shakeOff = 296; break; case "fall": shakeOff = 410; break; default: return; } bool gaveExp = false; foreach (var item in bush.currentLocation.debris) { if (item?.item?.ParentSheetIndex == shakeOff) { if (!gaveExp) { gaveExp = true; RewardBerryXP(mod); } if (mod.Config.BerryBushQuality) { ((StardewObject)item.item).Quality = ForageFantasy.DetermineForageQuality(Game1.player); } } } }
public static void Setup(ForageFantasy forageFantasy) { mod = forageFantasy; IDeluxeGrabberReduxApi api = mod.Helper.ModRegistry.GetApi <IDeluxeGrabberReduxApi>("ferdaber.DeluxeGrabberRedux"); if (api == null) { return; } // if neither is on, we can skip adding our overwrite for better compatibility with other mods if ((mod.Config.AutomationHarvestsGrantXP && mod.Config.BerryBushXPAmount > 0 && mod.Config.BerryBushChanceToGetXP > 0) || mod.Config.BerryBushQuality) { api.GetBerryBushHarvest += ChangeBerryBushHarvest; } // if neither is on, we can skip adding our overwrite for better compatibility with other mods if ((mod.Config.AutomationHarvestsGrantXP && mod.Config.MushroomXPAmount > 0) || mod.Config.MushroomBoxQuality) { api.GetMushroomHarvest += ChangeMushroomHarvest; } }
public static void SetUpModConfigMenu(ForageFantasyConfig config, ForageFantasy mod) { GenericModConfigMenuAPI api = mod.Helper.ModRegistry.GetApi <GenericModConfigMenuAPI>("spacechase0.GenericModConfigMenu"); if (api == null) { return; } var manifest = mod.ModManifest; api.RegisterModConfig(manifest, () => config = new ForageFantasyConfig(), delegate { mod.Helper.WriteConfig(config); VerifyConfigValues(config, mod); }); api.RegisterLabel(manifest, "Quality Tweaks", null); api.RegisterSimpleOption(manifest, "Berry Bush Quality", "Salmonberries and blackberries have quality based\non forage level even without botanist perk.", () => config.BerryBushQuality, (bool val) => config.BerryBushQuality = val); api.RegisterSimpleOption(manifest, "Mushroom Box Quality", "Mushrooms have quality based on forage level and botanist perk.", () => config.MushroomBoxQuality, (bool val) => config.MushroomBoxQuality = val); api.RegisterChoiceOption(manifest, "Tapper Quality Options", null, () => GetElementFromConfig(TQChoices, config.TapperQualityOptions), (string val) => config.TapperQualityOptions = GetIndexFromArrayElement(TQChoices, val), TQChoices); api.RegisterSimpleOption(manifest, "Tapper Perk Is Required", null, () => config.TapperQualityRequiresTapperPerk, (bool val) => config.TapperQualityRequiresTapperPerk = val); api.RegisterLabel(manifest, "XP Rewards", null); api.RegisterClampedOption(manifest, "Berry Bush Chance To Get XP", "Chance to get foraging experience when harvesting bushes.\nSet to 0 to disable feature.", () => config.BerryBushChanceToGetXP, (int val) => config.BerryBushChanceToGetXP = val, 0, 100); api.RegisterSimpleOption(manifest, "Berry Bush XP Amount", "Amount of XP gained per bush. For reference:\nChopping down a tree is 12XP, a foraging good is 7XP\nNegative values will be reset to 0.", () => config.BerryBushXPAmount, (int val) => config.BerryBushXPAmount = val); api.RegisterSimpleOption(manifest, "Mushroom Box XP Amount", "For reference:\nChopping down a tree is 12XP, a foraging good is 7XP\nNegative values will be reset to 0.", () => config.MushroomXPAmount, (int val) => config.MushroomXPAmount = val); api.RegisterSimpleOption(manifest, "Tapper XP Amount", "For reference:\nChopping down a tree is 12XP, a foraging good is 7XP\nNegative values will be reset to 0.", () => config.TapperXPAmount, (int val) => config.TapperXPAmount = val); api.RegisterSimpleOption(manifest, "Automation Harvests Grant XP", "Whether automatic harvests with the Automate, Deluxe\nGrabber Redux or One Click Shed Reloader should grant XP.\nKeep in mind that some of those only affect the host.", () => config.AutomationHarvestsGrantXP, (bool val) => config.AutomationHarvestsGrantXP = val); api.RegisterLabel(manifest, "Other Features", null); api.RegisterSimpleOption(manifest, "Common Fiddlehead Fern¹", "Fiddlehead fern is available outside of the secret forest\nand added to the wild seeds pack and summer foraging bundle.", () => config.CommonFiddleheadFern, (bool val) => config.CommonFiddleheadFern = val); api.RegisterSimpleOption(manifest, "Forage Survival Burger¹", "Forage based early game crafting recipes\nand even more efficient cooking recipes.", () => config.ForageSurvivalBurger, (bool val) => config.ForageSurvivalBurger = val); // this is a spacer api.RegisterLabel(manifest, string.Empty, null); api.RegisterLabel(manifest, "1: Restart Needed For Changes To Take Effect", null); }
public static void SetUpModConfigMenu(ForageFantasyConfig config, ForageFantasy mod) { IGenericModConfigMenuApi api = mod.Helper.ModRegistry.GetApi <IGenericModConfigMenuApi>("spacechase0.GenericModConfigMenu"); if (api == null) { return; } var manifest = mod.ModManifest; api.RegisterModConfig( manifest, delegate { // if the world is ready, then we are not in the main menu, so reset should only reset the keybindings and calendar if (Context.IsWorldReady) { config.TreeMenuKey = TreeMenuKeyDefault; config.MushroomTapperCalendar = false; } else { config = new ForageFantasyConfig(); } }, delegate { mod.Helper.WriteConfig(config); VerifyConfigValues(config, mod); } ); api.SetTitleScreenOnlyForNextOptions(manifest, true); api.RegisterLabel(manifest, "Quality Tweaks", null); if (mod.Helper.ModRegistry.IsLoaded("thelion.AwesomeProfessions")) { api.RegisterLabel(manifest, "Berry Bush Quality Disabled (Walk Of Life)", null); api.RegisterLabel(manifest, "Mushroom Box Quality Disabled (Walk Of Life)", null); } else { api.RegisterSimpleOption(manifest, "Berry Bush Quality", "Salmonberries and blackberries have quality based\non forage level even without botanist perk.", () => config.BerryBushQuality, (bool val) => config.BerryBushQuality = val); api.RegisterSimpleOption(manifest, "Mushroom Box Quality", "Mushrooms have quality based on forage level and botanist perk.", () => config.MushroomBoxQuality, (bool val) => config.MushroomBoxQuality = val); } api.RegisterChoiceOption(manifest, "Tapper Quality Options", null, () => GetElementFromConfig(TQChoices, config.TapperQualityOptions), (string val) => config.TapperQualityOptions = GetIndexFromArrayElement(TQChoices, val), TQChoices); api.RegisterSimpleOption(manifest, "Tapper Perk Is Required", null, () => config.TapperQualityRequiresTapperPerk, (bool val) => config.TapperQualityRequiresTapperPerk = val); api.RegisterLabel(manifest, "XP Rewards", null); api.RegisterClampedOption(manifest, "Berry Bush Chance To Get XP", "Chance to get foraging experience when harvesting bushes.\nSet to 0 to disable feature.", () => config.BerryBushChanceToGetXP, (int val) => config.BerryBushChanceToGetXP = val, 0, 100); api.RegisterSimpleOption(manifest, "Berry Bush XP Amount", "Amount of XP gained per bush. For reference:\nChopping down a tree is 12XP, a foraging good is 7XP\nNegative values will be reset to 0.", () => config.BerryBushXPAmount, (int val) => config.BerryBushXPAmount = val); api.RegisterSimpleOption(manifest, "Mushroom Box XP Amount", "For reference:\nChopping down a tree is 12XP, a foraging good is 7XP\nNegative values will be reset to 0.", () => config.MushroomXPAmount, (int val) => config.MushroomXPAmount = val); api.RegisterSimpleOption(manifest, "Tapper XP Amount", "For reference:\nChopping down a tree is 12XP, a foraging good is 7XP\nNegative values will be reset to 0.", () => config.TapperXPAmount, (int val) => config.TapperXPAmount = val); api.RegisterSimpleOption(manifest, "Automation Harvests Grant XP", "Whether automatic harvests with the Automate, Deluxe\nGrabber Redux or One Click Shed Reloader should grant XP.\nKeep in mind that some of those only affect the host.", () => config.AutomationHarvestsGrantXP, (bool val) => config.AutomationHarvestsGrantXP = val); api.RegisterLabel(manifest, "Tapper Days Needed Changes", null); api.RegisterSimpleOption(manifest, "Days Needed Changes Enabled", "If this is disabled, then all features\nin this category don't do anything", () => config.TapperDaysNeededChangesEnabled, (bool val) => config.TapperDaysNeededChangesEnabled = val); api.RegisterSimpleOption(manifest, "Maple Tree Days Needed", "default: 9 days, recommended: 7 days", () => config.MapleDaysNeeded, (int val) => config.MapleDaysNeeded = val); api.RegisterSimpleOption(manifest, "Oak Tree Days Needed", "default: 7 days, recommended: 7 days", () => config.OakDaysNeeded, (int val) => config.OakDaysNeeded = val); api.RegisterSimpleOption(manifest, "Pine Tree Days Needed", "default: 5 days, recommended: 7 days", () => config.PineDaysNeeded, (int val) => config.PineDaysNeeded = val); api.RegisterSimpleOption(manifest, "Mushroom Tree Heavy Tapper Fix", null, () => config.MushroomTreeHeavyTappersFix, (bool val) => config.MushroomTreeHeavyTappersFix = val); api.RegisterSimpleOption(manifest, "Mushroom Tree Tapper\nConsistency Change", null, () => config.MushroomTreeTappersConsistencyChange, (bool val) => config.MushroomTreeTappersConsistencyChange = val); api.RegisterLabel(manifest, "Other Features", null); api.RegisterSimpleOption(manifest, "Mushroom Tree Seeds Drop", null, () => config.MushroomTreeSeedsDrop, (bool val) => config.MushroomTreeSeedsDrop = val); api.RegisterSimpleOption(manifest, "Common Fiddlehead Fern", "Fiddlehead fern is available outside of the secret forest\nand added to the wild seeds pack and summer foraging bundle.", () => config.CommonFiddleheadFern, (bool val) => config.CommonFiddleheadFern = val); api.RegisterSimpleOption(manifest, "Forage Survival Burger", "Forage based early game crafting recipes\nand even more efficient cooking recipes.", () => config.ForageSurvivalBurger, (bool val) => config.ForageSurvivalBurger = val); api.SetTitleScreenOnlyForNextOptions(manifest, false); api.RegisterSimpleOption(manifest, "Mushroom Tapper Calendar", null, () => config.MushroomTapperCalendar, (bool val) => config.MushroomTapperCalendar = val); api.AddKeybindList(manifest, () => config.TreeMenuKey, (KeybindList keybindList) => config.TreeMenuKey = keybindList, () => "Tree Menu Key"); api.SetTitleScreenOnlyForNextOptions(manifest, true); if (GrapeLogic.AreGrapeJsonModsInstalled(mod)) { api.RegisterLabel(manifest, "Fine Grapes Feature Installed And Enabled", "Remove the Json Assets mod pack to disable this option"); } }
public static void VerifyConfigValues(ForageFantasyConfig config, ForageFantasy mod) { bool invalidConfig = false; if (config.MapleDaysNeeded <= 0) { invalidConfig = true; config.MapleDaysNeeded = 1; } if (config.PineDaysNeeded <= 0) { invalidConfig = true; config.PineDaysNeeded = 1; } if (config.OakDaysNeeded <= 0) { invalidConfig = true; config.OakDaysNeeded = 1; } if (config.TapperQualityOptions < 0 || config.TapperQualityOptions > 4) { invalidConfig = true; config.TapperQualityOptions = 0; } if (config.BerryBushChanceToGetXP < 0) { invalidConfig = true; config.BerryBushChanceToGetXP = 0; } if (config.BerryBushChanceToGetXP > 100) { invalidConfig = true; config.BerryBushChanceToGetXP = 100; } if (config.BerryBushXPAmount < 0) { invalidConfig = true; config.BerryBushXPAmount = 0; } if (config.TapperXPAmount < 0) { invalidConfig = true; config.TapperXPAmount = 0; } if (config.MushroomXPAmount < 0) { invalidConfig = true; config.MushroomXPAmount = 0; } if (mod.Helper.ModRegistry.IsLoaded("thelion.AwesomeProfessions")) { if (config.MushroomBoxQuality || config.BerryBushQuality) { invalidConfig = true; config.MushroomBoxQuality = false; config.BerryBushQuality = false; mod.DebugLog("Enabled Walk of Life compatibility."); } } try { // CommonFiddleheadFern and ForageSurvivalBurger mod.Helper.Content.InvalidateCache("Data/CraftingRecipes"); // CommonFiddleheadFern mod.Helper.Content.InvalidateCache("Data/Locations"); // ForageSurvivalBurger mod.Helper.Content.InvalidateCache("Data/CookingRecipes"); // Tapper days needed changes mod.Helper.Content.InvalidateCache("Data/ObjectInformation"); } catch (Exception e) { mod.DebugLog($"Exception when trying to invalidate cache on config change {e}"); } if (invalidConfig) { mod.DebugLog("At least one config value was out of range and was reset."); mod.Helper.WriteConfig(config); } }
public static void TestValues(ForageFantasy mod, bool showOnlyTotals, bool ignoreSecretForest) { List <string> seasons = new List <string>() { "summer" }; ////, "spring", "fall", "winter" }; foreach (var season in seasons) { int[][] results = new int[7][]; for (int i = 0; i < results.Length; i++) { results[i] = new int[1000]; } foreach (var loc in Game1.locations) { Random r = new Random(((int)Game1.uniqueIDForThisGame / 2) + (int)Game1.stats.DaysPlayed); Dictionary <string, string> locationData = Game1.content.Load <Dictionary <string, string> >("Data/Locations"); int id = -1; switch (loc.name) { case "BusStop": id = 0; break; case "Forest": id = 1; break; case "Mountain": id = 2; break; case "Railroad": id = 3; break; case "Woods": if (ignoreSecretForest) { continue; } id = 4; break; case "Town": id = 5; break; case "Backwoods": id = 6; break; default: continue; } if (locationData.ContainsKey(loc.name)) { string rawData = locationData[loc.name].Split(new char[] { '/' })[Utility.getSeasonNumber(season)]; //// && loc.numberOfSpawnedObjectsOnMap < 6) if (!rawData.Equals("-1")) { string[] split = rawData.Split(new char[] { ' ' }); int numberToSpawn = 100000; for (int i = 0; i < numberToSpawn; i++) { int xCoord = r.Next(loc.map.DisplayWidth / 64); int yCoord = r.Next(loc.map.DisplayHeight / 64); Vector2 location = new Vector2((float)xCoord, (float)yCoord); StardewValley.Object o; loc.objects.TryGetValue(location, out o); int whichObject = r.Next(split.Length / 2) * 2; if (r.NextDouble() < Convert.ToDouble(split[whichObject + 1], CultureInfo.InvariantCulture)) { o = new StardewValley.Object(location, Convert.ToInt32(split[whichObject]), null, false, true, false, true); results[id][o.ParentSheetIndex]++; } } } } } int[] totals = new int[1000]; float totaltotal = 0; for (int j = 0; j < results.Length; j++) { if (j == 4 && ignoreSecretForest) { continue; } string s = string.Empty; switch (j) { case 0: s += "BusStop"; break; case 1: s += "Forest"; break; case 2: s += "Mountain"; break; case 3: s += "Railroad"; break; case 4: s += "Woods"; break; case 5: s += "Town"; break; case 6: s += "Backwoods"; break; } float total = 0; for (int i = 0; i < results[j].Length; i++) { total += results[j][i]; } totaltotal += total; s += $"({total} items)"; if (!showOnlyTotals) { mod.DebugLog(s); mod.DebugLog(string.Empty); } for (int i = 0; i < results[j].Length; i++) { if (results[j][i] > 0) { var o = new StardewValley.Object(new Vector2(0, 0), i, null, false, true, false, true); int number = results[j][i]; float percentage = number / total; string percentageString = percentage.ToString("P", CultureInfo.InvariantCulture); if (!showOnlyTotals) { mod.DebugLog($"{o.DisplayName}: {number}, {percentageString}"); } } totals[i] += results[j][i]; } if (!showOnlyTotals) { mod.DebugLog(string.Empty); } } mod.DebugLog($"Total ({totaltotal} items)"); mod.DebugLog(string.Empty); for (int i = 0; i < totals.Length; i++) { if (totals[i] > 0) { var o = new StardewValley.Object(new Vector2(0, 0), i, null, false, true, false, true); int number = totals[i]; float percentage = number / totaltotal; string percentageString = percentage.ToString("P", CultureInfo.InvariantCulture); mod.DebugLog($"{o.DisplayName}: {number}, {percentageString}"); } } mod.DebugLog(string.Empty); } }
public static void Edit <T>(IAssetData asset, ForageFantasy mod) { if (mod.Config.TapperDaysNeededChangesEnabled && asset.AssetNameEquals("Data/ObjectInformation")) { /* here is the reasoning for the math * * normal tapper: * maple syrup 9 days 200g * oak resin 7 days 150g * pine tar 5 days 100g * * so 22,2g per day, 21,4g per day, 20g per day * * heavy tapper: * maple syrup 4 days 200g * oak resin 3 days 150g * pine tar 2 days 100g * * so 50g per day for all of them * * ---- * * wanted values: * maple syrup 7 days 150g * oak resin 7 days 150g * pine tar 7 days 150g * * so the calculation is: * newSellPrice = (int)Math.Round(daysNeeded * (150f / 7f), MidpointRounding.AwayFromZero); */ IDictionary <int, string> data = asset.AsDictionary <int, string>().Data; var priceChanges = new Dictionary <int, int>() { { 724, mod.Config.MapleDaysNeeded }, { 725, mod.Config.OakDaysNeeded }, { 726, mod.Config.PineDaysNeeded } }; foreach (var item in priceChanges) { var entry = data[item.Key]; var fields = entry.Split('/'); var newPrice = TapperAndMushroomQualityLogic.GetTapperProductValueForDaysNeeded(item.Value); fields[1] = newPrice.ToString(); data[item.Key] = string.Join("/", fields); } } if (mod.Config.CommonFiddleheadFern) { if (asset.AssetNameEquals("Data/CraftingRecipes")) { IDictionary <string, string> data = asset.AsDictionary <string, string>().Data; var entry = data["Wild Seeds (Su)"]; var fields = entry.Split('/'); fields[0] = "396 1 398 1 402 1 259 1"; data["Wild Seeds (Su)"] = string.Join("/", fields); } if (asset.AssetNameEquals("Data/Locations")) { IDictionary <string, string> data = asset.AsDictionary <string, string>().Data; var keys = data.Keys.ToList(); for (int i = 0; i < keys.Count; i++) { string location = keys[i]; string[] fields = data[location].Split('/'); switch (location) { case "BusStop": fields[1] = "396 .6 398 .6 402 .6"; break; case "Forest": fields[1] = "396 .8 398 .8 259 .8"; break; case "Mountain": fields[1] = "396 .7 398 .7 259 .8"; break; case "Backwoods": fields[1] = "396 .7 398 .7 259 .8"; break; case "Railroad": fields[1] = "396 .6 398 .6 402 .6"; break; case "Woods": fields[1] = "259 .7 420 .7"; break; } data[location] = string.Join("/", fields); } } } if (mod.Config.ForageSurvivalBurger) { var spring = mod.Helper.Translation.Get("SpringBurger"); var summer = mod.Helper.Translation.Get("SummerBurger"); var fall = mod.Helper.Translation.Get("FallBurger"); var winter = mod.Helper.Translation.Get("WinterBurger"); if (asset.AssetNameEquals("Data/CookingRecipes")) { IDictionary <string, string> data = asset.AsDictionary <string, string>().Data; data.Remove("Survival Burger"); data.Add("Survival Burger (Sp)", $"216 1 16 1 20 1 22 1/70 1/241 2/s Foraging 2/{spring}"); data.Add("Survival Burger (Su)", $"216 1 398 1 396 1 259 1/70 1/241 2/s Foraging 2/{summer}"); data.Add("Survival Burger (Fa)", $"216 1 404 1 406 1 408 1/70 1/241 2/s Foraging 2/{fall}"); data.Add("Survival Burger (Wi)", $"216 1 412 1 414 1 416 1/70 1/241 2/s Foraging 2/{winter}"); } if (asset.AssetNameEquals("Data/CraftingRecipes")) { IDictionary <string, string> data = asset.AsDictionary <string, string>().Data; data.Add("Survival Burger (Sp)", $"216 1 16 1 20 1 22 1/Field/241/false/s Foraging 2/{spring}"); data.Add("Survival Burger (Su)", $"216 1 398 1 396 1 259 1/Field/241/false/s Foraging 2/{summer}"); data.Add("Survival Burger (Fa)", $"216 1 404 1 406 1 408 1/Field/241/false/s Foraging 2/{fall}"); data.Add("Survival Burger (Wi)", $"216 1 412 1 414 1 416 1/Field/241/false/s Foraging 2/{winter}"); } } }
public static void PatchAll(ForageFantasy forageFantasy) { mod = forageFantasy; var harmony = new Harmony(mod.ModManifest.UniqueID); try { harmony.Patch( original: AccessTools.Method(typeof(StardewObject), "checkForAction"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchTapperAndMushroomQuality))); harmony.Patch( original: AccessTools.Method(typeof(Crop), "getRandomWildCropForSeason"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchSummerWildSeedResult))); harmony.Patch( original: AccessTools.Method(typeof(Bush), "shake"), prefix: new HarmonyMethod(typeof(Patcher), nameof(DetectHarvestableBerryBush))); harmony.Patch( original: AccessTools.Method(typeof(Bush), "shake"), postfix: new HarmonyMethod(typeof(Patcher), nameof(FixBerryQuality))); harmony.Patch( original: AccessTools.Method(typeof(Tree), nameof(Tree.UpdateTapperProduct)), postfix: new HarmonyMethod(typeof(Patcher), nameof(UpdateTapperProduct_Post))); harmony.Patch( original: AccessTools.Method(typeof(Tree), "shake"), prefix: new HarmonyMethod(typeof(Patcher), nameof(ShakeTree))); harmony.Patch( original: AccessTools.Method(typeof(Tree), "performSeedDestroy"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PerformSeedDestroy))); harmony.Patch( original: AccessTools.Method(typeof(Billboard), nameof(Billboard.draw), new Type[] { typeof(SpriteBatch) }), postfix: new HarmonyMethod(typeof(Patcher), nameof(Draw_Postfix))); } catch (Exception e) { mod.ErrorLog("Error while trying to setup required patches:", e); } if (mod.Helper.ModRegistry.IsLoaded("Pathoschild.Automate")) { try { mod.DebugLog("This mod patches Automate. If you notice issues with Automate, make sure it happens without this mod before reporting it to the Automate page."); // I don't see a use in using MachineWrapper because it's also internal I need to check for the type of the machine anyway which would be way too much reflection at runtime var mushroomBox = AccessTools.TypeByName("Pathoschild.Stardew.Automate.Framework.Machines.Objects.MushroomBoxMachine"); var tapper = AccessTools.TypeByName("Pathoschild.Stardew.Automate.Framework.Machines.Objects.TapperMachine"); var berryBush = AccessTools.TypeByName("Pathoschild.Stardew.Automate.Framework.Machines.TerrainFeatures.BushMachine"); harmony.Patch( original: AccessTools.Method(tapper, "GetOutput"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchTapperMachineOutput))); harmony.Patch( original: AccessTools.Method(mushroomBox, "GetOutput"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchMushroomBoxMachineOutput))); harmony.Patch( original: AccessTools.Method(berryBush, "GetOutput"), postfix: new HarmonyMethod(typeof(Patcher), nameof(PatchPostBushMachineXP))); } catch (Exception e) { mod.ErrorLog($"Error while trying to patch Automate. Please report this to the mod page of {mod.ModManifest.Name}, not Automate:", e); } } if (mod.Helper.ModRegistry.IsLoaded("BitwiseJonMods.OneClickShedReloader")) { try { mod.DebugLog("This mod patches OneClickShedReloader. If you notice issues with OneClickShedReloader, make sure it happens without this mod before reporting it to the OneClickShedReloader page."); var handler = AccessTools.TypeByName("BitwiseJonMods.BuildingContentsHandler"); var entry = AccessTools.TypeByName("BitwiseJonMods.ModEntry"); harmony.Patch( original: AccessTools.Method(handler, "TryAddItemToPlayerInventory"), prefix: new HarmonyMethod(typeof(Patcher), nameof(TryAddItemToPlayerInventory_Pre))); harmony.Patch( original: AccessTools.Method(handler, "TryAddItemToPlayerInventory"), postfix: new HarmonyMethod(typeof(Patcher), nameof(TryAddItemToPlayerInventory_Post))); harmony.Patch( original: AccessTools.Method(entry, "HarvestAllItemsInBuilding"), postfix: new HarmonyMethod(typeof(Patcher), nameof(ReduceQualityAfterHarvest))); } catch (Exception e) { mod.ErrorLog($"Error while trying to patch OneClickShedReloader. Please report this to the mod page of {mod.ModManifest.Name}, not OneClickShedReloader:", e); } } /* // in case I want this at some point * foreach (var method in Harmony.GetAllPatchedMethods()) * { * var patches = Harmony.GetPatchInfo(method); * * foreach (var patch in patches.Prefixes) * { * if (patch.owner == mod.ModManifest.UniqueID) * { * mod.TraceLog($"method: {method.Name} prefix: {patch.PatchMethod.Name}"); * } * } * * foreach (var patch in patches.Postfixes) * { * if (patch.owner == mod.ModManifest.UniqueID) * { * mod.TraceLog($"method: {method.Name} postfix: {patch.PatchMethod.Name}"); * } * } * * foreach (var patch in patches.Transpilers) * { * if (patch.owner == mod.ModManifest.UniqueID) * { * mod.TraceLog($"method: {method.Name} transpiler: {patch.PatchMethod.Name}"); * } * } * } */ }
public TreeMenu(ForageFantasy mod, TerrainFeature tree) : base(TreeTypeToName(tree)) { this.mod = mod; this.tree = tree; }
public static void PatchAll(ForageFantasy forageFantasy) { mod = forageFantasy; var harmony = HarmonyInstance.Create(mod.ModManifest.UniqueID); try { harmony.Patch( original: AccessTools.Method(typeof(StardewObject), "checkForAction"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchTapperAndMushroomQuality)) ); harmony.Patch( original: AccessTools.Method(typeof(Crop), "getRandomWildCropForSeason"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchSummerWildSeedResult)) ); harmony.Patch( original: AccessTools.Method(typeof(Bush), "shake"), prefix: new HarmonyMethod(typeof(Patcher), nameof(DetectHarvestableBerryBush)) ); harmony.Patch( original: AccessTools.Method(typeof(Bush), "shake"), postfix: new HarmonyMethod(typeof(Patcher), nameof(FixBerryQuality)) ); } catch (Exception e) { mod.ErrorLog("Error while trying to setup required patches:", e); } if (mod.Helper.ModRegistry.IsLoaded("Pathoschild.Automate")) { try { mod.DebugLog("This mod patches Automate. If you notice issues with Automate, make sure it happens without this mod before reporting it to the Automate page."); // this is so ugly but I can't include a reference Assembly assembly = null; foreach (var item in AppDomain.CurrentDomain.GetAssemblies()) { if (item.GetName().Name.Trim() == "Automate") { assembly = item; break; } } if (assembly == null) { mod.ErrorLog($"Error while trying to patch Automate. Please report this to the mod page of {mod.ModManifest.Name}, not Automate."); return; } // I don't see a use in using MachineWrapper because it's also internal I need to check for the type of the machine anyway which would be way too much reflection at runtime var mushroomBox = assembly.GetType("Pathoschild.Stardew.Automate.Framework.Machines.Objects.MushroomBoxMachine"); var tapper = assembly.GetType("Pathoschild.Stardew.Automate.Framework.Machines.Objects.TapperMachine"); var berryBush = assembly.GetType("Pathoschild.Stardew.Automate.Framework.Machines.TerrainFeatures.BushMachine"); harmony.Patch( original: AccessTools.Method(tapper, "GetOutput"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchTapperMachineOutput)) ); harmony.Patch( original: AccessTools.Method(mushroomBox, "GetOutput"), prefix: new HarmonyMethod(typeof(Patcher), nameof(PatchMushroomBoxMachineOutput)) ); harmony.Patch( original: AccessTools.Method(berryBush, "GetOutput"), postfix: new HarmonyMethod(typeof(Patcher), nameof(PatchPostBushMachineXP)) ); } catch (Exception e) { mod.ErrorLog($"Error while trying to patch Automate. Please report this to the mod page of {mod.ModManifest.Name}, not Automate:", e); } } if (mod.Helper.ModRegistry.IsLoaded("BitwiseJonMods.OneClickShedReloader")) { try { mod.DebugLog("This mod patches OneClickShedReloader. If you notice issues with OneClickShedReloader, make sure it happens without this mod before reporting it to the OneClickShedReloader page."); // this is so ugly but I can't include a reference Assembly assembly = null; foreach (var item in AppDomain.CurrentDomain.GetAssemblies()) { if (item.GetName().Name.Trim() == "BitwiseJonMods.OneClickShedReloader") { assembly = item; break; } } if (assembly == null) { mod.ErrorLog($"Error while trying to patch OneClickShedReloader. Please report this to the mod page of {mod.ModManifest.Name}, not OneClickShedReloader."); return; } var handler = assembly.GetType("BitwiseJonMods.BuildingContentsHandler"); var entry = assembly.GetType("BitwiseJonMods.ModEntry"); harmony.Patch( original: AccessTools.Method(handler, "TryAddItemToPlayerInventory"), prefix: new HarmonyMethod(typeof(Patcher), nameof(TryAddItemToPlayerInventory_Pre)) ); harmony.Patch( original: AccessTools.Method(handler, "TryAddItemToPlayerInventory"), postfix: new HarmonyMethod(typeof(Patcher), nameof(TryAddItemToPlayerInventory_Post)) ); harmony.Patch( original: AccessTools.Method(entry, "HarvestAllItemsInBuilding"), postfix: new HarmonyMethod(typeof(Patcher), nameof(ReduceQualityAfterHarvest)) ); } catch (Exception e) { mod.ErrorLog($"Error while trying to patch OneClickShedReloader. Please report this to the mod page of {mod.ModManifest.Name}, not OneClickShedReloader:", e); } } }
public static bool AreGrapeJsonModsInstalled(ForageFantasy mod) { return(mod.Helper.ModRegistry.IsLoaded("spacechase0.JsonAssets") && mod.Helper.ModRegistry.IsLoaded("Goldenrevolver.TheGrapeDivide")); }