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 TerrainRewards v1.1.1
TerrainRewards.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TerrainRewards")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("TerrainRewards")] [assembly: AssemblyTitle("TerrainRewards")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 TerrainRewards { internal static class BiomeDetector { private static readonly MethodInfo GetCurrentBiomeMethod = AccessTools.Method(typeof(Player), "GetCurrentBiome", (Type[])null, (Type[])null); internal static string GetBiomeName(Player player) { if ((Object)(object)player == (Object)null || GetCurrentBiomeMethod == null) { TerrainRewardsPlugin.Debug("GetCurrentBiome method was unavailable."); return string.Empty; } try { return GetCurrentBiomeMethod.Invoke(player, null)?.ToString() ?? string.Empty; } catch (Exception ex) { TerrainRewardsPlugin.Log.LogWarning((object)("Unable to determine current biome: " + ex.Message)); return string.Empty; } } } internal static class RewardConfig { private static readonly Dictionary<string, ConfigEntry<string>> BiomeEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private static ConfigEntry<string> _allBiomes; private const string RewardKey = "Rewards v2"; internal static void Bind(ConfigFile config) { _allBiomes = config.Bind<string>("Rewards - All Biomes", "Rewards v2", "Stone:2:25", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "Meadows", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "BlackForest", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "Swamp", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "Mountain", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "Plains", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "Mistlands", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "AshLands", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "DeepNorth", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); BindBiome(config, "Ocean", "", "Unlimited semicolon-separated entries using ItemPrefab:Amount:ChancePercent. Each entry rolls independently. Decimal chances such as 0.5 are supported."); } private static void BindBiome(ConfigFile config, string biome, string defaults, string description) { BiomeEntries[Normalize(biome)] = config.Bind<string>("Rewards - " + biome, "Rewards v2", defaults, description); } internal static IReadOnlyList<RewardDefinition> GetRewardsForBiome(string biome) { List<RewardDefinition> list = new List<RewardDefinition>(); Parse(_allBiomes.Value, list, "All Biomes"); if (BiomeEntries.TryGetValue(Normalize(biome), out var value)) { Parse(value.Value, list, biome); } return list; } private static void Parse(string raw, List<RewardDefinition> destination, string source) { if (string.IsNullOrWhiteSpace(raw)) { return; } string[] array = raw.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string text2 = text.Trim(); string[] array2 = text2.Split(':'); if (array2.Length != 3) { Warn(source, text2, "expected ItemPrefab:Amount:ChancePercent"); continue; } string text3 = array2[0].Trim(); int result; bool flag = int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result); float result2; bool flag2 = float.TryParse(array2[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result2); if (string.IsNullOrWhiteSpace(text3) || !flag || !flag2) { Warn(source, text2, "item, amount, or chance could not be parsed"); } else if (result < 1 || result2 < 0f || result2 > 100f) { Warn(source, text2, "amount must be >= 1 and chance must be 0-100"); } else { destination.Add(new RewardDefinition(text3, result, result2)); } } } private static void Warn(string source, string entry, string reason) { TerrainRewardsPlugin.Log.LogWarning((object)("Invalid reward '" + entry + "' in '" + source + "': " + reason + ".")); } private static string Normalize(string value) { return (value ?? string.Empty).Replace(" ", string.Empty).Replace("_", string.Empty).Replace("-", string.Empty) .Trim(); } } internal sealed class RewardDefinition { public string ItemName { get; } public int Amount { get; } public float ChancePercent { get; } public RewardDefinition(string itemName, int amount, float chancePercent) { ItemName = itemName; Amount = amount; ChancePercent = chancePercent; } } [HarmonyPatch] internal static class TerrainOpAwakePatch { internal static MethodBase ResolveTargetMethod() { return AccessTools.Method(typeof(TerrainOp), "Awake", (Type[])null, (Type[])null); } private static MethodBase TargetMethod() { MethodBase methodBase = ResolveTargetMethod(); if (methodBase == null) { ManualLogSource log = TerrainRewardsPlugin.Log; if (log != null) { log.LogError((object)"Harmony could not resolve TerrainOp.Awake."); } } return methodBase; } private static void Postfix(TerrainOp __instance) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) try { ConfigEntry<bool> enabled = TerrainRewardsPlugin.Enabled; if (enabled == null || !enabled.Value || (Object)(object)__instance == (Object)null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { TerrainRewardsPlugin.Debug("TerrainOp ignored: no local player."); return; } Settings settings = __instance.m_settings; if (settings == null) { TerrainRewardsPlugin.Debug("TerrainOp ignored: settings were null."); return; } Vector3 position = ((Component)__instance).transform.position; float num = Vector3.Distance(((Component)localPlayer).transform.position, position); string[] obj = new string[11] { "TerrainOp.Awake postfix: ", $"position={position}, distance={num:0.00}, ", $"level={settings.m_level}, levelOffset={settings.m_levelOffset:0.###}, ", $"raise={settings.m_raise}, raiseDelta={settings.m_raiseDelta:0.###}, ", $"raisePower={settings.m_raisePower:0.###}, ", $"smooth={settings.m_smooth}, smoothPower={settings.m_smoothPower:0.###}, ", $"paintCleared={settings.m_paintCleared}, ", "spawnOnPlaced=", null, null, null }; GameObject spawnOnPlaced = __instance.m_spawnOnPlaced; obj[8] = ((spawnOnPlaced != null) ? ((Object)spawnOnPlaced).name : null) ?? "null"; obj[9] = ", "; obj[10] = $"spawnAtMaxLevelDepth={__instance.m_spawnAtMaxLevelDepth}"; TerrainRewardsPlugin.Debug(string.Concat(obj)); if (num > TerrainRewardsPlugin.MaximumPlayerDistance.Value) { TerrainRewardsPlugin.Debug($"TerrainOp ignored: local player is {num:0.00}m away."); return; } if (!settings.m_raise) { TerrainRewardsPlugin.Debug("TerrainOp ignored: m_raise is false."); return; } if (settings.m_raiseDelta >= TerrainRewardsPlugin.MaximumDigRaiseDelta.Value) { TerrainRewardsPlugin.Debug($"TerrainOp ignored: raiseDelta {settings.m_raiseDelta:0.###} " + $"is not below {TerrainRewardsPlugin.MaximumDigRaiseDelta.Value:0.###}."); return; } Vector3 val = position + Vector3.up * settings.m_levelOffset; if (Heightmap.AtMaxLevelDepth(val) && !TerrainRewardsPlugin.RewardAtMaximumDepth.Value) { TerrainRewardsPlugin.Debug("TerrainOp ignored: terrain is already at maximum dig depth."); return; } TerrainRewardsPlugin.Debug("TerrainOp accepted as a nearby negative-delta digging operation."); TerrainRewardsPlugin.AwardRewards(localPlayer, position); } catch (Exception arg) { ManualLogSource log = TerrainRewardsPlugin.Log; if (log != null) { log.LogError((object)$"Error while processing TerrainOp rewards: {arg}"); } } } } [BepInPlugin("com.danielcannady.terrainrewards", "Terrain Rewards", "0.4.1")] public sealed class TerrainRewardsPlugin : BaseUnityPlugin { public const string PluginGuid = "com.danielcannady.terrainrewards"; public const string PluginName = "Terrain Rewards"; public const string PluginVersion = "0.4.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DebugLogging; internal static ConfigEntry<bool> ShowMessages; internal static ConfigEntry<float> MaximumPlayerDistance; internal static ConfigEntry<float> MaximumDigRaiseDelta; internal static ConfigEntry<bool> RewardAtMaximumDepth; private Harmony _harmony; private void Awake() { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable or disable Terrain Rewards."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", true, "Write detailed TerrainOp and reward-roll information to LogOutput.log."); ShowMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowMessages", true, "Show a message when rewards drop from terrain."); MaximumPlayerDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "MaximumPlayerDistance", 8f, "Maximum distance in meters between the local player and a terrain operation."); MaximumDigRaiseDelta = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "MaximumDigRaiseDelta", -0.01f, "Only terrain operations with m_raise=true and a raise delta below this value count as digging. Current Valheim pickaxes use a negative raise delta."); RewardAtMaximumDepth = ((BaseUnityPlugin)this).Config.Bind<bool>("Detection", "RewardAtMaximumDepth", false, "Award rewards even when the terrain is already at maximum dig depth."); RewardConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.danielcannady.terrainrewards"); _harmony.PatchAll(); MethodBase methodBase = TerrainOpAwakePatch.ResolveTargetMethod(); if (methodBase == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find TerrainOp.Awake. Terrain rewards will not run."); return; } Patches patchInfo = Harmony.GetPatchInfo(methodBase); bool flag = false; if (patchInfo != null) { foreach (Patch postfix in patchInfo.Postfixes) { if (postfix.owner == "com.danielcannady.terrainrewards") { flag = true; break; } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Terrain Rewards 0.4.1 loaded. Patch target: " + methodBase.DeclaringType?.FullName + "." + methodBase.Name + "; " + $"postfix installed: {flag}")); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static void Debug(string message) { ConfigEntry<bool> debugLogging = DebugLogging; if (debugLogging != null && debugLogging.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[Debug] " + message)); } } } internal static void AwardRewards(Player player, Vector3 dropPosition) { //IL_0134: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = Enabled; if (enabled == null || !enabled.Value || (Object)(object)player == (Object)null) { return; } string biomeName = BiomeDetector.GetBiomeName(player); IReadOnlyList<RewardDefinition> rewardsForBiome = RewardConfig.GetRewardsForBiome(biomeName); Debug($"Biome='{biomeName}', configured rewards={rewardsForBiome.Count}"); foreach (RewardDefinition item in rewardsForBiome) { float num = Random.value * 100f; bool flag = num < item.ChancePercent; Debug($"Roll {item.ItemName}: rolled={num:0.00}, " + $"chance={item.ChancePercent:0.##}, success={flag}"); if (!flag) { continue; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(item.ItemName) : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Reward prefab '" + item.ItemName + "' was not found. Use its internal prefab name.")); } continue; } int num2 = SpawnRewardDrops(val, item.Amount, dropPosition); Debug($"SpawnDrop {item.ItemName} x{item.Amount}: " + $"spawned={num2}"); if (num2 > 0 && ShowMessages.Value) { ((Character)player).Message((MessageType)1, $"Dropped {num2} {item.ItemName}", 0, (Sprite)null); } } } private static int SpawnRewardDrops(GameObject prefab, int amount, Vector3 dropPosition) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Reward prefab '" + ((Object)prefab).name + "' has no ItemDrop component.")); } return 0; } int num = 1; if (component.m_itemData?.m_shared != null) { num = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize); } int num2 = amount; int num3 = 0; int num4 = 0; while (num2 > 0) { int num5 = Mathf.Min(num2, num); Vector2 val = Random.insideUnitCircle * 0.35f; Vector3 val2 = dropPosition + new Vector3(val.x, 0.35f + (float)num4 * 0.05f, val.y); GameObject val3 = Object.Instantiate<GameObject>(prefab, val2, Quaternion.identity); ItemDrop component2 = val3.GetComponent<ItemDrop>(); if ((Object)(object)component2 == (Object)null) { Object.Destroy((Object)(object)val3); ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Spawned reward '" + ((Object)prefab).name + "' had no ItemDrop component.")); } break; } component2.m_itemData.m_stack = num5; num3 += num5; num2 -= num5; num4++; } return num3; } } }