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.21.0
plugins\GrindstoneSkills.dll
Decompiled a week 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 Hotkeys; using Microsoft.CodeAnalysis; using PatchGuard; using PlateColumn; using RecipeCounts; using StatusTemplates; using SyncedConfig; using TMPro; using Translations; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; using Valheim.SettingsGui; using YamlConfig; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; 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.21.0.0")] [assembly: AssemblyInformationalVersion("0.21.0+e5b23530f4409360754bfb9dedae4184bbf9987a")] [assembly: AssemblyProduct("GrindstoneSkills")] [assembly: AssemblyTitle("GrindstoneSkills")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.21.0.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<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, what kitchens make, and the crops and seeds kitchens use (and their flour), separated by commas.", 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)); } } 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 class Crops { [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class SceneAwake { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { foreach (Plant plant in PrefabIndex.Scene().Plants) { Add(plant); } } } 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 ConfigEntry<bool> CatchUp { 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); CatchUp = config.Bind("6 - Death", "Catch Up", true, "A skill below the best level it ever reached (after dying, for one) trains twice as fast until it is back there. The best levels are kept with the character. Off, every skill trains at the game's pace.", 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.", 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<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); 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.", 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 static class DisplaySettings { public const string Section = "7 - Display"; public static ConfigEntry<bool> StarsOnIcons { get; private set; } public static void Initialize(SyncedConfiguration config) { StarsOnIcons = config.Bind("7 - Display", "Stars On Icons", true, "Shows an egg's stars (the level of the hen that laid it) on its icon in inventories, containers and the hotbar.", 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; } } [HarmonyPatch(typeof(EnvMan), "SetEnv")] public static class EnvFogPass { [HarmonyPostfix] private static void Postfix() { if ((Object)(object)Utils.GetMainCamera() != (Object)null) { Guard.Run("fog", delegate { Apply(); }); } } private static void Apply() { FogLiftView.Apply(); UnderwaterView.Apply(); } } 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 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, 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); } 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<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) { BindGameplay(config); BindPlayer(config); FarmingPerkSettings.Initialize(config); FarmingExperienceSettings.Initialize(config); CompostSettings.Initialize(config); } private static void BindGameplay(SyncedConfiguration config) { Enabled = config.Bind("29 - Farming", "Farming Enabled", true, "Turns every Farming feature on or off: perks, giant crops, compost, experience changes. Off, crops behave exactly as in the game. Levels are kept.", true, null); 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).", 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> 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)); } 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<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)); } 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 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)); } 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_005f: 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)) { string text2 = (LangWire.IsMessage(text) ? LangWire.Read(text) : text); DamageText.instance.AddInworldText((TextType)7, position, num, text2, false); } } } } public static class ForagePerkSettings { public const string Section = "34 - Forage Perks"; public static ConfigEntry<float> ExtraYieldAt100 { 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)); 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<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: experience, extra yield, sweep picking. 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); } 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 GameSkillSettings { public static ConfigEntry<bool> Running { get; private set; } public static ConfigEntry<bool> Jumping { get; private set; } public static ConfigEntry<bool> Sneaking { get; private set; } public static ConfigEntry<bool> Swimming { get; private set; } public static ConfigEntry<bool> Dodging { get; private set; } public static ConfigEntry<bool> Riding { get; private set; } public static ConfigEntry<bool> Crafting { get; private set; } public static ConfigEntry<bool> WeaponMastery { get; private set; } public static void Initialize(SyncedConfiguration config) { BindMovement(config); BindCombat(config); Crafting = config.Bind("42 - Crafting", "Crafting Enabled", true, "Maker's Mark (weapons, armour, shields and tools you craft or upgrade get up to 25% more durability at level 100, kept on the item), Sturdy Build (pieces you place get up to 25% more health, kept on the piece), Thrifty (up to a 10% chance that a craft at the workbench, forge or another Crafting station gives one material back) and Journeyman at 75 (the workbench and forge count one level higher for you). Off, crafting and building are exactly the game's; items and pieces made earlier keep theirs.", true, null); WeaponMastery = config.Bind("43 - Weapon Mastery", "Weapon Mastery Enabled", true, "Weapon Switch (a weapon skill below your best weapon skill trains 50% faster) and one milestone per weapon skill at level 50: Swords Edge (+25% damage to a staggered foe), Knives Bleed (hits add poison worth 20% of the hit), Clubs Crush (+30% stagger), Polearms Sweep (+30% knockback), Spears Long Throw (thrown spears +25% damage), Axes Rend (each hit on the same foe within 3 s adds 5%, up to 25%), Unarmed Brawler (+50% damage), Bows Recover (25% chance a shot uses no arrow), Crossbows Steady Reload (sprinting, jumping and dodging pause a reload instead of restarting it), Elemental Magic Attunement (+25% eitr regeneration holding a staff) and Blood Magic Life Tap (spells cost 25% less health). Blood magic shields also train their caster when they absorb damage.", true, null); } private static void BindMovement(SyncedConfiguration config) { Running = config.Bind("36 - Running", "Running Enabled", true, "Pack Mule (up to +50 carry weight at level 100), Burden at 50 (half the stamina drain while over-encumbered) and Second Wind at 75 (running out of stamina while running gives 30 back, once every 3 minutes). Off, running is exactly the game's.", true, null); Jumping = config.Bind("37 - Jumping", "Jumping Enabled", true, "Light Feet (jumps cost up to 50% less stamina at level 100), Soft Landing (falls are safe up to 4 m higher, and fall damage is up to 50% less) and Roll at 50 (landing from a harmful fall while moving forward rolls: no damage up to twice the safe height, half above it). Off, jumping and falling are exactly the game's.", true, null); Sneaking = config.Bind("38 - Sneaking", "Sneaking Enabled", true, "Prowl (crouched walking up to 50% faster at level 100), Soft Steps (up to 50% less noise while crouched), Assassin (sneak attacks up to 50% stronger, and 5 Sneak experience each) and Vanish at 75 (crouch for 3 s with no enemy within 8 m and creatures hunting you within 40 m lose track of you, once a minute). Off, sneaking is exactly the game's.", true, null); Swimming = config.Bind("39 - Swimming", "Swimming Enabled", true, "Quick Dry (Wet ends up to 50% sooner at level 100), Long Breath (out of stamina in water, drowning starts up to 3 s later and hurts up to 50% less), Salvage (floating items are picked up from up to twice as far while swimming), Diving (hold Crouch to dive and Jump to swim up; under water you swim where you look; breath lasts 20 s, up to 60 s at level 100, then you drown) and Water Builder at 50 (the hammer, hoe and cultivator stay in hand while swimming, so you can build from the water and on the bottom). Swim speed and swim stamina stay the game's. Off, swimming is exactly the game's.", true, null); Riding = config.Bind("41 - Riding", "Riding Enabled", true, "Gallop (a mount you ride runs up to 15% faster at level 100, on top of the game's 25%) and Steady Seat (a mount takes up to 25% less damage while you ride it). Off, riding is exactly the game's.", true, null); } private static void BindCombat(SyncedConfiguration config) { Dodging = config.Bind("40 - Dodging", "Dodging Enabled", true, "Long Roll (a roll's invulnerability lasts up to 30% longer at level 100), Counter at 50 (dodging through a hit makes the next melee attack you start within 2 s deal 25% more) and Heavy Roll at 75 (you can dodge while over-encumbered, for twice the stamina). Off, dodging is exactly the game's.", true, null); } } 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 void Run<TArg>(string context, Action<TArg> action, TArg arg) { try { action(arg); } catch (Exception exception) { Guard.Report(exception, context); } } public static T Run<TArg, T>(string context, Func<TArg, T> func, TArg arg, T fallback) { try { return func(arg); } 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<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)); } 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 SlotLevel = "grindstone_level"; public const string BaseLevel = "grindstone_base_level"; public const string RpcAddItem = "grindstone_AddItem"; public const string RpcAddBase = "grindstone_AddBase"; 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 RpcWindCall = "grindstone_WindCall"; public const string WindCallDirection = "grindstone_wind_dir"; public const string WindCallUntil = "grindstone_wind_until"; public const string RpcFogLift = "grindstone_FogLift"; public const string FogLiftUntil = "grindstone_foglift_until"; 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 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 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 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 FarmFed = "grindstone_farm_fed"; public const string FarmTended = "grindstone_farm_tended"; 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 CompostPrefab = "grindstone_compost_bin"; public const string CompostPoints = "grindstone_compost"; public const string MakerLevel = "grindstone_maker"; public const string BuilderLevel = "grindstone_builder"; public const string BestLevels = "grindstone_best_levels"; public const string RpcVanish = "grindstone_Vanish"; public const string RpcShieldXp = "grindstone_ShieldXp"; } 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 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); } } } 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 (PrefabIndex.Converter converter in PrefabIndex.Scene().Converters) { AddStation(converter); } } 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 products: {Kitchen.Count}."); } } private static void AddStation(PrefabIndex.Converter converter) { CookingStation station = converter.Station; if (Kitchen.IsKitchen(station)) { foreach (ItemConversion item in station.m_conversion) { AddConversion(item.m_from, item.m_to); } } Fermenter fermenter = converter.Fermenter; if (!((Object)(object)fermenter != (Object)null)) { return; } foreach (ItemConversion item2 in fermenter.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 KitchenSettings { public const string Section = "4 - Kitchen"; 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) { 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 LazyRpcs { [HarmonyPatch(typeof(ZNetView), "HandleRoutedRPC")] private static class Arrival { [HarmonyPrefix] private static void Prefix(ZNetView __instance, RoutedRPCData rpcData) { int methodHash = rpcData.m_methodHash; if (Registrars.TryGetValue(methodHash, out var value) && !__instance.m_functions.ContainsKey(methodHash)) { HookGuard.Run("object rpc registration", value, __instance); } } } private static readonly Dictionary<int, Action<ZNetView>> Registrars = new Dictionary<int, Action<ZNetView>> { { StringExtensionMethods.GetStableHashCode("grindstone_Tend"), PlantRpcs.RegisterOn }, { StringExtensionMethods.GetStableHashCode("grindstone_Fertilize"), PlantRpcs.RegisterOn }, { StringExtensionMethods.GetStableHashCode("grindstone_CleanStrike"), CleanStrikeMarks.RegisterOn }, { StringExtensionMethods.GetStableHashCode("grindstone_Pet"), Petting.RegisterOn }, { StringExtensionMethods.GetStableHashCode("grindstone_Vanish"), VanishRpc.RegisterOn } }; public static bool Has(ZNetView view, string rpc) { return view.m_functions.ContainsKey(StringExtensionMethods.GetStableHashCode(rpc)); } } [HarmonyPatch(typeof(Player), "Update")] public static class LocalPlayerTick { [HarmonyPostfix] private static void Postfix(Player __instance) { if (__instance == Player.m_localPlayer) { float deltaTime = Time.deltaTime; SailingXp.Tick(__instance, deltaTime); HerdWatch.Tick(__instance, deltaTime); CustomSkillLevels.Tick(__instance, deltaTime); WindCallInput.Tick(__instance); LookoutInput.Tick(__instance); FogLiftInput.Tick(__instance); Breath.Tick(__instance, deltaTime); Recovery.Tick(__instance, deltaTime); DefenseEffects.Tick(deltaTime); Vanish.Tick(__instance, deltaTime); } } } public static class LocalSkill { public static bool IsLocal(Character character) { if ((Object)(object)character != (Object)null) { return (object)character == Player.m_localPlayer; } return false; } public static float Level(SkillType skill) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return 0f; } return ((Character)localPlayer).GetSkillLevel(skill); } public static float Factor(SkillType skill) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Mathf.Clamp01(Level(skill) / 100f); } public static bool Reached(SkillType skill, float level) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Level(skill) >= level; } public static float Scaled(SkillType skill, float atHundred) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return atHundred * Factor(skill); } } 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 NearbyRpc { private const float Margin = 20f; public static void Send(Vector3 position, float range, string method, params object[] parameters) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { return; } long uID = ZNet.GetUID(); instance.InvokeRoutedRPC(uID, method, parameters); if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null) { return; } float reach = (range + 20f) * (range + 20f); foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { long num = PeerNear(player.m_characterID, position, reach); if (num != 0L && num != uID) { instance.InvokeRoutedRPC(num, method, parameters); } } } private static long PeerNear(ZDOID character, Vector3 position, float reach) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result ty