Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of GrindstoneSkills v0.9.1
plugins\GrindstoneSkills.dll
Decompiled 15 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Charter; using ConfigReload; using HarmonyLib; using Microsoft.CodeAnalysis; using PatchGuard; using PlateColumn; using SyncedConfig; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Rendering; using UnityEngine.UI; using YamlConfig; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("GrindstoneSkills")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("GrindstoneSkills: deeper skills for Valheim, cooking and sailing")] [assembly: AssemblyFileVersion("0.9.1.0")] [assembly: AssemblyInformationalVersion("0.9.1+9ee5b8330d02da922fcf5a84e07fa482d9304da0")] [assembly: AssemblyProduct("GrindstoneSkills")] [assembly: AssemblyTitle("GrindstoneSkills")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.9.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace GrindstoneSkills { public static class CleanSplitSettings { public const string Section = "12 - Chopping"; public static ConfigEntry<float> ChanceAt100 { get; private set; } public static ConfigEntry<float> WoodBonus { get; private set; } public static void Initialize(SyncedConfiguration config) { ChanceAt100 = config.Bind("12 - Chopping", "Clean Split Chance At 100", 20f, "Percent of axe hits on a log that split it at once, for a level 100 woodcutter. The axe must still be good enough for the wood. 0 turns clean splits off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); WoodBonus = config.Bind("12 - Chopping", "Clean Split Bonus", 50f, "Percent more wood from a log that was split cleanly. A whole log split cleanly passes this on to both of its halves.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); } } public static class CompostSettings { public const string Section = "32 - Compost"; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<float> CompostTime { get; private set; } public static ConfigEntry<int> Capacity { get; private set; } public static ConfigEntry<float> Radius { get; private set; } public static ConfigEntry<float> GrowthSpeed { get; private set; } public static ConfigEntry<float> StarLevels { get; private set; } public static ConfigEntry<bool> KitchenTrash { get; private set; } public static ConfigEntry<string> ExtraItems { get; private set; } public static void Initialize(SyncedConfiguration config) { BindBin(config); BindEffect(config); } private static void BindBin(SyncedConfiguration config) { Enabled = config.Bind("32 - Compost", "Compost Enabled", true, "Compost bins turn their contents into compost and feed the crops around them. Off, a bin is a plain barrel.", true, null); CompostTime = config.Bind("32 - Compost", "Compost Time", 30f, "Seconds a bin takes to turn one item into one point of compost.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 3600f)); Capacity = config.Bind("32 - Compost", "Compost Capacity", 100, "The most compost points a bin holds; it stops composting when full.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 10000)); ExtraItems = config.Bind("32 - Compost", "Compost Items", "Entrails, BoneFragments", "Item prefab names that compost besides food and anything that carries stars, separated by commas.", true, null); KitchenTrash = config.Bind("32 - Compost", "Kitchen Trash Compost", true, "A dish a kitchen's trash filter throws away within 20 m of a compost bin becomes a point of compost in the nearest one.", true, null); } private static void BindEffect(SyncedConfiguration config) { Radius = config.Bind("32 - Compost", "Compost Radius", 12f, "A bin fertilizes growing crops within this many metres, one point of compost each.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 50f)); GrowthSpeed = config.Bind("32 - Compost", "Compost Growth Speed", 25f, "Percent faster growth for a fertilized crop.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); StarLevels = config.Bind("32 - Compost", "Compost Star Levels", 10f, "Levels added to a fertilized crop's star roll.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); } } public static class CookLevel { public const SkillType Skill = (SkillType)105; public const float MaxLevel = 100f; public static float Local() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { return ((Character)localPlayer).GetSkillLevel((SkillType)105); } return 0f; } public static float Factor(float level) { return Mathf.Clamp01(level / 100f); } public static float Effective(float cookLevel, float averageIngredientStars) { float num = Mathf.Max(0f, OddsSettings.IngredientLevelsPerStar.Value); return Mathf.Max(0f, cookLevel) + Mathf.Max(0f, averageIngredientStars) * num; } } public static class Crops { [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class SceneAwake { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(ZNetScene __instance) { foreach (GameObject prefab in __instance.m_prefabs) { Add(((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Plant>() : null); } } } private static readonly HashSet<string> names = new HashSet<string>(); public static bool IsCrop(Pickable pickable) { if ((Object)(object)pickable != (Object)null) { return names.Contains(Utils.GetPrefabName(((Component)pickable).gameObject)); } return false; } public static bool IsCropPrefab(string prefabName) { if (prefabName != null) { return names.Contains(prefabName); } return false; } private static void Add(Plant plant) { if (plant?.m_grownPrefabs == null) { return; } GameObject[] grownPrefabs = plant.m_grownPrefabs; foreach (GameObject val in grownPrefabs) { if ((Object)(object)val != (Object)null && (Object)(object)val.GetComponent<Pickable>() != (Object)null) { names.Add(((Object)val).name); } } } } public static class DeathSettings { public const string Section = "6 - Death"; public static ConfigEntry<float> SkillLoss { get; private set; } public static ConfigEntry<bool> LoseProgress { get; private set; } public static void Initialize(SyncedConfiguration config) { SkillLoss = config.Bind("6 - Death", "Skill Loss On Death", 5f, "Percent of every skill's level lost when you die. The game's own value is 5; 0 keeps every level. The world's death penalty modifier still scales it, and a world that resets skills on death still does.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); LoseProgress = config.Bind("6 - Death", "Lose Progress On Death", true, "Whether dying also loses the experience gathered toward each skill's next level, as in the game. Off keeps it.", true, null); } } public static class DefenseExperienceSettings { public const string Section = "20 - Defense Experience"; public static ConfigEntry<float> Multiplier { get; private set; } public static ConfigEntry<float> BlockExperience { get; private set; } public static ConfigEntry<float> ParryMultiplier { get; private set; } public static ConfigEntry<float> WeaponBlockShare { get; private set; } public static ConfigEntry<float> HitExperience { get; private set; } public static ConfigEntry<float> HitSizeDamage { get; private set; } public static ConfigEntry<float> MinHitSize { get; private set; } public static ConfigEntry<float> MaxHitSize { get; private set; } public static ConfigEntry<float> Cooldown { get; private set; } public static ConfigEntry<float> FirstBlockBonus { get; private set; } public static ConfigEntry<bool> PlayerHitsTrain { get; private set; } public static void Initialize(SyncedConfiguration config) { Multiplier = config.Bind("20 - Defense Experience", "Experience Multiplier", 1f, "Multiplies all Defense experience.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); BlockExperience = config.Bind("20 - Defense Experience", "Block Experience", 1f, "Experience for blocking a size 1 hit with a shield.", true, (AcceptableValueBase?)(object)Settings.UpTo(20f)); ParryMultiplier = config.Bind("20 - Defense Experience", "Parry Multiplier", 2f, "A parry earns this many times a block's experience.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); WeaponBlockShare = config.Bind("20 - Defense Experience", "Weapon Block Share", 50f, "Percent of a shield's experience for blocking or parrying with a weapon (no shield equipped).", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); HitExperience = config.Bind("20 - Defense Experience", "Hit Taken Experience", 0.5f, "Experience for a size 1 hit that hurts you without being blocked.", true, (AcceptableValueBase?)(object)Settings.UpTo(20f)); BindSizes(config); } private static void BindSizes(SyncedConfiguration config) { HitSizeDamage = config.Bind("20 - Defense Experience", "Hit Size Damage", 10f, "Damage of a size 1 hit, as it arrives, before armour and blocking. A hit of 4 times this damage is size 2.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 1000f)); MinHitSize = config.Bind("20 - Defense Experience", "Min Hit Size", 0.5f, "The smallest size a hit counts as.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); MaxHitSize = config.Bind("20 - Defense Experience", "Max Hit Size", 4f, "The largest size a hit counts as.", true, (AcceptableValueBase?)(object)Settings.UpTo(20f)); Cooldown = config.Bind("20 - Defense Experience", "Experience Cooldown", 0.5f, "Seconds after a hit earned experience before the next one can, so a swarm of small creatures does not outpace one big one.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); FirstBlockBonus = config.Bind("20 - Defense Experience", "First Block Bonus", 3f, "Experience multiplier for the first block against each kind of creature. 1 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 20f)); PlayerHitsTrain = config.Bind("20 - Defense Experience", "Player Hits Train", false, "When on, hits from other players (PvP) train Defense like hits from creatures.", true, null); } } public static class DefenseGuardSettings { public const string Section = "22 - Defense Guard"; public static ConfigEntry<float> ReflexChance { get; private set; } public static ConfigEntry<float> BashChance { get; private set; } public static ConfigEntry<float> Thorns { get; private set; } public static ConfigEntry<float> Adrenaline { get; private set; } public static ConfigEntry<float> ShieldWear { get; private set; } public static ConfigEntry<float> Knockback { get; private set; } public static ConfigEntry<float> DesperationHealth { get; private set; } public static ConfigEntry<float> DesperationMultiplier { get; private set; } public static void Initialize(SyncedConfiguration config) { ReflexChance = config.Bind("22 - Defense Guard", "Reflex Chance At 100", 10f, "Percent chance at level 100 that a hit from the front is blocked by your shield although you were not blocking. It costs stamina like a block and never parries.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); BashChance = config.Bind("22 - Defense Guard", "Shield Bash Chance At 100", 15f, "Percent chance at level 100 that an ordinary block with a shield staggers the attacker, as a parry does.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Thorns = config.Bind("22 - Defense Guard", "Thorns At 100", 10f, "Percent of the damage your block stopped that goes back to a melee attacker, at level 100.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); Adrenaline = config.Bind("22 - Defense Guard", "Adrenaline Bonus At 100", 25f, "Percent more adrenaline from blocks and parries at level 100, and that much less adrenaline lost to hits you did not block.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); BindWear(config); } private static void BindWear(SyncedConfiguration config) { ShieldWear = config.Bind("22 - Defense Guard", "Shield Wear Reduction At 100", 50f, "Percent less durability lost by the shield or weapon you block with, at level 100. 100 means no wear at all.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Knockback = config.Bind("22 - Defense Guard", "Knockback Reduction At 100", 50f, "Percent less knockback from hits you block, at level 100.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); DesperationHealth = config.Bind("22 - Defense Guard", "Desperation Health", 25f, "Below this percent of your max health, Damage Reduction is multiplied by Desperation Multiplier. 0 turns Desperation off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); DesperationMultiplier = config.Bind("22 - Defense Guard", "Desperation Multiplier", 2f, "How much stronger Damage Reduction is while you are desperate.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 5f)); } } public static class DefenseMilestoneSettings { public const string Section = "21 - Defense Milestones"; public static ConfigEntry<float> RiposteLevel { get; private set; } public static ConfigEntry<float> RiposteWindow { get; private set; } public static ConfigEntry<float> RiposteDamage { get; private set; } public static ConfigEntry<float> ShieldWallLevel { get; private set; } public static ConfigEntry<float> ShieldWallRadius { get; private set; } public static ConfigEntry<float> ShieldWallReduction { get; private set; } public static ConfigEntry<float> HardenedLevel { get; private set; } public static ConfigEntry<float> HardenedPerStack { get; private set; } public static ConfigEntry<int> HardenedMaxStacks { get; private set; } public static ConfigEntry<float> HardenedDuration { get; private set; } public static ConfigEntry<float> LastStandLevel { get; private set; } public static ConfigEntry<float> LastStandCooldown { get; private set; } public static ConfigEntry<float> LastStandInvulnerability { get; private set; } private static AcceptableValueRange<float> Levels => new AcceptableValueRange<float>(0f, 101f); public static void Initialize(SyncedConfiguration config) { BindRiposte(config); BindShieldWall(config); BindHardened(config); BindLastStand(config); } private static void BindRiposte(SyncedConfiguration config) { RiposteLevel = config.Bind("21 - Defense Milestones", "Riposte Level", 25f, "Defense level that unlocks Riposte: a parry powers up your next melee attack.", true, (AcceptableValueBase?)(object)Levels); RiposteWindow = config.Bind("21 - Defense Milestones", "Riposte Window", 2f, "Seconds after a parry in which an attack you start is a riposte.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 10f)); RiposteDamage = config.Bind("21 - Defense Milestones", "Riposte Damage", 25f, "Percent more damage for a riposte's melee hits, which also stagger the creatures they hit (not bosses, nor creatures the game never staggers).", true, (AcceptableValueBase?)(object)Settings.UpTo(300f)); } private static void BindShieldWall(SyncedConfiguration config) { ShieldWallLevel = config.Bind("21 - Defense Milestones", "Shield Wall Level", 50f, "Defense level that unlocks Shield Wall: while you block with a shield, players close behind you take less damage.", true, (AcceptableValueBase?)(object)Levels); ShieldWallRadius = config.Bind("21 - Defense Milestones", "Shield Wall Radius", 4f, "How far behind the blocker, in metres, a player is sheltered.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 15f)); ShieldWallReduction = config.Bind("21 - Defense Milestones", "Shield Wall Reduction", 10f, "Percent less damage for a sheltered player. Several blockers do not add up.", true, (AcceptableValueBase?)(object)Settings.UpTo(90f)); } private static void BindHardened(SyncedConfiguration config) { HardenedLevel = config.Bind("21 - Defense Milestones", "Hardened Level", 75f, "Defense level that unlocks Hardened: each hit that hurts you makes you take less damage for a while.", true, (AcceptableValueBase?)(object)Levels); HardenedPerStack = config.Bind("21 - Defense Milestones", "Hardened Per Stack", 3f, "Percent less damage per stack.", true, (AcceptableValueBase?)(object)Settings.UpTo(30f)); HardenedMaxStacks = config.Bind("21 - Defense Milestones", "Hardened Max Stacks", 5, "The most stacks you can have.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 20)); HardenedDuration = config.Bind("21 - Defense Milestones", "Hardened Duration", 8f, "Seconds the stacks last after the last hit that added one.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 60f)); } private static void BindLastStand(SyncedConfiguration config) { LastStandLevel = config.Bind("21 - Defense Milestones", "Last Stand Level", 100f, "Defense level that unlocks Last Stand: a blow that would kill you leaves you at 1 health instead.", true, (AcceptableValueBase?)(object)Levels); LastStandCooldown = config.Bind("21 - Defense Milestones", "Last Stand Cooldown", 600f, "Seconds before Last Stand can save you again.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(10f, 7200f)); LastStandInvulnerability = config.Bind("21 - Defense Milestones", "Last Stand Invulnerability", 2f, "Seconds you take no damage at all after Last Stand saved you.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); } } public static class DefenseSettings { public const string Section = "19 - Defense"; public const string ExperienceSection = "20 - Defense Experience"; public const string MilestonesSection = "21 - Defense Milestones"; public const string GuardSection = "22 - Defense Guard"; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<bool> ShowCallouts { get; private set; } public static ConfigEntry<bool> ShowPlate { get; private set; } public static ConfigEntry<float> MaxHealth { get; private set; } public static ConfigEntry<float> FoodHealth { get; private set; } public static ConfigEntry<float> DamageReduction { get; private set; } public static ConfigEntry<float> Regeneration { get; private set; } public static ConfigEntry<float> RegenerationInterval { get; private set; } public static ConfigEntry<float> OutOfCombatDelay { get; private set; } public static ConfigEntry<float> Poise { get; private set; } public static ConfigEntry<float> ParryWindow { get; private set; } public static ConfigEntry<float> BlockStamina { get; private set; } public static ConfigEntry<float> DodgeStamina { get; private set; } public static void Initialize(SyncedConfiguration config) { Enabled = config.Bind("19 - Defense", "Defense Enabled", true, "Turns every Defense perk, milestone, guard perk and Defense experience on or off. Off, fights play exactly as in the game. Levels are kept either way.", true, null); ShowCallouts = config.Bind("19 - Defense", "Show Callouts", true, "Shows words like Riposte!, Reflex! and Bash! floating where they happen. Each player's own.", false, null); ShowPlate = config.Bind("19 - Defense", "Show Plate", true, "Shows your damage reduction on a plate in the inventory's stat column, with every Defense bonus in its tooltip. Each player's own.", false, null); BindVitality(config); BindCombat(config); DefenseExperienceSettings.Initialize(config); DefenseMilestoneSettings.Initialize(config); DefenseGuardSettings.Initialize(config); } private static void BindVitality(SyncedConfiguration config) { MaxHealth = config.Bind("19 - Defense", "Max Health At 100", 25f, "Health added to your base health (the game's 25) at level 100, food or no food.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); FoodHealth = config.Bind("19 - Defense", "Food Health At 100", 10f, "Percent more health from the food you have eaten, at level 100, counted after cooking stars.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); DamageReduction = config.Bind("19 - Defense", "Damage Reduction At 100", 10f, "Percent less damage taken at level 100, from every source, after armour and blocking.", true, (AcceptableValueBase?)(object)Settings.UpTo(90f)); Regeneration = config.Bind("19 - Defense", "Regeneration At 100", 1f, "Percent of your max health healed every Regeneration Interval at level 100, while you are out of combat. The game's health regeneration modifiers (resting, meads) apply to it.", true, (AcceptableValueBase?)(object)Settings.UpTo(20f)); RegenerationInterval = config.Bind("19 - Defense", "Regeneration Interval", 10f, "Seconds between two out-of-combat heals.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 60f)); OutOfCombatDelay = config.Bind("19 - Defense", "Out Of Combat Delay", 10f, "Seconds without attacking, blocking or taking damage before out-of-combat regeneration starts.", true, (AcceptableValueBase?)(object)Settings.UpTo(120f)); } private static void BindCombat(SyncedConfiguration config) { Poise = config.Bind("19 - Defense", "Poise At 100", 25f, "Percent more stagger damage it takes to stagger you, or to break your guard while blocking, at level 100.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); ParryWindow = config.Bind("19 - Defense", "Parry Window At 100", 0.1f, "Seconds added to the game's 0.25 second parry window at level 100, so a block raised a little earlier still parries.", true, (AcceptableValueBase?)(object)Settings.UpTo(0.5f)); BlockStamina = config.Bind("19 - Defense", "Block Stamina Reduction At 100", 10f, "Percent less stamina spent blocking, at level 100.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); DodgeStamina = config.Bind("19 - Defense", "Dodge Stamina Reduction At 100", 10f, "Percent less stamina spent dodging, at level 100, on top of what the game's Dodge skill takes off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } } public enum IngredientOrder { LowestStarsFirst, HighestStarsFirst } public static class DisplaySettings { public const string Section = "7 - Display"; public static ConfigEntry<bool> StarsOnIcons { get; private set; } public static ConfigEntry<IngredientOrder> Order { get; private set; } public static void Initialize(SyncedConfiguration config) { StarsOnIcons = config.Bind("7 - Display", "Stars On Icons", true, "Shows a starred dish's stars on its icon in inventories, containers and the hotbar.", false, null); Order = config.Bind("7 - Display", "Ingredient Order", IngredientOrder.LowestStarsFirst, "Which stacks a recipe uses first: the lowest stars (keeps your best food) or the highest (the best result).", false, null); } } public static class DominoSettings { public const string Section = "11 - Felling"; public static ConfigEntry<float> ImpactAt100 { get; private set; } public static ConfigEntry<int> MaxChain { get; private set; } public static void Initialize(SyncedConfiguration config) { ImpactAt100 = config.Bind("11 - Felling", "Domino Impact At 100", 200f, "Percent more damage a tree felled by a level 100 woodcutter deals when it falls onto other trees, logs and stumps, so it can knock them over in turn. Players, creatures and buildings take the game's own damage.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); MaxChain = config.Bind("11 - Felling", "Domino Max Chain", 5, "How many trees in a row the harder impacts reach: the first tree a felled one knocks over is 1. Further down the chain, logs hit with the game's own force.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 50)); } } public static class EmbeddedIcon { private static readonly Dictionary<string, Sprite> loaded = new Dictionary<string, Sprite>(); public static Sprite Load(string name) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 if (loaded.TryGetValue(name, out var value)) { return value; } if ((int)SystemInfo.graphicsDeviceType != 4) { value = EmbeddedSprite.Load(typeof(EmbeddedIcon).Assembly, "GrindstoneSkills.assets." + name + ".png", name); } if ((Object)(object)value == (Object)null && (int)SystemInfo.graphicsDeviceType != 4) { GrindstoneSkills.Log.LogWarning((object)("The icon assets/" + name + ".png could not be loaded; the game's icon stays.")); } loaded[name] = value; return value; } } public static class ExperienceSettings { public const string Section = "5 - Experience"; public static ConfigEntry<float> Multiplier { get; private set; } public static ConfigEntry<bool> TierScaling { get; private set; } public static ConfigEntry<float> TierReferenceValue { get; private set; } public static ConfigEntry<float> TierMaximum { get; private set; } public static ConfigEntry<float> DiscoveryMultiplier { get; private set; } public static ConfigEntry<float> FermenterTap { get; private set; } public static void Initialize(SyncedConfiguration config) { Multiplier = config.Bind("5 - Experience", "Experience Multiplier", 1f, "Multiplies all Cooking experience earned at kitchens.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); TierScaling = config.Bind("5 - Experience", "Tier Scaling", true, "Richer dishes teach more: experience is multiplied by the dish's health + stamina + eitr divided by Tier Reference Food Value, never below 1.", true, null); TierReferenceValue = config.Bind("5 - Experience", "Tier Reference Food Value", 50f, "The food value (health + stamina + eitr) that earns the game's own experience.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 500f)); TierMaximum = config.Bind("5 - Experience", "Tier Maximum Multiplier", 3f, "The most Tier Scaling can multiply experience by.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 10f)); DiscoveryMultiplier = config.Bind("5 - Experience", "Discovery Multiplier", 3f, "Experience multiplier the first time a character makes each dish.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 10f)); FermenterTap = config.Bind("5 - Experience", "Fermenter Tap Experience", 1f, "Cooking experience for tapping a fermenter (the game gives none). 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); } } public static class ExtraFood { public struct State { public bool Applied; public float Chance; public int Amount; } public static State Apply() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null) { return default(State); } State result = new State { Applied = true, Chance = instance.m_craftBonusChance, Amount = instance.m_craftBonusAmount }; instance.m_craftBonusChance = Mathf.Clamp01(KitchenSettings.ExtraFoodChance.Value / 100f); instance.m_craftBonusAmount = Perks.ExtraFoodAmount; return result; } public static void Restore(State state) { InventoryGui instance = InventoryGui.instance; if (state.Applied && !((Object)(object)instance == (Object)null)) { instance.m_craftBonusChance = state.Chance; instance.m_craftBonusAmount = state.Amount; } } } public static class FarmingExperienceSettings { public const string Section = "31 - Farming Experience"; public static ConfigEntry<float> Multiplier { get; private set; } public static ConfigEntry<bool> TierScaling { get; private set; } public static ConfigEntry<float> TierReferenceValue { get; private set; } public static ConfigEntry<float> TierMaximum { get; private set; } public static ConfigEntry<float> DiscoveryMultiplier { get; private set; } public static ConfigEntry<float> GiantMultiplier { get; private set; } public static ConfigEntry<float> TendingExperience { get; private set; } public static void Initialize(SyncedConfiguration config) { BindScaling(config); BindBonuses(config); } private static void BindScaling(SyncedConfiguration config) { Multiplier = config.Bind("31 - Farming Experience", "Experience Multiplier", 1f, "Multiplies all Farming experience from planting and picking crops.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); TierScaling = config.Bind("31 - Farming Experience", "Tier Scaling", true, "Richer crops teach more: experience is multiplied by the crop's value divided by Tier Reference Value, never below 1. A crop's value is its food value, or that of what it is milled into or cooked into.", true, null); TierReferenceValue = config.Bind("31 - Farming Experience", "Tier Reference Value", 30f, "The crop value (health + stamina + eitr) that earns the game's own experience.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 500f)); TierMaximum = config.Bind("31 - Farming Experience", "Tier Maximum Multiplier", 3f, "The most Tier Scaling can multiply experience by.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 10f)); } private static void BindBonuses(SyncedConfiguration config) { DiscoveryMultiplier = config.Bind("31 - Farming Experience", "Discovery Multiplier", 3f, "Experience multiplier the first time a character picks each kind of crop.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 10f)); GiantMultiplier = config.Bind("31 - Farming Experience", "Giant Crop Multiplier", 5f, "Experience multiplier for picking a giant crop.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 20f)); TendingExperience = config.Bind("31 - Farming Experience", "Tending Experience", 0.25f, "Farming experience for each plant you tend. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(5f)); } } public static class FarmingPerkSettings { public const string Section = "30 - Farming Perks"; private static readonly AcceptableValueRange<int> Levels = new AcceptableValueRange<int>(0, 101); public static ConfigEntry<float> GrowthSpeedAt100 { get; private set; } public static ConfigEntry<float> GrowSpaceAt100 { get; private set; } public static ConfigEntry<float> BonusYieldAt100 { get; private set; } public static ConfigEntry<float> SeedReturnAt100 { get; private set; } public static ConfigEntry<int> AutoReplantLevel { get; private set; } public static ConfigEntry<int> RowOfThreeLevel { get; private set; } public static ConfigEntry<int> RowOfFiveLevel { get; private set; } public static ConfigEntry<int> HeatToleranceLevel { get; private set; } public static ConfigEntry<int> ColdToleranceLevel { get; private set; } public static ConfigEntry<float> RainBonus { get; private set; } public static ConfigEntry<float> TendingBonus { get; private set; } public static ConfigEntry<float> TendingRadius { get; private set; } public static ConfigEntry<int> AlmanacLevel { get; private set; } public static void Initialize(SyncedConfiguration config) { BindGrowth(config); BindPicking(config); BindMilestones(config); BindCare(config); } private static void BindGrowth(SyncedConfiguration config) { GrowthSpeedAt100 = config.Bind("30 - Farming Perks", "Growth Speed At Level 100", 40f, "Percent faster growth for everything a player plants, from the planter's level.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); GrowSpaceAt100 = config.Bind("30 - Farming Perks", "Grow Space Reduction At Level 100", 40f, "Percent less room a crop needs around it to grow, from the planter's level, so crops stand closer.", true, (AcceptableValueBase?)(object)Settings.UpTo(80f)); } private static void BindPicking(SyncedConfiguration config) { BonusYieldAt100 = config.Bind("30 - Farming Perks", "Bonus Yield Chance At Level 100", 50f, "Chance in percent of the game's bonus crop when picking a crop, from the picker's level. Replaces the game's own 25%.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); SeedReturnAt100 = config.Bind("30 - Farming Perks", "Seed Return Chance At Level 100", 30f, "Chance in percent that picking a crop also gives back the seed it grew from, with the crop's stars, from the picker's level.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); AutoReplantLevel = config.Bind("30 - Farming Perks", "Auto Replant Level", 50, "From this level, picking a crop puts its plant back in the same spot with a seed from your inventory (each player can turn it off). 101 turns it off.", true, (AcceptableValueBase?)(object)Levels); } private static void BindMilestones(SyncedConfiguration config) { RowOfThreeLevel = config.Bind("30 - Farming Perks", "Row Of Three Level", 25, "From this level, placing a seed plants a row of three (each pays its own seed). 101 turns it off.", true, (AcceptableValueBase?)(object)Levels); RowOfFiveLevel = config.Bind("30 - Farming Perks", "Row Of Five Level", 50, "From this level, a row of five. 101 turns it off.", true, (AcceptableValueBase?)(object)Levels); HeatToleranceLevel = config.Bind("30 - Farming Perks", "Heat Tolerance Level", 75, "From this planter level, crops grow in the Ashlands without a shield. 101 turns it off.", true, (AcceptableValueBase?)(object)Levels); ColdToleranceLevel = config.Bind("30 - Farming Perks", "Cold Tolerance Level", 100, "From this planter level, crops that grow in the Meadows also grow in the Mountain and Deep North. 101 turns it off.", true, (AcceptableValueBase?)(object)Levels); AlmanacLevel = config.Bind("30 - Farming Perks", "Almanac Level", 20, "From this level, a growing crop's hover shows the odds of the stars it will ripen with. 101 turns it off.", true, (AcceptableValueBase?)(object)Levels); } private static void BindCare(SyncedConfiguration config) { RainBonus = config.Bind("30 - Farming Perks", "Rain Growth Bonus", 50f, "Percent faster growth while it rains, for everything a player planted.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); TendingBonus = config.Bind("30 - Farming Perks", "Tending Bonus", 10f, "Percent of its grow time a plant gains when tended (the use key on a growing plant), once per in-game day. 0 turns tending off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); TendingRadius = config.Bind("30 - Farming Perks", "Tending Radius", 2.5f, "Tending reaches every growing plant within this many metres of the one you use.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 10f)); } } public static class FarmingSettings { public const string Section = "29 - Farming"; public const string PerksSection = "30 - Farming Perks"; public const string ExperienceSection = "31 - Farming Experience"; public const string CompostSection = "32 - Compost"; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<bool> CropStars { get; private set; } public static ConfigEntry<float> SeedLevelsPerStar { get; private set; } public static ConfigEntry<float> CompanionLevels { get; private set; } public static ConfigEntry<float> CompanionRadius { get; private set; } public static ConfigEntry<int> CompanionKinds { get; private set; } public static ConfigEntry<float> GiantChanceAt100 { get; private set; } public static ConfigEntry<int> GiantYield { get; private set; } public static ConfigEntry<float> GiantSize { get; private set; } public static ConfigEntry<bool> ShowCallouts { get; private set; } public static ConfigEntry<bool> RowPlanting { get; private set; } public static ConfigEntry<bool> AutoReplant { get; private set; } public static void Initialize(SyncedConfiguration config) { BindStars(config); BindGiants(config); BindPlayer(config); FarmingPerkSettings.Initialize(config); FarmingExperienceSettings.Initialize(config); CompostSettings.Initialize(config); } private static void BindStars(SyncedConfiguration config) { Enabled = config.Bind("29 - Farming", "Farming Enabled", true, "Turns every Farming feature on or off: crop stars, perks, the windmill's stars, compost, experience changes. Off, crops behave exactly as in the game. Levels and stars already on items are kept.", true, null); CropStars = config.Bind("29 - Farming", "Crop Stars", true, "Crops roll 0 to 3 stars when they ripen, from the planter's Farming level, with the odds table of section 3.", true, null); SeedLevelsPerStar = config.Bind("29 - Farming", "Seed Levels Per Star", 10f, "Levels added to the planter's level for each star of the seed a crop was planted from (heirloom seeds).", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); CompanionLevels = config.Bind("29 - Farming", "Companion Levels", 5f, "Levels added for each other kind of crop growing or ripe near a crop when it ripens (companion planting). A seed and its crop are one kind.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); CompanionRadius = config.Bind("29 - Farming", "Companion Radius", 2f, "How far away, in metres, another crop counts as a companion.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 10f)); CompanionKinds = config.Bind("29 - Farming", "Companion Kinds", 3, "The most companion kinds that count for one crop.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 10)); } private static void BindGiants(SyncedConfiguration config) { GiantChanceAt100 = config.Bind("29 - Farming", "Giant Crop Chance At Level 100", 2f, "Chance in percent that a crop ripens into a giant, from the planter's level (half at level 50). A giant is always 3 stars.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); GiantYield = config.Bind("29 - Farming", "Giant Crop Yield", 6, "How many times the crop's normal amount a giant gives.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 50)); GiantSize = config.Bind("29 - Farming", "Giant Crop Size", 2.5f, "How many times bigger a giant crop stands.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 5f)); } private static void BindPlayer(SyncedConfiguration config) { ShowCallouts = config.Bind("29 - Farming", "Show Callouts", true, "Shows words like Giant turnip! and returned seeds floating above your crops.", false, null); RowPlanting = config.Bind("29 - Farming", "Row Planting", true, "Plants a whole row at once once your level allows it (see Row Of Three Level). Hold the alternative place key (Shift) to plant one.", false, null); AutoReplant = config.Bind("29 - Farming", "Auto Replant", true, "Puts a picked crop's plant back in its spot with a seed from your inventory, once your level allows it (see Auto Replant Level).", false, null); } } public static class FellPerkSettings { public const string Section = "11 - Felling"; public const float MaxChance = 100f; public static ConfigEntry<float> CleanFellAt100 { get; private set; } public static ConfigEntry<float> ReplantingAt100 { get; private set; } public static void Initialize(SyncedConfiguration config) { CleanFellAt100 = config.Bind("11 - Felling", "Clean Fell At 100", 100f, "Percent chance that a tree felled by a level 100 woodcutter takes its stump with it. The stump drops its wood as if you had chopped it.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); ReplantingAt100 = config.Bind("11 - Felling", "Replanting At 100", 50f, "Percent chance that a sapling of the same kind takes root, free, where a level 100 woodcutter felled a tree; the stump comes out too. Only for trees the game has a sapling for, and only where that sapling can grow.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } } public static class FindSettings { public const string Section = "13 - Finds"; public static ConfigEntry<float> ChanceAt0 { get; private set; } public static ConfigEntry<float> ChanceAt100 { get; private set; } public static void Initialize(SyncedConfiguration config) { ChanceAt0 = config.Bind("13 - Finds", "Find Chance At 0", 2f, "Percent chance that a tree felled by a level 0 woodcutter hides a find from GrindstoneSkills.Finds.yml. Domino fells count too. 0 with the next setting at 0 turns finds off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); ChanceAt100 = config.Bind("13 - Finds", "Find Chance At 100", 15f, "Percent chance of a find for a level 100 woodcutter; levels in between are in proportion.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); FindFile.Register(config); } } public static class FishingBigFishSettings { public const string Section = "53 - Big Fish"; public static ConfigEntry<float> BigOneChanceAt100 { get; private set; } public static ConfigEntry<float> NightBonus { get; private set; } public static ConfigEntry<float> BaitBigOnePerStar { get; private set; } public static ConfigEntry<float> LegendaryChance { get; private set; } public static ConfigEntry<float> LegendaryLevel { get; private set; } public static ConfigEntry<int> LegendaryThrashes { get; private set; } public static ConfigEntry<bool> AnnounceLegendary { get; private set; } public static void Initialize(SyncedConfiguration config) { BindBigOnes(config); BindLegendary(config); } private static void BindBigOnes(SyncedConfiguration config) { BigOneChanceAt100 = config.Bind("53 - Big Fish", "Big One Chance At 100", 25f, "Percent chance, for a level 100 angler, that a fish grows a level when it is hooked (It's a big one!). Rolled again for each further level, up to level 5.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); NightBonus = config.Bind("53 - Big Fish", "Night Big One Bonus", 50f, "Percent more big-one chance at night.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); BaitBigOnePerStar = config.Bind("53 - Big Fish", "Bait Big One Bonus Per Star", 5f, "Percentage points added to the big-one chance for each star of the bait on the hook, at any level.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); } private static void BindLegendary(SyncedConfiguration config) { LegendaryChance = config.Bind("53 - Big Fish", "Legendary Chance", 0.5f, "Percent of fish spawned in the world that are legendary: level 6, three times the size, glowing. Fish dropped back in the water never become legendary. 0 turns new legendary fish off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); LegendaryLevel = config.Bind("53 - Big Fish", "Legendary Level", 50f, "Fishing level from which a legendary fish takes your bait. 0 lets everyone hook them; above 100 nobody can.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); LegendaryThrashes = config.Bind("53 - Big Fish", "Legendary Thrashes", 8, "Thrashes after which a hooked legendary fish is spent. 0 means it never tires.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 50)); AnnounceLegendary = config.Bind("53 - Big Fish", "Announce Legendary Catches", true, "Tells every player on the server when someone lands a legendary fish.", true, null); } } public static class FishingBiteSettings { public const string Section = "52 - Bites"; public static ConfigEntry<float> BiteChanceAt100 { get; private set; } public static ConfigEntry<float> DawnDuskBonus { get; private set; } public static ConfigEntry<float> RainBonus { get; private set; } public static ConfigEntry<float> BaitBitePerStar { get; private set; } public static ConfigEntry<string> ChumItems { get; private set; } public static ConfigEntry<float> ChumBonus { get; private set; } public static ConfigEntry<float> ChumRadius { get; private set; } public static ConfigEntry<float> ChumDuration { get; private set; } public static ConfigEntry<float> SpeciesSenseLevel { get; private set; } public static ConfigEntry<float> SizeSenseLevel { get; private set; } public static ConfigEntry<float> WaterSenseLevel { get; private set; } public static void Initialize(SyncedConfiguration config) { BindBites(config); BindChum(config); BindSenses(config); } private static void BindBites(SyncedConfiguration config) { BiteChanceAt100 = config.Bind("52 - Bites", "Bite Chance At 100", 100f, "Percent more bites for a level 100 angler: 100 doubles the game's chance that a fish goes for your float.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); DawnDuskBonus = config.Bind("52 - Bites", "Dawn And Dusk Bite Bonus", 50f, "Percent more bites at the height of dawn and dusk, fading in and out around them (the hours fish jump most in the game).", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); RainBonus = config.Bind("52 - Bites", "Rain Bite Bonus", 25f, "Percent more bites in wet weather.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); BaitBitePerStar = config.Bind("52 - Bites", "Bait Bite Bonus Per Star", 20f, "Percent more bites for each star of the bait on the hook. Baits crafted at the prep table roll Cooking stars like any dish.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); } private static void BindChum(SyncedConfiguration config) { ChumItems = config.Bind("52 - Bites", "Chum Items", "Entrails, Bloodbag", "Item prefab names, comma-separated, that are chum: dropped in the water they draw fish to any float near them, then dissolve. Empty turns chum off.", true, null); ChumBonus = config.Bind("52 - Bites", "Chum Bite Bonus", 100f, "Percent more bites for a float with chum floating near it.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); ChumRadius = config.Bind("52 - Bites", "Chum Radius", 12f, "How close chum must float to a float, in metres.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 50f)); ChumDuration = config.Bind("52 - Bites", "Chum Duration", 60f, "Seconds a piece of chum lasts in the water before it dissolves (checked every 10 seconds).", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(10f, 3600f)); } private static void BindSenses(SyncedConfiguration config) { SpeciesSenseLevel = config.Bind("52 - Bites", "Species Sense Level", 25f, "Fishing level from which a nibble names the fish, and a cast that lands tells the fishing conditions. 0 gives it to everyone; above 100 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); SizeSenseLevel = config.Bind("52 - Bites", "Size Sense Level", 50f, "Fishing level from which a nibble also tells the fish's size (level), so you can let a small one go. Looking at a fish in the water shows it too.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); WaterSenseLevel = config.Bind("52 - Bites", "Water Sense Level", 75f, "Fishing level from which a cast that lands tells which fish in reach take your bait, and whether something big lurks.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); } } public static class FishingCatchSettings { public const string Section = "54 - Catch And Tackle"; public static ConfigEntry<float> BonusItemChanceAt100 { get; private set; } public static ConfigEntry<float> DoubleBonusLevel { get; private set; } public static ConfigEntry<float> BaitSaverAt100 { get; private set; } public static ConfigEntry<float> SnagChanceAt0 { get; private set; } public static ConfigEntry<float> SnagChanceAt100 { get; private set; } public static ConfigEntry<float> SnagWait { get; private set; } public static ConfigEntry<float> CastDistanceAt100 { get; private set; } public static ConfigEntry<float> LineLengthAt100 { get; private set; } public static ConfigEntry<float> FilletLevelsPerFishLevel { get; private set; } public static void Initialize(SyncedConfiguration config) { BindCatch(config); BindSnags(config); BindTackle(config); SnagFile.Register(config); } private static void BindCatch(SyncedConfiguration config) { BonusItemChanceAt100 = config.Bind("54 - Catch And Tackle", "Bonus Item Chance At 100", 40f, "Percent chance that a landed fish brings its bonus item (Perch: stone or amber, Tetra: obsidian or coins...) for a level 100 angler. The game's own is 20 at any level; levels in between are in proportion. Legendary fish always bring one.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); DoubleBonusLevel = config.Bind("54 - Catch And Tackle", "Double Bonus Level", 50f, "Fishing level from which a bonus roll can bring two items instead of one. Above 100 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); BaitSaverAt100 = config.Bind("54 - Catch And Tackle", "Bait Saver At 100", 30f, "Percent chance, for a level 100 angler, that landing a fish gives the bait back. The game already gives it back when nothing bit.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); FilletLevelsPerFishLevel = config.Bind("54 - Catch And Tackle", "Fillet Levels Per Fish Level", 10f, "Cleaning a fish at the prep table rolls Cooking stars for the raw fish it gives. Each level of the fish above 1 adds this many to the cook's level for that roll, like starred ingredients: a level 5 fish +40, a legendary +50. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); } private static void BindSnags(SyncedConfiguration config) { SnagChanceAt0 = config.Bind("54 - Catch And Tackle", "Snag Chance At 0", 2f, "Percent chance that a cast left in the water for Snag Wait seconds without a bite snags something from GrindstoneSkills.Snags.yml, for a level 0 angler. It reels in heavy, then lands in your inventory. 0 with the next setting at 0 turns snags off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); SnagChanceAt100 = config.Bind("54 - Catch And Tackle", "Snag Chance At 100", 6f, "The same for a level 100 angler; levels in between are in proportion.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); SnagWait = config.Bind("54 - Catch And Tackle", "Snag Wait", 8f, "Seconds a cast must sit in the water, without a fish on the line, before it can snag. Rolled once per cast.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 120f)); } private static void BindTackle(SyncedConfiguration config) { CastDistanceAt100 = config.Bind("54 - Catch And Tackle", "Cast Distance At 100", 30f, "Percent farther a level 100 angler casts.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); LineLengthAt100 = config.Bind("54 - Catch And Tackle", "Line Length At 100", 50f, "Percent longer line for a level 100 angler. The game's line snaps when the float is 30 m from the rod.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); } } public static class FishingExperienceSettings { public const string Section = "50 - Fishing"; public static ConfigEntry<float> Multiplier { get; private set; } public static ConfigEntry<float> EmptyReel { get; private set; } public static ConfigEntry<float> FightReel { get; private set; } public static ConfigEntry<float> Catch { get; private set; } public static ConfigEntry<float> SizeBonus { get; private set; } public static ConfigEntry<float> Discovery { get; private set; } public static ConfigEntry<float> NewSize { get; private set; } public static void Initialize(SyncedConfiguration config) { BindReeling(config); BindCredits(config); } private static void BindReeling(SyncedConfiguration config) { Multiplier = config.Bind("50 - Fishing", "Experience Multiplier", 1f, "Multiplies all Fishing experience: reeling, catches and discoveries.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); EmptyReel = config.Bind("50 - Fishing", "Empty Reel Experience", 0f, "Percent of the game's reeling experience earned while no fish is on the line. The game gives 100, which makes casting and reeling an empty line the fastest way to train; 0 stops that.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); FightReel = config.Bind("50 - Fishing", "Fight Experience", 100f, "Percent of the game's reeling experience earned while a fish is on the line.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); } private static void BindCredits(SyncedConfiguration config) { Catch = config.Bind("50 - Fishing", "Catch Experience", 10f, "Fishing experience for each fish you land, times its species (the square root of how hard it pulls against a Perch: a Pike x1.3, a Northern salmon x2.6) and its size. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); SizeBonus = config.Bind("50 - Fishing", "Size Experience Bonus", 50f, "Percent more catch experience for each level of the fish above 1: 50 gives x3 for a level 5 fish.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); Discovery = config.Bind("50 - Fishing", "Discovery Experience", 30f, "Fishing experience for the first catch of each species, times its species. Each character discovers each species once. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); NewSize = config.Bind("50 - Fishing", "New Size Experience", 10f, "Fishing experience for the first catch of a species you know at a size (level) you have not landed before, times its species. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); } } public static class FishingFightSettings { public const string Section = "51 - Fishing Fight"; public static ConfigEntry<bool> TensionEnabled { get; private set; } public static ConfigEntry<float> TensionBuildAt0 { get; private set; } public static ConfigEntry<float> TensionBuildAt100 { get; private set; } public static ConfigEntry<float> TensionEase { get; private set; } public static ConfigEntry<float> StrikeWindowAt0 { get; private set; } public static ConfigEntry<float> StrikeWindowAt100 { get; private set; } public static ConfigEntry<float> PerfectStrikeWindow { get; private set; } public static ConfigEntry<float> TiringPerThrash { get; private set; } public static ConfigEntry<int> ThrashesToTire { get; private set; } public static ConfigEntry<float> SpentReelSpeed { get; private set; } public static ConfigEntry<float> GraceLevel { get; private set; } public static ConfigEntry<float> GraceSeconds { get; private set; } public static void Initialize(SyncedConfiguration config) { BindTension(config); BindStrike(config); BindTiring(config); } private static void BindTension(SyncedConfiguration config) { TensionEnabled = config.Bind("51 - Fishing Fight", "Line Tension", true, "Reeling while a hooked fish thrashes builds tension on the line, shown in a bar under the crosshair; when it is full the line snaps. Ease off while it thrashes, reel while it rests. Off, the line only breaks as in the game.", true, null); TensionBuildAt0 = config.Bind("51 - Fishing Fight", "Tension Build At 0", 60f, "Percent of the line's strength a level 0 angler builds per second of reeling against a thrashing fish.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 500f)); TensionBuildAt100 = config.Bind("51 - Fishing Fight", "Tension Build At 100", 30f, "The same for a level 100 angler.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 500f)); TensionEase = config.Bind("51 - Fishing Fight", "Tension Ease", 50f, "Percent of the line's strength the tension drops per second while you are not reeling. It drops a fifth as fast while you reel a fish that is not thrashing.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 500f)); } private static void BindStrike(SyncedConfiguration config) { StrikeWindowAt0 = config.Bind("51 - Fishing Fight", "Strike Window At 0", 0.5f, "Seconds after a nibble in which reeling sets the hook, for a level 0 angler. The game's own is 0.5.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.1f, 5f)); StrikeWindowAt100 = config.Bind("51 - Fishing Fight", "Strike Window At 100", 1f, "The same for a level 100 angler.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.1f, 5f)); PerfectStrikeWindow = config.Bind("51 - Fishing Fight", "Perfect Strike Window", 0.2f, "Seconds after a nibble in which starting to reel is a perfect strike: the fish skips its first thrash. Reeling already when it nibbles never counts. 0 turns perfect strikes off.", true, (AcceptableValueBase?)(object)Settings.UpTo(2f)); } private static void BindTiring(SyncedConfiguration config) { TiringPerThrash = config.Bind("51 - Fishing Fight", "Tiring Per Thrash", 15f, "Percent shorter each thrash of a hooked fish is than its first: the third thrash lasts 70% as long. Never below 20%.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); ThrashesToTire = config.Bind("51 - Fishing Fight", "Thrashes To Tire", 4, "Thrashes after which a hooked fish is spent: it thrashes no more and comes in faster. Legendary fish take the Legendary Thrashes setting instead. 0 turns tiring out off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 50)); SpentReelSpeed = config.Bind("51 - Fishing Fight", "Spent Reel Speed", 50f, "Percent faster the line comes in once the fish is spent.", true, (AcceptableValueBase?)(object)Settings.UpTo(300f)); GraceLevel = config.Bind("51 - Fishing Fight", "Grace Level", 75f, "Fishing level from which running out of stamina with a fish on the line does not lose it at once: the fish takes line while you catch your breath, once per fish. 0 gives it to everyone; above 100 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); GraceSeconds = config.Bind("51 - Fishing Fight", "Grace Seconds", 4f, "How long the grace lasts. It ends early once a quarter of your stamina is back; still out of stamina at the end, the fish gets away.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 30f)); } } public static class FishingSettings { public const string Section = "50 - Fishing"; public const string FightSection = "51 - Fishing Fight"; public const string BitesSection = "52 - Bites"; public const string BigFishSection = "53 - Big Fish"; public const string CatchSection = "54 - Catch And Tackle"; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<bool> ShowCallouts { get; private set; } public static void Initialize(SyncedConfiguration config) { Enabled = config.Bind("50 - Fishing", "Fishing Enabled", true, "Turns every Fishing feature on or off: line tension, strikes, bites, big and legendary fish, snags, the angler's log, experience changes. Off, fishing plays exactly as in the game. Levels are kept either way.", true, null); ShowCallouts = config.Bind("50 - Fishing", "Show Callouts", true, "Shows words like It's a big one! and finds floating above the water near you.", false, null); FishingExperienceSettings.Initialize(config); FishingFightSettings.Initialize(config); FishingBiteSettings.Initialize(config); FishingBigFishSettings.Initialize(config); FishingCatchSettings.Initialize(config); } } public static class FloatingText { public const float Range = 40f; public static void Show(Vector3 position, string text) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Camera mainCamera = Utils.GetMainCamera(); if (!((Object)(object)mainCamera == (Object)null) && !((Object)(object)DamageText.instance == (Object)null) && !Hud.IsUserHidden() && !string.IsNullOrEmpty(text)) { float num = Vector3.Distance(((Component)mainCamera).transform.position, position); if (num <= 40f) { DamageText.instance.AddInworldText((TextType)7, position, num, text, false); } } } } public static class ForagePerkSettings { public const string Section = "34 - Forage Perks"; public static ConfigEntry<float> ExtraYieldAt100 { get; private set; } public static ConfigEntry<float> BestTimeLevels { get; private set; } public static ConfigEntry<int> SweepLevel { get; private set; } public static ConfigEntry<float> SweepRadiusAt100 { get; private set; } public static void Initialize(SyncedConfiguration config) { ExtraYieldAt100 = config.Bind("34 - Forage Perks", "Extra Yield Chance At 100", 50f, "Percent chance at level 100 that a pick gives one more, growing linearly from 0 at level 0. It replaces the game's own 25% for these picks.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); BestTimeLevels = config.Bind("34 - Forage Perks", "Best Time Levels", 20f, "Levels added to the picker's Foraging level for the star roll when a plant is picked at its best time (the Forage file's 'best'). Odds rows above level 100 are reached this way.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); SweepLevel = config.Bind("34 - Forage Perks", "Sweep Level", 25, "From this Foraging level, picking a plant also picks every plant of the same kind around it. 101 turns sweep picking off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 101)); SweepRadiusAt100 = config.Bind("34 - Forage Perks", "Sweep Radius At 100", 4f, "How far sweep picking reaches, in metres, at level 100; at lower levels it reaches level / 100 of this (1 m at level 25).", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); } } public static class ForagingSettings { public const string Section = "33 - Foraging"; public const string PerksSection = "34 - Forage Perks"; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<bool> ShowCallouts { get; private set; } public static ConfigEntry<bool> ShowHints { get; private set; } public static ConfigEntry<float> ExperiencePerPick { get; private set; } public static ConfigEntry<float> BiomeStep { get; private set; } public static ConfigEntry<float> DiscoveryMultiplier { get; private set; } public static void Initialize(SyncedConfiguration config) { BindSwitches(config); BindExperience(config); ForagePerkSettings.Initialize(config); ForageFile.Register(config); } private static void BindSwitches(SyncedConfiguration config) { Enabled = config.Bind("33 - Foraging", "Foraging Enabled", true, "Turns every Foraging feature on or off: stars on picks, experience, extra yield, sweep picking, hints. Off, wild plants are picked exactly as in the game (the game trains Farming with them). Levels are kept either way.", true, null); ShowCallouts = config.Bind("33 - Foraging", "Show Callouts", true, "Shows words like Discovered Thistle! floating above plants you pick.", false, null); ShowHints = config.Bind("33 - Foraging", "Show Hints", true, "Adds a line to a wild plant's hover text saying when it is best picked, or that it is at its best now.", false, null); } private static void BindExperience(SyncedConfiguration config) { ExperiencePerPick = config.Bind("33 - Foraging", "Experience Per Pick", 3f, "Foraging experience for one pick in the Meadows, times the item's own factor in the Forage file. The game gives 1 per pick, to Farming.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); BiomeStep = config.Bind("33 - Foraging", "Experience Per Biome Step", 25f, "Percent more experience per pick for each biome step of the plant's biome: Meadows 0, Black Forest 1, Swamp 2, Mountain 3, Plains 4, Mistlands 5, Ashlands and Deep North 6.", true, (AcceptableValueBase?)(object)Settings.UpTo(200f)); DiscoveryMultiplier = config.Bind("33 - Foraging", "Discovery Multiplier", 3f, "The first pick of each kind of forage earns this many times the experience. Each character discovers each kind once. 1 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 20f)); } } public static class HookGuard { public static void Run(string context, Action action) { try { action(); } catch (Exception exception) { Guard.Report(exception, context); } } public static T Run<T>(string context, Func<T> func, T fallback) { try { return func(); } catch (Exception exception) { Guard.Report(exception, context); return fallback; } } } public static class HusbandryBreedingSettings { public const string Section = "25 - Breeding"; public static ConfigEntry<float> BreedingSpeed { get; private set; } public static ConfigEntry<int> HerdSize { get; private set; } public static ConfigEntry<float> BetterOffspring { get; private set; } public static ConfigEntry<int> MaxOffspringLevel { get; private set; } public static ConfigEntry<float> Twins { get; private set; } public static ConfigEntry<float> GrowthSpeed { get; private set; } public static ConfigEntry<float> ContentDuration { get; private set; } public static ConfigEntry<float> ContentBonus { get; private set; } public static void Initialize(SyncedConfiguration config) { BindBreeding(config); BindOffspring(config); BindContent(config); } private static void BindBreeding(SyncedConfiguration config) { BreedingSpeed = config.Bind("25 - Breeding", "Breeding Speed At 100", 100f, "Percent faster breeding with a level 100 keeper: pregnancy is shorter and fewer breeding checks are skipped. 100 halves the wait.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); HerdSize = config.Bind("25 - Breeding", "Herd Size At 100", 4, "More animals of a kind (young included) that may stand within the game's crowding range (10 m, 20 m for lox) before breeding stops, with a level 100 keeper. The game allows 4 to 10 by kind.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 20)); GrowthSpeed = config.Bind("25 - Breeding", "Growth Speed At 100", 100f, "Percent faster growing up for tamed young animals and faster hatching for warm eggs, with a level 100 keeper. 100 halves the time.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); } private static void BindOffspring(SyncedConfiguration config) { BetterOffspring = config.Bind("25 - Breeding", "Better Offspring At 100", 25f, "Percent chance, with a level 100 keeper, that a newborn or a laid egg is one star above its parent (the game passes the parent's own level on).", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); MaxOffspringLevel = config.Bind("25 - Breeding", "Max Offspring Level", 3, "The highest game level Better Offspring reaches: 1 is no star, 2 one star, 3 two stars (the most the game spawns). Drops double with each level. With Elite Creatures Reborn installed its own breeding decides stars, and Better Offspring gives one star more, up to one above the stronger parent.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 10)); Twins = config.Bind("25 - Breeding", "Twins At 100", 25f, "Percent chance, with a level 100 keeper, that a birth is followed by a second one (a second egg for hens) half a minute later. A twin never has twins of its own.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } private static void BindContent(SyncedConfiguration config) { ContentDuration = config.Bind("25 - Breeding", "Content Duration", 600f, "Seconds a tamed animal stays content after it is petted. Petting a content animal again earns no experience. 0 turns contentment off.", true, (AcceptableValueBase?)(object)Settings.UpTo(3600f)); ContentBonus = config.Bind("25 - Breeding", "Content Breeding Bonus", 50f, "Percent faster breeding while an animal is content, on top of Breeding Speed. Any level can pet.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); } } public static class HusbandryCompanionSettings { public const string Section = "27 - Companions"; public static ConfigEntry<float> PackDamage { get; private set; } public static ConfigEntry<float> PackToughness { get; private set; } public static ConfigEntry<float> FeederLevel { get; private set; } public static ConfigEntry<float> FeederRange { get; private set; } public static ConfigEntry<string> FeederRecipe { get; private set; } public static void Initialize(SyncedConfiguration config) { BindPack(config); BindFeeder(config); } private static void BindPack(SyncedConfiguration config) { PackDamage = config.Bind("27 - Companions", "Pack Damage At 100", 50f, "Percent more damage dealt by a tamed creature that follows a level 100 player (wolves, which the game lets you command).", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); PackToughness = config.Bind("27 - Companions", "Pack Toughness At 100", 33f, "Percent less damage taken by a tamed creature that follows a level 100 player.", true, (AcceptableValueBase?)(object)Settings.UpTo(90f)); } private static void BindFeeder(SyncedConfiguration config) { FeederLevel = config.Bind("27 - Companions", "Feeder Level", 25f, "Husbandry level from which a player can build the Animal Feeder (hammer, Misc). 0 lets everyone; above 100 hides it from the hammer. Feeders already built keep working.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); FeederRange = config.Bind("27 - Companions", "Feeder Range", 10f, "Metres. A hungry tamed animal, or one being tamed, walks to a feeder this close that holds food it eats, and eats one item from it.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(2f, 30f)); FeederRecipe = config.Bind("27 - Companions", "Feeder Recipe", "Wood:10, LeatherScraps:4", "What the Animal Feeder costs: item prefab names with amounts, comma-separated. Built at a workbench. Unknown items are skipped; an empty or unusable recipe falls back to Wood:10.", true, null); } } public static class HusbandryExperienceSettings { public const string Section = "28 - Husbandry Experience"; public static ConfigEntry<float> Multiplier { get; private set; } public static ConfigEntry<float> Taming { get; private set; } public static ConfigEntry<float> Tamed { get; private set; } public static ConfigEntry<float> Discovery { get; private set; } public static ConfigEntry<float> Feeding { get; private set; } public static ConfigEntry<float> Birth { get; private set; } public static ConfigEntry<float> Petting { get; private set; } public static ConfigEntry<float> Butchering { get; private set; } public static ConfigEntry<float> Honey { get; private set; } public static void Initialize(SyncedConfiguration config) { BindTaming(config); BindKeeping(config); } private static void BindTaming(SyncedConfiguration config) { Multiplier = config.Bind("28 - Husbandry Experience", "Experience Multiplier", 1f, "Multiplies all Husbandry experience.", true, (AcceptableValueBase?)(object)Settings.UpTo(10f)); Taming = config.Bind("28 - Husbandry Experience", "Taming Experience", 20f, "Experience for a whole taming's progress (0% to 100%), earned bit by bit while you are near, times the tier.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); Tamed = config.Bind("28 - Husbandry Experience", "Tamed Experience", 10f, "Experience when a creature near you becomes tame, times the tier.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); Discovery = config.Bind("28 - Husbandry Experience", "Discovery Multiplier", 3f, "The Tamed Experience is multiplied by this for the first creature of each kind a character tames. 1 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 10f)); } private static void BindKeeping(SyncedConfiguration config) { Feeding = config.Bind("28 - Husbandry Experience", "Feeding Experience", 1f, "Experience each time a tamed animal, or one being tamed, eats near you, times the tier.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Birth = config.Bind("28 - Husbandry Experience", "Birth Experience", 3f, "Experience for each birth or laid egg near you, times the tier.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Petting = config.Bind("28 - Husbandry Experience", "Petting Experience", 1f, "Experience for petting a tamed animal that is not content yet, times the tier.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Butchering = config.Bind("28 - Husbandry Experience", "Butchering Experience", 5f, "Experience for killing a tamed animal, times the tier.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Honey = config.Bind("28 - Husbandry Experience", "Honey Experience", 0.5f, "Experience per honey harvested from a beehive (no tier).", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } } public static class HusbandrySettings { public const string Section = "23 - Husbandry"; public const string TamingSection = "24 - Taming"; public const string BreedingSection = "25 - Breeding"; public const string YieldSection = "26 - Animal Yield"; public const string CompanionSection = "27 - Companions"; public const string ExperienceSection = "28 - Husbandry Experience"; public static ConfigEntry<bool> Enabled { get; private set; } public static ConfigEntry<float> KeeperRange { get; private set; } public static ConfigEntry<float> LoreLevel { get; private set; } public static ConfigEntry<bool> ShowCallouts { get; private set; } public static void Initialize(SyncedConfiguration config) { Enabled = config.Bind("23 - Husbandry", "Husbandry Enabled", true, "Turns every Husbandry feature on or off: taming, breeding, yield, companions, the feeder's eating, animal lore and experience. Off, animals behave exactly as in the game. Levels are kept either way.", true, null); KeeperRange = config.Bind("23 - Husbandry", "Keeper Range", 30f, "Metres. An animal is tended by the best Husbandry level among the players this close to it: that level drives its fed time, breeding, twins, better offspring, growth and produce, and players this close earn experience from it. Taming speed uses the game's own taming range (60 m) instead.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(5f, 100f)); LoreLevel = config.Bind("23 - Husbandry", "Animal Lore Level", 20f, "Husbandry level from which hovering an animal or an egg shows its timers: fed time left, taming time left, love and pregnancy, herd room, contentment, growing up and hatching. 0 gives it to everyone; above 100 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); ShowCallouts = config.Bind("23 - Husbandry", "Show Callouts", true, "Shows words like Twins! and Strong offspring! floating above animals near you.", false, null); HusbandryTamingSettings.Initialize(config); HusbandryBreedingSettings.Initialize(config); HusbandryYieldSettings.Initialize(config); HusbandryCompanionSettings.Initialize(config); HusbandryExperienceSettings.Initialize(config); } } public static class HusbandryTamingSettings { public const string Section = "24 - Taming"; public static ConfigEntry<float> TamingSpeed { get; private set; } public static ConfigEntry<float> FedDuration { get; private set; } public static ConfigEntry<float> CalmLevel { get; private set; } public static ConfigEntry<float> CalmBreakTime { get; private set; } public static ConfigEntry<string> TamingLevels { get; private set; } public static void Initialize(SyncedConfiguration config) { BindSpeed(config); BindCalm(config); } private static void BindSpeed(SyncedConfiguration config) { TamingSpeed = config.Bind("24 - Taming", "Taming Speed At 100", 100f, "Percent faster taming with a level 100 keeper within the creature's taming range (the game's 60 m). 100 tames in half the time. It stacks with the game's own taming boost.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); FedDuration = config.Bind("24 - Taming", "Fed Duration At 100", 100f, "Percent longer an animal stays fed after eating, with a level 100 keeper within Keeper Range when it eats. 100 doubles it (the game's 10 minutes become 20). Animals breed, heal and tame only while fed.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); TamingLevels = config.Bind("24 - Taming", "Taming Levels", "", "Creatures that need a keeper of some Husbandry level before taming makes progress: creature prefab names with a level, comma-separated, e.g. Lox:30, Asksvin:50, Moose:60. The keeper must stand within the game's taming range (60 m). Empty: anyone tames anything, as in the game.", true, null); } private static void BindCalm(SyncedConfiguration config) { CalmLevel = config.Bind("24 - Taming", "Calm Level", 50f, "Husbandry level from which a creature you have started taming (tameness above 0%) neither flees from nor attacks you, and you no longer frighten it, so taming goes on while you stand beside it. Only you: players below the level still scare it. 0 gives it to everyone; above 100 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); CalmBreakTime = config.Bind("24 - Taming", "Calm Break Time", 120f, "Seconds a creature stays wary of a calm player after that player hurt it: it defends itself as usual until then.", true, (AcceptableValueBase?)(object)Settings.UpTo(3600f)); } } public static class HusbandryYieldSettings { public const string Section = "26 - Animal Yield"; public static ConfigEntry<float> ButcherYield { get; private set; } public static ConfigEntry<bool> PrimeCuts { get; private set; } public static ConfigEntry<float> ProduceChance { get; private set; } public static ConfigEntry<float> ProduceInterval { get; private set; } public static ConfigEntry<float> ExtraHoney { get; private set; } public static void Initialize(SyncedConfiguration config) { BindButcher(config); BindLiving(config); } private static void BindButcher(SyncedConfiguration config) { ButcherYield = config.Bind("26 - Animal Yield", "Butcher Yield At 100", 50f, "Percent more drops from a tamed animal killed by a level 100 player (meat, hides, feathers; never trophies). A fraction is a chance of one more.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); PrimeCuts = config.Bind("26 - Animal Yield", "Prime Cuts", false, "When on, meat from a starred tamed animal carries its stars (up to 3) into Cooking, where starred ingredients raise a dish's odds. Meat of different stars then stacks apart. Turning it off takes effect for new meat; items already starred keep their stars until a restart.", true, null); } private static void BindLiving(SyncedConfiguration config) { ProduceChance = config.Bind("26 - Animal Yield", "Produce Chance At 100", 50f, "Percent chance, each Produce Interval, that a fed tamed animal with a level 100 keeper near drops one of its own materials without being killed: feathers from hens, leather scraps from boars, pelts from wolves and lox, hides from moose. Meat and trophies never. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); ProduceInterval = config.Bind("26 - Animal Yield", "Produce Interval", 1200f, "Seconds between an animal's produce rolls.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(60f, 86400f)); ExtraHoney = config.Bind("26 - Animal Yield", "Extra Honey At 100", 50f, "Percent chance per honey that a level 100 player harvesting a beehive gets one more. 0 turns it off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } } public static class Keys { public const string SlotCook = "grindstone_cook"; public const string SlotLevel = "grindstone_level"; public const string SlotInput = "grindstone_input"; public const string SlotStars = "grindstone_stars"; public const string BaseStars = "grindstone_base_stars"; public const string BaseLevel = "grindstone_base_level"; public const string MinStars = "grindstone_min_stars"; public const string RpcAddItem = "grindstone_AddItem"; public const string RpcAddBase = "grindstone_AddBase"; public const string RpcSetFilter = "grindstone_SetFilter"; public const string RpcCookCredit = "grindstone_CookCredit"; public const string ActiveFood = "grindstone_food_"; public const string MadeDishes = "grindstone_made"; public const string SailingLevel = "grindstone_sailing_level"; public const string ShipwrightLevel = "grindstone_shipwright"; public const string RpcLookout = "grindstone_Lookout"; public const string SailingSkillName = "grindstone_sailing"; public const string WoodPlayer = "grindstone_wood_player"; public const string WoodLevel = "grindstone_wood_level"; public const string WoodChain = "grindstone_wood_chain"; public const string CleanSplit = "grindstone_clean_split"; public const string WoodSize = "grindstone_wood_size"; public const string RpcWoodCredit = "grindstone_WoodCredit"; public const string RpcWoodCallout = "grindstone_WoodCallout"; public const string FelledTrees = "grindstone_felled"; public const string Replanted = "grindstone_replanted"; public const string FindsSync = "grindstone_finds"; public const string RpcCleanStrike = "grindstone_CleanStrike"; public const string RpcMineCallout = "grindstone_MineCallout"; public const string MinedDeposits = "grindstone_mined"; public const string MineFindsSync = "grindstone_mine_finds"; public const string ForagingSkillName = "grindstone_foraging"; public const string ForagingLevel = "grindstone_foraging_level"; public const string RpcForageMark = "grindstone_ForageMark"; public const string ForagedKinds = "grindstone_foraged"; public const string ForageSync = "grindstone_forage"; public const string HusbandrySkillName = "grindstone_husbandry"; public const string HusbandryLevel = "grindstone_husbandry_level"; public const string CalmBrokenBy = "grindstone_calm_broken_by"; public const string CalmBrokenAt = "grindstone_calm_broken_at"; public const string Births = "grindstone_births"; public const string Twin = "grindstone_twin"; public const string StarUp = "grindstone_star_up"; public const string ContentUntil = "grindstone_content_until"; public const string ProduceLast = "grindstone_produce_last"; public const string PrimeStars = "grindstone_prime_stars"; public const string RpcPet = "grindstone_Pet"; public const string RpcFeederTake = "grindstone_FeederTake"; public const string RpcHusbandryCredit = "grindstone_HusbandryCredit"; public const string RpcHerdCallout = "grindstone_HerdCallout"; public const string TamedKinds = "grindstone_tamed"; public const string FeederPrefab = "grindstone_feeder"; public const string DefenseSkillName = "grindstone_defense"; public const string DefenseLevel = "grindstone_defense_level"; public const string BlockedFoes = "grindstone_blocked"; public const string AnglerLevel = "grindstone_angler_level"; public const string BaitStars = "grindstone_bait_stars"; public const string ChumSince = "grindstone_chum_since"; public const string RpcFishCallout = "grindstone_FishCallout"; public const string RpcFishAnnounce = "grindstone_FishAnnounce"; public const string FishLog = "grindstone_fish_log"; public const string FishRecords = "grindstone_fish_records"; public const string SnagsSync = "grindstone_snags"; public const string FarmPlanter = "grindstone_farm_planter"; public const string FarmLevel = "grindstone_farm_level"; public const string FarmSeedStars = "grindstone_farm_seed"; public const string FarmFed = "grindstone_farm_fed"; public const string FarmTended = "grindstone_farm_tended"; public const string FarmStars = "grindstone_farm_stars"; public const string FarmGiant = "grindstone_farm_giant"; public const string FarmFrom = "grindstone_farm_from"; public const string RpcTend = "grindstone_Tend"; public const string RpcFertilize = "grindstone_Fertilize"; public const string HarvestedCrops = "grindstone_harvested"; public const string MillStars = "grindstone_mill_stars"; public const string MillSpawnStars = "grindstone_mill_spawn"; public const string RpcAddMill = "grindstone_AddMill"; public const string CompostPrefab = "grindstone_compost_bin"; public const string CompostPoints = "grindstone_compost"; public const string RpcAddCompost = "grindstone_AddCompost"; } public static class Kitchen { private static readonly HashSet<string> itemNames = new HashSet<string>(); private static readonly Dictionary<string, float> values = new Dictionary<string, float>(); internal static int Count => itemNames.Count; public static bool IsKitchen(CookingStation station) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((Object)(object)station != (Object)null) { return (int)station.m_skill == 105; } return false; } public static bool IsKitchen(CraftingStation station) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((Object)(object)station != (Object)null) { return (int)station.m_craftingSkill == 105; } return false; } public static bool IsKitchenItem(ItemData item) { if (item?.m_shared != null) { return itemNames.Contains(item.m_shared.m_name); } return false; } public static bool IsKitchenName(string sharedName) { if (sharedName != null) { return itemNames.Contains(sharedName); } return false; } public static bool IsKitchenItem(string prefabName) { ItemDrop val = ItemPrefab(prefabName); if ((Object)(object)val != (Object)null) { return IsKitchenItem(val.m_itemData); } return false; } public static float FoodValue(ItemData item) { if (item?.m_shared == null) { return 0f; } if (!values.TryGetValue(item.m_shared.m_name, out var value)) { return Value(item.m_shared); } return value; } public static ItemDrop ItemPrefab(string prefabName) { GameObject val = (((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(prefabName)) ? null : ObjectDB.instance.GetItemPrefab(prefabName)); if (!((Object)(object)val == (Object)null)) { return val.GetComponent<ItemDrop>(); } return null; } internal static float Value(SharedData shared) { return shared.m_food + shared.m_foodStamina + shared.m_foodEitr; } internal static void AddItem(ItemDrop drop) { if (!((Object)(object)drop == (Object)null) && drop.m_itemData?.m_shared != null && !((Object)(object)((Component)drop).GetComponent<Piece>() != (Object)null) && !((Object)(object)((Component)drop).GetComponent<Feast>() != (Object)null)) { SharedData shared = drop.m_itemData.m_shared; if (shared.m_maxStackSize > 1) { itemNames.Add(shared.m_name); shared.m_scaleByQuality = 0f; shared.m_scaleWeightByQuality = 0f; } } } public static void AddStarItem(ItemDrop drop) { AddItem(drop); } internal static void AddConversion(ItemDrop from, ItemDrop to) { if (from?.m_itemData?.m_shared != null && to?.m_itemData?.m_shared != null) { SharedData shared = from.m_itemData.m_shared; if (Value(shared) <= 0f && !values.ContainsKey(shared.m_name)) { values[shared.m_name] = Value(to.m_itemData.m_shared); } } } } public static class KitchenDiscovery { [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class SceneAwake { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { Discover(); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class DatabaseAwake { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { Discover(); } } public static void Discover() { int count = Kitchen.Count; if ((Object)(object)ZNetScene.instance != (Object)null) { foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { AddStation(prefab); } } if ((Object)(object)ObjectDB.instance != (Object)null) { foreach (Recipe recipe in ObjectDB.instance.m_recipes) { AddRecipe(recipe); } } if (Kitchen.Count != count) { GrindstoneSkills.Log.LogInfo((object)$"Kitchen items that carry stars: {Kitchen.Count}."); } } private static void AddStation(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return; } CookingStation component = prefab.GetComponent<CookingStation>(); if (Kitchen.IsKitchen(component)) { foreach (ItemConversion item in component.m_conversion) { AddConversion(item.m_from, item.m_to); } } Fermenter component2 = prefab.GetComponent<Fermenter>(); if (!((Object)(object)component2 != (Object)null)) { return; } foreach (ItemConversion item2 in component2.m_conversion) { AddConversion(item2.m_from, item2.m_to); } } private static void AddConversion(ItemDrop from, ItemDrop to) { Kitchen.AddItem(to); Kitchen.AddConversion(from, to); } private static void AddRecipe(Recipe recipe) { if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && Kitchen.IsKitchen(recipe.m_craftingStation)) { Kitchen.AddItem(recipe.m_item); } } } public static class KitchenFilter { public static int MinStars(ZNetView nview) { if (!KitchenSettings.TrashFilter.Value || (Object)(object)nview == (Object)null || !nview.IsValid()) { return 0; } return Mathf.Clamp(nview.GetZDO().GetInt("grindstone_min_stars", 0), 0, 3); } public static bool Trashes(ZNetView nview, int stars) { return stars < MinStars(nview); } } public static class KitchenSettings { public const string Section = "4 - Kitchen"; public static ConfigEntry<bool> TrashFilter { get; private set; } public static ConfigEntry<float> CookingSpeed { get; private set; } public static ConfigEntry<float> ExtraBurnTime { get; private set; } public static ConfigEntry<float> FermentingSpeed { get; private set; } public static ConfigEntry<float> ExtraFoodChance { get; private set; } public static ConfigEntry<int> ExtraFoodAmount { get; private set; } public static ConfigEntry<float> IngredientSaveChance { get; private set; } public static void Initialize(SyncedConfiguration config) { TrashFilter = config.Bind("4 - Kitchen", "Trash Filter", true, "Lets players set a minimum star count on each kitchen; dishes below it are thrown away. Off: every kitchen keeps everything.", true, null); CookingSpeed = config.Bind("4 - Kitchen", "Cooking Speed At Level 100", 50f, "Percent faster cooking on cooking stations and the oven, from the level of the player who put the food on.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); ExtraBurnTime = config.Bind("4 - Kitchen", "Extra Burn Time At Level 100", 100f, "Extra time before a finished dish burns, in percent of its cook time. The game burns a dish at twice its cook time; 100 moves that to three times.", true, (AcceptableValueBase?)(object)Settings.UpTo(1000f)); FermentingSpeed = config.Bind("4 - Kitchen", "Fermenting Speed At Level 100", 30f, "Percent faster fermenting, from the level of the player who put the mead base in.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); ExtraFoodChance = config.Bind("4 - Kitchen", "Extra Food Chance At Level 100", 50f, "Chance in percent of extra food from a kitchen (cooking stations, oven, cauldron, mead cauldron, prep table). Replaces the game's own bonus chance at kitchens. Grows from 0 at level 0.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); ExtraFoodAmount = config.Bind("4 - Kitchen", "Extra Food Amount", 1, "How many extra dishes a bonus gives.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 10)); IngredientSaveChance = config.Bind("4 - Kitchen", "Ingredient Save Chance At Level 100", 10f, "Chance in percent that crafting at a kitchen gives back one of the ingredients it used.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } } public static class LookoutSettings { public const string Section = "9 - Lookout"; public static ConfigEntry<float> Level { get; private set; } public static ConfigEntry<float> Radius { get; private set; } public static ConfigEntry<float> Duration { get; private set; } public static ConfigEntry<float> Cooldown { get; private set; } public static ConfigEntry<KeyboardShortcut> Key { get; private set; } public static void Initialize(SyncedConfiguration config) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) Level = config.Bind("9 - Lookout", "Lookout Level", 50f, "Sailing level that unlocks the lookout. 0 gives it to everyone; above 100 turns it off.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 101f)); Radius = config.Bind("9 - Lookout", "Lookout Radius", 100f, "How far from the ship the pulse reaches, in metres.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(10f, 300f)); Duration = config.Bind("9 - Lookout", "Lookout Duration", 30f, "Seconds the enemies the pulse reached keep their name tags.", true, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 300f)); Cooldown = config.Bind("9 - Lookout", "Lookout Cooldown", 60f, "Seconds before a player can send the next pulse.", true, (AcceptableValueBase?)(object)Settings.UpTo(3600f)); Key = config.Bind<KeyboardShortcut>("9 - Lookout", "Lookout Key", new KeyboardShortcut((KeyCode)111, Array.Empty<KeyCode>()), "Sends the lookout pulse while you are aboard a ship. Each player's own.", synced: false, null, Array.Empty<object>()); } } public static class MineFindSettings { public const string Section = "18 - Mine Finds"; public static ConfigEntry<float> ChanceAt0 { get; private set; } public static ConfigEntry<float> ChanceAt100 { get; private set; } public static void Initialize(SyncedConfiguration config) { ChanceAt0 = config.Bind("18 - Mine Finds", "Find Chance At 0", 0.2f, "Percent chance that a chunk broken by a level 0 miner hides a find from GrindstoneSkills.MineFinds.yml. Chunks with less than 50 health get a share in proportion (a mud pile's 5-health chunks a tenth). 0 with the next setting at 0 turns finds off.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); ChanceAt100 = config.Bind("18 - Mine Finds", "Find Chance At 100", 1f, "Percent chance of a find for a level 100 miner; levels in between are in proportion.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); MineFinds.Register(config); } } public static class OddsSettings { public const string Section = "3 - Odds"; public static readonly float[] Levels = new float[6] { 0f, 25f, 50f, 75f, 100f, 130f }; private static readonly string[] Defaults = new string[6] { "90, 10, 0, 0", "45, 40, 15, 0", "15, 40, 35, 10", "5, 20, 45, 30", "0, 10, 40, 50", "0, 0, 25, 75" }; public static ConfigEntry<string>[] Rows { get; } = new ConfigEntry<string>[Levels.Length]; public static ConfigEntry<float> IngredientLevelsPerStar { get; private set; } public static void Initialize(SyncedConfiguration config) { for (int i = 0; i < Levels.Length; i++) { Rows[i] = config.Bind("3 - Odds", $"Odds At Level {Levels[i]:0}", Defaults[i], "Chances in percent of 0, 1, 2 and 3 stars at this effective level, separated by commas. They are scaled to add up to 100. A row that cannot be read uses its default.", true, null); } IngredientLevelsPerStar = config.Bind("3 - Odds", "Ingredient Levels Per Star", 10f, "Levels added to the cook's level for each star, on average, of the ingredients that can carry stars.", true, (AcceptableValueBase?)(object)Settings.UpTo(50f)); } public static string DefaultRow(int index) { return Defaults[index]; } } public static class OldGrowthSettings { public const string Section = "12 - Chopping"; public static ConfigEntry<float> Bonus { get; private set; } public static ConfigEntry<float> Start { get; private set; } public static ConfigEntry<float> Full { get; private set; } public static void Initialize(SyncedConfiguration config) { Bonus = config.Bind("12 - Chopping", "Old Growth Bonus", 100f, "Percent more wood from the logs of the biggest trees for a level 100 woodcutter, less for smaller trees and lower levels. It follows the level of whoever breaks the log, or else of whoever felled the tree.", true, (AcceptableValueBase?)(object)Settings.UpTo(500f)); Start = config.Bind("12 - Chopping", "Old Growth Start", 50f, "How big a tree must be before its logs give extra wood, in percent of its own kind's size range: 0 is the smallest size that kind of tree grows to, 100 the largest. Trees this size or smaller give no bonus.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); Full = config.Bind("12 - Chopping", "Old Growth Full", 100f, "Trees this big within their kind, in percent, give the whole bonus, and trees between the start and this size a share of it. If this is not above the start, every tree larger than the start gives the whole bonus.", true, (AcceptableValueBase?)(object)Settings.UpTo(100f)); } } public static class Perks { public static int ExtraFoodAmount => Mathf.Max(1, KitchenSettings.ExtraFoodAmount.Value); public static float CookingSpeed(float level) { return 1f + Share(KitchenSettings.CookingSpeed.Value, level); } public static float ExtraBurnTime(float level) { return Share(Kitche