Stardew Valley

File information

Last updated

Original upload

Created by

NCarigon

Uploaded by

ncarigon

Virus scan

Safe to use

Tags for this mod

About this mod

Allows creating custom tree shaking schedules to support whatever seasons, days, locations, and items you may want.

Requirements
Permissions and credits
Changelogs
Do you feel like the existing tree shaking mechanism is a bit lack-luster? Or maybe you don't like shaking every tree because it *might* have a seed. Or maybe you just want more variety.

This mod adds the ability to make those changes, if you want, via content packs.

This is a sibling to my Bush Bloom Mod, and thanks to user dkim0001 for the idea.

General features:
  • Create your own tree shaking schedules;
  • Shake off whatever item you choose;
  • Supports multiple schedules active at the same time;
  • Supports individual tree types for each schedule;
  • Supports custom seeding chance;
  • Supports only seeding in specific locations, by inclusion or exclusion;
  • Schedules can be as short as a single day, or as long as all year;
  • Can show the seed item as an overlay on the tree;
  • Only supports Oak, Pine, Maple, and Mahogany trees, ignoring Palm and Mushroom trees;
  • Allows other mods to check for custom seeding schedules;
  • Config options to toggle some features depending on your other mods.
  • Built-in support for Deluxe Grabber Redux
  • Built-in support for Almanac
  • Built-in support for Automate

Configuration options:
You can edit these configuration files in-game using the Generic Mod Config Menu interface, or directly in the config.json file.

Enable Default Shaking: (true|false) Enable the game's default tree shaking schedules. Disabling them does not modify any other game logic, bundles, quests, or storytelling in the game. Do so at your own risk!

Draw Item When Seeded: (true|false) Draw the seed item when a tree has a seed.
                   
Support Deluxe Grabber Redux: (true|false) When using that mod, allows the grabber to harvest custom items from trees.

Support Almanac: (true|false) When using that mod, allows the almanac to show custom shaking schedules. When enabled, it's highly recommended to disable that mod's option 'Page: Local Notices' > 'Show Gathering' in order to not show duplicates of the default shaking schedules.

No normal seeds in Almanac: (true|false) When using that mod, prevents the almanac from showing the all-year normal seed schedules. Has no effect if 'Support Almanac' is disabled.

Support Automate: (true|false) When using that mod, allows chests to harvest custom items from trees.


Content pack details:
This mod includes an example directory that contains a manifest.json and content.json. These can serve as a template for creating additional content packs. Below are some examples of what the content.json can contain.

Example full schedule config:

[{
// Optional; Default is true if missing
"Enabled": true,

// Required; Value can be the tree type ID or Name; Values must be [1] Oak, [2] Maple, [3] Pine, [8] Mahogany
"TreeType": 1,

// Required; Value can be the item ID or Name, but must exist
"ShakeOff": 0,

// Optional; Value must be 'spring', 'summer', 'fall', or 'winter'; Default is 'spring' if missing
"StartSeason": "spring",

// Optional; Value must be from 1 to 28; Default is 1
"StartDay": 1,

// Optional; Default is StartSeason value if missing, or 'winter' if that is also missing
"EndSeason": "winter",

// Optional; Default is StartDay value if missing, or 28 if that is also missing
"EndDay": 28,

// Optional; Default is 0.05 if missing; Value must be between 0.0 and 1.0, inclusive
"Chance": 0.05,

// Optional; Default is all locations if missing or empty
"Locations": [ "Forest", "Mountain" ],

// Optional: Default is no locations if missing or empty
"ExcludeLocations": [ "Town" ]
}]


For easier configs, I have set up the required and default parameters in a way where a year-long shake schedule can be created with only the TreeType and ShakeOff, such as:

[{
"TreeType": 1,
"ShakeOff": 309
}]

And a month-long shake schedule can be created by only adding StartSeason:
[{
"TreeType": 1,
"ShakeOff": 309,
"StartSeason": "spring"
}]

More complicated shake schedules will require using the additional config parameters.

Example minimal default schedules:

[{
"TreeType": 1,
"ShakeOff": 309
},{
"TreeType": 3,
"ShakeOff": 311
},{
"TreeType": 8,
"ShakeOff": 292
},{
"TreeType": 2,
"ShakeOff": 310,
"EndSeason": "fall",
"EndDay": 13
},{
"TreeType": 2,
"ShakeOff": 408,
"StartSeason": "fall",
"StartDay": 14,
"EndDay": 28
},{
"TreeType": 2,
"ShakeOff": 310,
"StartSeason": "winter"
}]


Integration details:
If you are making a different mod and want to integrate with this mods features, familiarize yourself with this: Modding:Modder Guide/APIs/Integrations

To incorporate this mod's logic into your own mod, create this interface in your project, including the specific method(s) you will actually need:

public interface ITreeShakeModApi {
/// <summary>
/// Returns an array of (item_id, first_day, last_day, isDefault) for all possible active tree
/// schedules on the given season and day, optionally within the given location.
/// </summary>
public (int, WorldDate, WorldDate, bool)[] GetActiveSchedules(string season, int dayofMonth, GameLocation? location = null);

/// <summary>
/// Returns the chosen shake off item id, or -1 if none, for the given tree.
/// </summary>
public int GetShakeOffId(Tree tree);
}


Then call for the API in your Entry method:

var tsm = helper.ModRegistry.GetApi<ITreeShakeModApi>("NCarigon.TreeShakeMod");

You will need to update your tree checking logic to include calling the API.

For example, if your original method is:

private static IEnumerable<Item> Shake(Tree tree, Vector2 tileLocation, GameLocation location) {
List<Item> items = new();
if (tree.hasSeed.Value) {
int seedIndex = -1;
switch (tree.treeType.Value) {
case 3:
seedIndex = 311;
break;
case 1:
seedIndex = 309;
break;
case 8:
seedIndex = 292;
break;
case 2:
seedIndex = 310;
break;
case 6:
case 9:
seedIndex = 88;
break;
}
if (Game1.GetSeasonForLocation(tree.currentLocation).Equals("fall") && tree.treeType.Value == 2 && Game1.dayOfMonth >= 14) {
seedIndex = 408;
}
if (seedIndex != -1) {
items.Add(new SObject(seedIndex, 1));
}
if (seedIndex == 88 && new Random((int)Game1.uniqueIDForThisGame + (int)Game1.stats.DaysPlayed + (int)tileLocation.X * 13 + (int)tileLocation.Y * 54).NextDouble() < 0.1 && location != null && location is IslandLocation) {
items.Add(new SObject(791, 1));
}
if (Game1.random.NextDouble() <= 0.5 && Game1.player.team.SpecialOrderRuleActive("DROP_QI_BEANS")) {
items.Add(new SObject(890, 1));
}
tree.hasSeed.Value = false;
}
return items;
}


Updated it to include the API call, with a fallback for when the mod is not installed:

private static IEnumerable<Item> Shake(Tree tree, Vector2 tileLocation, GameLocation location) {
            List<Item> items = new();
            if (tree.hasSeed.Value) {
                int seedIndex = tsm?.GetShakeOffId(tree) ?? -1;
                if (seedIndex == -1) {
                    switch (tree.treeType.Value) {
                        case 3:
                            seedIndex = 311;
                            break;
                        case 1:
                            seedIndex = 309;
                            break;
                        case 8:
                            seedIndex = 292;
                            break;
                        case 2:
                            seedIndex = 310;
                            break;
                        case 6:
                        case 9:
                            seedIndex = 88;
                            break;
                    }
                    if (Game1.GetSeasonForLocation(tree.currentLocation).Equals("fall") && tree.treeType.Value == 2 && Game1.dayOfMonth >= 14) {
                        seedIndex = 408;
                    }
                }
                if (seedIndex != -1) {
                    items.Add(new SObject(seedIndex, 1));
                }
                if (seedIndex == 88 && new Random((int)Game1.uniqueIDForThisGame + (int)Game1.stats.DaysPlayed + (int)tileLocation.X * 13 + (int)tileLocation.Y * 54).NextDouble() < 0.1 && location != null && location is IslandLocation) {
                    items.Add(new SObject(791, 1));
                }
                if (Game1.random.NextDouble() <= 0.5 && Game1.player.team.SpecialOrderRuleActive("DROP_QI_BEANS")) {
                    items.Add(new SObject(890, 1));
                }
                tree.hasSeed.Value = false;
            }
            return items;
        }


Source: ncarigon/StardewValleyMods (github.com)

My mods:
Copper Still
Garden Pot - Automate
Passable Crops
Bush Bloom Mod
Ore Berries