Example #1
0
        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");
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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;
        }
Example #4
0
 /// <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);
             }
         }
     }
 }
Example #5
0
 /// <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}");
     }
 }
Example #6
0
 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);
 }
Example #7
0
            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);
                }
            }
Example #8
0
        /// <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);
 }
Example #10
0
        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);
        }
Example #11
0
        /// <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");
                }
            }
        }
Example #12
0
        /// <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);
                }
            }
        }
Example #13
0
 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);
            }
        }
Example #15
0
 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);
                }
            }
Example #17
0
        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);
        }
Example #18
0
        /// <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);
            }
        }
Example #19
0
        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);
        }
Example #20
0
        /// <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);
            }
        }
Example #21
0
        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);
        }
Example #22
0
        /// <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);
        }
Example #24
0
        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;
                }
            }
        }
Example #25
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;
                    }
                }
            }
Example #26
0
        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);
        }
Example #27
0
        /// <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);
        }
Example #28
0
        /// <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;
                }
            }
        }