public void CheckConstructorNullName() { // Arrange string nameOfFruit = null; int startingPiecesOfFruit = 1; FruitTree pineappleTree; // Act pineappleTree = new FruitTree(nameOfFruit, startingPiecesOfFruit); // Assert Assert.AreEqual(nameOfFruit, pineappleTree.TypeOfFruit, "I didn't get Pineapple"); }
public void TestHappyPathPickFruit() { // Arrange FruitTree appleTree = new FruitTree("Apple", 25); int numberOfApplesToPick = 10; int numberOfApplesLeft = 15; // Act bool didPickFruit = appleTree.PickFruit(numberOfApplesToPick); // Assert Assert.AreEqual(numberOfApplesLeft, appleTree.PiecesOfFruitLeft); }
/// <summary>Age a fruit tree if it's eligible for aging.</summary> /// <param name="tree">The tree to grow.</param> private void AgeFruitTree(FruitTree tree) { FruitTreeConfig config = this.Config.FruitTrees; // check if ageable if (!config.InstantlyAge || tree.growthStage.Value < FruitTree.treeStage || tree.daysUntilMature.Value <= -ModEntry.FruitTreeIridiumDays) { return; } // age tree tree.daysUntilMature.Value = -ModEntry.FruitTreeIridiumDays; }
/// <summary>Before a save, check every fruit tree to see if it can grow. If not, make it grow.</summary> private void OnSaving(object sender, SavingEventArgs e) { foreach (GameLocation l in Game1.locations) { foreach (KeyValuePair <Vector2, TerrainFeature> fruitTree in l.terrainFeatures.Pairs.Where( item => item.Value is FruitTree)) { if (FruitTree.IsGrowthBlocked(fruitTree.Key, l)) { this.SimulateFruitTreeDayUpdate(l, fruitTree.Value as FruitTree); } } } }
/// <summary>Patch to increase Abrorist fruit tree growth speed.</summary> private static void FruitTreeDayUpdatePostfix(ref FruitTree __instance) { try { if (Utility.AnyPlayerHasProfession("Arborist", out _) && __instance.daysUntilMature.Value % 4 == 0) { --__instance.daysUntilMature.Value; } } catch (Exception ex) { Monitor.Log($"Failed in {nameof(FruitTreeDayUpdatePostfix)}:\n{ex}"); } }
public static void FillBox <T>(this Box <T> box, FruitTree <T> tree) where T : Fruit, new() { do { try { box.Add(tree.CreateFruit()); } catch (Exception) { break; } } while (true); }
public static void Postfix(FruitTree __instance, SpriteBatch spriteBatch, Vector2 tileLocation) { if (!Config.EnableMod || __instance.fruitsOnTree.Value <= 3 || __instance.growthStage.Value < 4) { return; } for (int i = 3; i < __instance.fruitsOnTree.Value; i++) { Vector2 offset = GetFruitOffset(__instance, i); Color color = fruitColors[Game1.currentLocation][tileLocation][i]; spriteBatch.Draw(Game1.objectSpriteSheet, Game1.GlobalToLocal(Game1.viewport, tileLocation * 64 - new Vector2(16, 80) * 4 + offset), new Rectangle?(Game1.getSourceRectForStandardTileSheet(Game1.objectSpriteSheet, (__instance.struckByLightningCountdown.Value > 0) ? 382 : __instance.indexOfFruit.Value, 16, 16)), color, 0f, Vector2.Zero, GetFruitScale(__instance, i), SpriteEffects.None, (float)__instance.getBoundingBox(tileLocation).Bottom / 10000f + 0.002f - tileLocation.X / 1000000f + i / 100000f); } }
/// <summary>Get the data to display for this subject.</summary> /// <param name="metadata">Provides metadata that's not available from the game data directly.</param> /// <remarks>Tree growth algorithm reverse engineered from <see cref="FruitTree.dayUpdate"/>.</remarks> public override IEnumerable <ICustomField> GetData(Metadata metadata) { FruitTree tree = this.Target; // get basic info bool isMature = tree.daysUntilMature <= 0; bool isDead = tree.stump; bool isStruckByLightning = tree.struckByLightningCountdown > 0; // show growth countdown if (!isMature) { System.Tuple <string, int> dayOfMaturity = GameHelper.GetDayOffset(tree.daysUntilMature, metadata.Constants.DaysInSeason); string growthText = $"mature on {dayOfMaturity.Item1} {dayOfMaturity.Item2} ({GameHelper.Pluralise(tree.daysUntilMature, "tomorrow", $"in {tree.daysUntilMature} days")})"; yield return(new GenericField("Next fruit", "too young to bear fruit")); yield return(new GenericField("Growth", growthText)); if (this.HasAdjacentObjects(this.Tile)) { yield return(new GenericField("Complaints", "can't grow because there are adjacent objects")); } } // show next fruit if (isMature && !isDead) { if (isStruckByLightning) { yield return(new GenericField("Next fruit", $"struck by lightning! Will recover in {tree.struckByLightningCountdown} days.")); } else if (Game1.currentSeason != tree.fruitSeason && !tree.greenHouseTree) { yield return(new GenericField("Next fruit", "out of season")); } else if (tree.fruitsOnTree == FruitTree.maxFruitsOnTrees) { yield return(new GenericField("Next fruit", "won't grow any more fruit until you harvest those it has")); } else { yield return(new GenericField("Next fruit", "tomorrow")); } } // show seasons yield return(new GenericField("Season", $"{tree.fruitSeason} (or any season in greenhouse)")); }
private static Color ReplaceColorIfNeeded(Color prevcolor, FruitTree tree) { try { if (tree.modData?.GetInt(CanPlaceHandler.FruitTreeFertilizer) is int result) { return(result > 1 ? Color.Red : Color.Orange); } } catch (Exception ex) { ModEntry.ModMonitor.LogOnce($"Crash while drawing fruit trees!\n\n{ex}", LogLevel.Error); } return(prevcolor); }
public void TestSillyArray() { // Arrange FruitTree pear = new FruitTree("Pear", 42); int[] expected = new int[2] { 8, 42 }; // Act int[] output = pear.TreeData(8); // Assert CollectionAssert.AreEqual(expected, output); }
/// <summary> /// Applies growth speedup (200%) or growth slowdown (50%) to the a fruit tree depending on the settings. /// </summary> /// <param name="fruittree">current fruit tree</param> /// <param name="location">ingame location (e.g. farm)</param> /// <param name="tileLocation">position in said location</param> public void CheckForExtraGrowth(FruitTree fruittree, GameLocation location, Vector2 tileLocation) { if (treeOverhaulConfig.FruitTreeGrowth == 1) { GrowFruitTree(fruittree, location, tileLocation, "minus"); } if (treeOverhaulConfig.FruitTreeGrowth == 2) { if (Game1.dayOfMonth % 2 == 1) //odd day { GrowFruitTree(fruittree, location, tileLocation, "plus"); } } }
/// <summary> /// Applies growth speedup (200%) or growth slowdown (50%) to a fruit tree depending on the settings. /// </summary> /// <param name="fruittree">current fruit tree</param> /// <param name="location">ingame location (e.g. farm)</param> /// <param name="tileLocation">position in said location</param> private void CheckForExtraGrowth(FruitTree fruittree, GameLocation location, Vector2 tileLocation) { if (config.FruitTreeGrowth == 1) { GrowFruitTree(fruittree, location, tileLocation, true); } if (config.FruitTreeGrowth == 2) { // odd day if (Game1.dayOfMonth % 2 == 1) { GrowFruitTree(fruittree, location, tileLocation, false); } } }
private static bool CanPlantFruitTreeOnTile(GameLocation farm, Vector2 pos) { if (FruitTree.IsGrowthBlocked(pos, farm)) { return(false); } if (Util.IsOccupied(farm, pos)) { return(false); } if (Util.IsHoed(farm, pos)) { return(false); } return(true); }
public override void Resolve(GameLocation location, BaseFeatureSaveData featureSaveData) { FruitTreeSaveData fruitTreeSaveData = featureSaveData as FruitTreeSaveData; FruitTree fruitTree = location.terrainFeatures[featureSaveData.featurePosition] as FruitTree; fruitTree.struckByLightningCountdown = 0; fruitTree.fruitsOnTree = fruitTreeSaveData.fruitsOnTree; monitor.Log($"Turned {fruitTree.GetType()} at position {featureSaveData.featurePosition} back to fruit.", LogLevel.Trace); //Handle the dropped coal if (sideEffectHandlers[0].CanHandle(fruitTreeSaveData)) { sideEffectHandlers[0].Handle(fruitTreeSaveData, location); } }
private static int CalculateExtraGrowth(FruitTree tree) { try { if (tree.modData?.GetInt(CanPlaceHandler.FruitTreeFertilizer) is int result && Game1.random.NextDouble() <= 0.1 * result) { return(1); } } catch (Exception ex) { ModEntry.ModMonitor.LogOnce($"Crash while calculating extra growth for fruit trees!\n\n{ex}", LogLevel.Error); } return(0); }
public static void Prefix(FruitTree __instance, Tool t, int explosion, Vector2 tileLocation) { if (t == null || !(t is Axe) || explosion > 0) { return; } float num; switch (t.UpgradeLevel) { case 0: num = 1f; break; case 1: num = 1.25f; break; case 2: num = 1.67f; break; case 3: num = 2.5f; break; case 4: num = 5f; break; default: num = 5f; break; } if (__instance.growthStage.Value >= 3) { num *= 2; } if (ModEntry.ModConfig.AxeDefine.TryGetValue(t.UpgradeLevel, out ModConfig.PowerDefine define)) { if (define.Power < 0) { define.Power = 1f; } __instance.health.Value -= num * (define.Power - 1); } }
private Object GetFruit(FruitTree fruitTree) { var quality = 0; if (fruitTree is null) { return(null); } if (!_config.DoHarvestFruitTrees) { return(null); } if (fruitTree.growthStage.Value < 4) { return(null); } if (fruitTree.fruitsOnTree.Value <= 0) { return(null); } if (fruitTree.daysUntilMature.Value <= -112) { quality = 1; } if (fruitTree.daysUntilMature.Value <= -224) { quality = 2; } if (fruitTree.daysUntilMature.Value <= -336) { quality = 4; } if (fruitTree.struckByLightningCountdown.Value > 0) { quality = 0; } var fruit = new Object(fruitTree.indexOfFruit.Value, fruitTree.fruitsOnTree.Value, false, -1, quality); fruitTree.fruitsOnTree.Value = 0; return(fruit); }
/// <summary> /// Checks if a FruitTree is localized on the right side of the greenhouse based on the new upgrade level. /// </summary> /// <param name="newLevel">The new upgrade level.</param> /// <param name="fruitTree">The fruit tree to check.</param> /// <returns></returns> private static bool RightSideTree(int newLevel, FruitTree fruitTree) { float x = fruitTree.currentTileLocation.X; float y = fruitTree.currentTileLocation.Y; switch (newLevel) { case 1: return((x >= 17 && x <= 18) && (y >= 9 && y <= 22)); case 2: return((x >= 20 && x <= 21) && (y >= 9 && y <= 27)); default: return(false); } }
public static object getReplacement(object element) { object replacement = element; if (element is ISaveElement ise) { replacement = getReplacement(ise); } if (element is TerrainFeature && !(replacement is FruitTree)) { replacement = new FruitTree(628, 1); } setDataString(replacement, getReplacementName(element)); return(replacement); }
/// <summary>Get raw debug data to display for this subject.</summary> public override IEnumerable <IDebugField> GetDebugFields() { FruitTree target = this.Target; // pinned fields yield return(new GenericDebugField("mature in", $"{target.daysUntilMature} days", pinned: true)); yield return(new GenericDebugField("growth stage", target.growthStage.Value, pinned: true)); yield return(new GenericDebugField("health", target.health.Value, pinned: true)); // raw fields foreach (IDebugField field in this.GetDebugFieldsFrom(target)) { yield return(field); } }
private static object getReplacement(object element) { if (element is ISaveElement ise) { return(getReplacement(ise)); } object o = Helper.Reflection.GetMethod(element, "getReplacement").Invoke <object>(); if (element is TerrainFeature && !(o is FruitTree)) { o = new FruitTree(628, 1); } setDataString(o, getReplacementName(element)); return(o); }
/// <summary> /// Checks if a FruitTree is localized on the bottom side of the greenhouse based on the new upgrade level. /// </summary> /// <param name="newLevel">The new upgrade level.</param> /// <param name="fruitTree">The fruit tree to check.</param> /// <returns></returns> private static bool BottomSideTree(int newLevel, FruitTree fruitTree) { float x = fruitTree.currentTileLocation.X; float y = fruitTree.currentTileLocation.Y; switch (newLevel) { case 1: return((x >= 3 && x <= 16) && (y >= 21 && y <= 22)); case 2: return((x >= 3 && x <= 19) && (y >= 26 && y <= 27)); default: return(false); } }
private static bool HarvestFromTree(Vector2 pos, JunimoHarvester junimo, FruitTree tree) { //shake the tree without it releasing any fruit var fruitsOnTree = tree.fruitsOnTree.Value; tree.fruitsOnTree.Value = 0; tree.performUseAction(pos, junimo.currentLocation); tree.fruitsOnTree.Value = fruitsOnTree; var result = GetFruitFromTree(tree); if (result == null) { return(false); } junimo.tryToAddItemToHut(result); return(true); }
private void FruitTrees() { var nearbyTiles = (Grabber.RangeEntireMap ? Utilities.GetLocationObjectTiles(Grabber.Location) : Grabber.NearbyTilesRange) .Where(t => Grabber.Location.terrainFeatures.ContainsKey(t) && Grabber.Location.terrainFeatures[t] is FruitTree) .GroupBy(t => t).Select(g => g.First()) .ToDictionary(t => t, t => Grabber.Location.terrainFeatures[t] as FruitTree); foreach (var pair in nearbyTiles) { if (Grabber.IsChestFull) { break; } FruitTree tree = pair.Value; if (tree.growthStage.Value >= FruitTree.treeStage && tree.fruitsOnTree.Value > 0 && !tree.stump.Value) { //Utilities.Monitor.Log($" {Grabber.InstanceName} harvesting fruit tree: {(new SVObject(tree.indexOfFruit.Value, 1)).Name} {tree.fruitsOnTree.Value} {pair.Key.X},{pair.Key.Y}", StardewModdingAPI.LogLevel.Trace); SVObject item; if (tree.struckByLightningCountdown.Value > 0) { item = new SVObject(382, tree.fruitsOnTree.Value); } else { int quality = SVObject.lowQuality; if (tree.daysUntilMature.Value <= -112) { quality = SVObject.medQuality; } if (tree.daysUntilMature.Value <= -224) { quality = SVObject.highQuality; } if (tree.daysUntilMature.Value <= -336) { quality = SVObject.bestQuality; } item = new SVObject(tree.indexOfFruit.Value, tree.fruitsOnTree.Value, quality: quality); } Grabber.GrabberChest.addItem(item); tree.fruitsOnTree.Value = 0; } } }
public static void Postfix(FruitTree __instance, SpriteBatch spriteBatch, Vector2 tileLocation, NetBool ___falling) { if (!Config.EnableMod || currentConversations.Count == 0) { return; } for (int i = currentConversations.Count - 1; i >= 0; i--) { if (currentConversations[i].Participant == __instance) { Vector2 local = Game1.GlobalToLocal(tileLocation * 64 + new Vector2(32, ___falling.Value || __instance.stump.Value ? -128 : -192)); SpriteText.drawStringWithScrollCenteredAt(spriteBatch, currentConversations[i].Dialogue.data.text, (int)local.X, (int)local.Y, "", currentConversations[i].Dialogue.textAboveHeadAlpha, currentConversations[i].Dialogue.data.color, 1, 1, false); return; } } }
private Object GetFruit(FruitTree fruitTree, Vector2 tile, GameLocation location) { Object fruit = null; int quality = 0; if (fruitTree is null) { return(null); } if (!Config.DoHarvestFruitTrees) { return(null); } if (fruitTree.growthStage.Value >= 4) { if (fruitTree.fruitsOnTree.Value > 0) { if (fruitTree.daysUntilMature.Value <= -112) { quality = 1; } if (fruitTree.daysUntilMature.Value <= -224) { quality = 2; } if (fruitTree.daysUntilMature.Value <= -336) { quality = 4; } if (fruitTree.struckByLightningCountdown.Value > 0) { quality = 0; } fruit = new Object(fruitTree.indexOfFruit.Value, fruitTree.fruitsOnTree.Value, false, -1, quality); fruitTree.fruitsOnTree.Value = 0; return(fruit); } } return(null); }
/// <summary>Get whether a given tree should be chopped.</summary> /// <param name="tree">The tree to check.</param> private bool ShouldCut(FruitTree tree) { var config = this.Config; // seed if (tree.growthStage.Value == Tree.seedStage) { return(config.ClearFruitTreeSeeds); } // sapling if (tree.growthStage.Value < Tree.treeStage) { return(config.ClearFruitTreeSaplings); } // full-grown return(config.CutGrownFruitTrees); }
/// <summary>Grow a fruit tree if it's eligible for growth.</summary> /// <param name="tree">The tree to grow.</param> /// <param name="location">The tree's location.</param> /// <param name="tile">The tree's tile position.</param> private void GrowFruitTree(FruitTree tree, GameLocation location, Vector2 tile) { FruitTreeConfig config = this.Config.FruitTrees; // check if growable bool isGrown = tree.growthStage.Value >= FruitTree.treeStage; bool isMature = tree.daysUntilMature.Value <= 0; if ((isGrown && isMature) || !this.CanGrowNow(location, config.InstantlyGrowInWinter)) { return; } // ignore if tree blocked if (!config.InstantlyGrowWhenInvalid) { foreach (Vector2 adjacentTile in Utility.getSurroundingTileLocationsArray(tile)) { bool occupied = location.isTileOccupied(adjacentTile) && ( !location.terrainFeatures.ContainsKey(tile) || !(location.terrainFeatures[tile] is HoeDirt) || ((HoeDirt)location.terrainFeatures[tile]).crop == null ); if (occupied) { return; } } } // grow tree if (!isGrown) { tree.growthStage.Value = FruitTree.treeStage; } if (!isMature) { tree.daysUntilMature.Value = 0; } }
private bool Plant(Farm farm, Vector2 pos, int index) { if (farm.terrainFeatures.Keys.Contains(pos)) { Monitor.Log($"Plant: {pos.X} {pos.Y} occupied by {farm.terrainFeatures[pos]}", LogLevel.Error); return(false); } FruitTree tree = new FruitTree(index, 0); farm.terrainFeatures.Add(pos, tree); if (Utility.isOnScreen(Utility.Vector2ToPoint(pos), 64, farm)) { farm.playSound("stoneStep"); farm.playSound("dirtyHit"); } return(true); }
/*** * Simulates a day of growth on a fruit tree. ***/ private void SimulateFruitTreeDayUpdate(GameLocation l, FruitTree tree) { if (tree.daysUntilMature > 28) { tree.daysUntilMature = 28; } tree.daysUntilMature--; int oldGrowthStage = tree.growthStage; tree.growthStage = tree.daysUntilMature > 0 ? (tree.daysUntilMature > 7 ? (tree.daysUntilMature > 14 ? (tree.daysUntilMature > 21 ? 0 : 1) : 2) : 3) : 4; //We only want to add a fruit to the tree if our simulated growth caused the tree to fully mature. If it is already mature, the game would have already added a fruit. if (oldGrowthStage != 4 && !tree.stump && tree.growthStage == 4 && (tree.struckByLightningCountdown > 0 && !Game1.IsWinter || (Game1.currentSeason.Equals(tree.fruitSeason) || l.name.ToLower().Contains("greenhouse")))) { tree.fruitsOnTree = Math.Min(3, tree.fruitsOnTree + 1); if (l.name.ToLower().Contains("greenhouse")) { tree.greenHouseTree = true; } } }