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 BerrySlot v0.1.2
BepInEx/plugins/BerrySlot/BerrySlot.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; 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 TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("BerrySlot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2")] [assembly: AssemblyProduct("BerrySlot")] [assembly: AssemblyTitle("BerrySlot")] [assembly: AssemblyVersion("0.1.2.0")] [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 BerrySlot { public sealed class BerryHud : MonoBehaviour { private Image? _icon; private TMP_Text? _timer; private readonly List<GameObject> _roots = new List<GameObject>(); private bool _attempted; private static readonly ManualLogSource Log = Logger.CreateLogSource("Berry Slot HUD"); internal void Render(Hud hud, Food? berry) { //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) if (!_attempted) { _attempted = true; try { Build(hud); } catch (Exception ex) { foreach (GameObject root in _roots) { Object.Destroy((Object)(object)root); } _roots.Clear(); _icon = null; _timer = null; Log.LogError((object)("Could not clone the native food HUD. " + ex)); } } if ((Object)(object)_icon == (Object)null || (Object)(object)_timer == (Object)null) { return; } foreach (GameObject root2 in _roots) { root2.SetActive(berry != null); } if (berry != null) { ((Component)_icon).gameObject.SetActive(true); ((Component)_timer).gameObject.SetActive(true); _icon.sprite = berry.m_item.GetIcon(); ((Graphic)_icon).color = (Color)(berry.CanEatAgain() ? new Color(1f, 1f, 1f, 0.7f + Mathf.Sin(Time.time * 5f) * 0.3f) : Color.white); float num = berry.m_time / Game.m_foodRate; _timer.text = ((num >= 60f) ? (Mathf.CeilToInt(num / 60f) + "m") : (Mathf.FloorToInt(num) + "s")); ((Graphic)_timer).color = (Color)((num >= 60f) ? Color.white : new Color(1f, 1f, 1f, 0.4f + Mathf.Sin(Time.time * 10f) * 0.6f)); } } private void Build(Hud hud) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) if (hud.m_foodIcons.Length != 3 || hud.m_foodTime.Length != 3 || hud.m_foodBars.Length != 3) { throw new InvalidOperationException("Expected a three-slot vanilla food HUD. Disable extra-slot HUD mods."); } int num = 0; for (int i = 1; i < 3; i++) { if (((Component)hud.m_foodIcons[i]).transform.position.y > ((Component)hud.m_foodIcons[num]).transform.position.y) { num = i; } } Image val = hud.m_foodIcons[num]; TMP_Text val2 = hud.m_foodTime[num]; Transform val3 = ((Component)val).transform; while ((Object)(object)val3 != (Object)null && !val2.transform.IsChildOf(val3)) { val3 = val3.parent; } if ((Object)(object)val3 == (Object)null) { throw new InvalidOperationException("Food icon and timer have no common parent."); } bool flag = false; for (int j = 0; j < 3; j++) { if (j != num && ((Component)hud.m_foodIcons[j]).transform.IsChildOf(val3)) { flag = true; } } if (flag) { throw new InvalidOperationException("The food HUD was replaced by another mod; no isolated native slot template exists."); } float num2 = float.NegativeInfinity; for (int k = 0; k < 3; k++) { if (k != num) { num2 = Mathf.Max(num2, ((Component)hud.m_foodIcons[k]).transform.position.y); } } float num3 = ((Component)val).transform.position.y - num2; if (num3 <= 0f) { throw new InvalidOperationException("Food slots must have a vertical layout."); } Vector3 val4 = ((Component)val).transform.parent.TransformVector(new Vector3(Plugin.OffsetX.Value, Plugin.OffsetY.Value, 0f)); Vector3 shift = new Vector3(0f, num3, 0f) + val4; GameObject val5 = Clone(((Component)val3).gameObject, shift); _icon = Counterpart<Image>(((Component)val).transform, val3, val5.transform); _timer = Counterpart<TMP_Text>(val2.transform, val3, val5.transform); if ((Object)(object)_icon == (Object)null || (Object)(object)_timer == (Object)null) { throw new InvalidOperationException("Native food components were not cloned."); } foreach (GameObject root in _roots) { Graphic[] componentsInChildren = root.GetComponentsInChildren<Graphic>(true); for (int l = 0; l < componentsInChildren.Length; l++) { componentsInChildren[l].raycastTarget = false; } root.SetActive(false); } Log.LogInfo((object)"Added a berry icon and timer one native row above the food stack."); } private GameObject Clone(GameObject source, Vector3 shift) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(source, source.transform.parent, false); _roots.Add(val); ((Object)val).name = "BerrySlot_" + ((Object)source).name; val.transform.position = source.transform.position + shift; return val; } private static T? Counterpart<T>(Transform source, Transform sourceRoot, Transform cloneRoot) where T : Component { Stack<int> stack = new Stack<int>(); Transform val = source; while ((Object)(object)val != (Object)(object)sourceRoot) { stack.Push(val.GetSiblingIndex()); val = val.parent; } Transform val2 = cloneRoot; while (stack.Count > 0) { val2 = val2.GetChild(stack.Pop()); } return ((Component)val2).GetComponent<T>(); } private void OnDestroy() { foreach (GameObject root in _roots) { if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } } } } internal static class FoodRouting { internal sealed class Scope { internal readonly List<Food> Foods; internal readonly List<Food> Before; private readonly List<Food> _hidden; private readonly bool _bonusDestination; private readonly bool _readOnly; private bool _finished; internal Scope(List<Food> foods, ItemData? item, bool readOnly) { Foods = foods; Before = new List<Food>(foods); _readOnly = readOnly; Food berry = Reserved(Before); _bonusDestination = item != null && BonusDestination(Before, item); List<Food> collection = (_bonusDestination ? Before.Where((Food f) => f == berry && f.m_item.m_shared.m_name == item.m_shared.m_name).ToList() : Before.Where((Food f) => f != berry).ToList()); _hidden = (_bonusDestination ? Before.Where((Food f) => f != berry).ToList() : Before.Where((Food f) => f == berry).ToList()); Foods.Clear(); Foods.AddRange(collection); } internal void Finish(bool success) { if (!_finished) { _finished = true; Food[] collection = Foods.ToArray(); Foods.Clear(); if (_readOnly || !success) { Foods.AddRange(Before); } else if (_bonusDestination) { Foods.AddRange(_hidden); Foods.AddRange(collection); } else { Foods.AddRange(collection); Foods.AddRange(_hidden); } } } } internal static bool IsBonusFood(string? prefab) { switch (prefab) { default: return prefab == "MushroomSmokePuff"; case "Raspberry": case "Blueberries": case "Cloudberry": case "Vineberry": case "Lingonberry": case "Mushroom": case "MushroomYellow": case "MushroomBlue": case "MushroomJotunPuffs": case "MushroomMagecap": return true; } } internal static bool IsBonusFood(ItemData item) { if ((Object)(object)item.m_dropPrefab != (Object)null) { return IsBonusFood(((Object)item.m_dropPrefab).name); } return false; } internal static Food? Reserved(IReadOnlyList<Food> foods) { return ((IEnumerable<Food>)foods).LastOrDefault((Func<Food, bool>)((Food f) => IsBonusFood(f.m_name))); } internal static Food? OrdinaryMatch(IReadOnlyList<Food> foods, ItemData item) { Food reserved = Reserved(foods); return ((IEnumerable<Food>)foods).FirstOrDefault((Func<Food, bool>)((Food f) => f != reserved && f.m_item.m_shared.m_name == item.m_shared.m_name)); } internal static bool BonusDestination(IReadOnlyList<Food> foods, ItemData item) { if (!IsBonusFood(item) || OrdinaryMatch(foods, item) != null) { return false; } Food val = Reserved(foods); if (val != null && !(val.m_item.m_shared.m_name == item.m_shared.m_name)) { return val.CanEatAgain(); } return true; } internal static bool CanUseBonus(IReadOnlyList<Food> foods) { Food val = Reserved(foods); if (val != null) { return val.CanEatAgain(); } return true; } } [BepInPlugin("vegasx.valheim.berryslot", "Berry & Mushroom Slot", "0.1.2")] [BepInProcess("valheim.exe")] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Player), "CanEat", new Type[] { typeof(ItemData), typeof(bool) })] private static class CanEatPatch { private static bool Prefix(Player __instance, ItemData item, bool showMessages, ref bool __result, out FoodRouting.Scope? __state) { __state = null; if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || Eating.ContainsKey(__instance)) { return true; } List<Food> foods = __instance.GetFoods(); if (FoodRouting.BonusDestination(foods, item)) { __result = FoodRouting.CanUseBonus(foods); if (!__result && showMessages) { ((Character)__instance).Message((MessageType)2, "Your forage slot is full. Replace it after half its time has passed.", 0, (Sprite)null, false); } return false; } __state = new FoodRouting.Scope(foods, item, readOnly: true); return true; } private static void Finalizer(FoodRouting.Scope? __state) { __state?.Finish(success: false); } } [HarmonyPatch(typeof(Player), "EatFood", new Type[] { typeof(ItemData) })] private static class EatFoodPatch { private static bool Prefix(Player __instance, ItemData item, ref bool __result, out FoodRouting.Scope? __state) { __state = null; if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (Eating.ContainsKey(__instance)) { __result = false; return false; } List<Food> foods = __instance.GetFoods(); if (FoodRouting.BonusDestination(foods, item) && !FoodRouting.CanUseBonus(foods)) { __result = false; return false; } __state = new FoodRouting.Scope(foods, item, readOnly: false); Eating.Add(__instance, __state); return true; } private static void Finalizer(Player __instance, bool __result, Exception? __exception, FoodRouting.Scope? __state) { if (__state != null) { __state.Finish(__result && __exception == null); Eating.Remove(__instance); } } } [HarmonyPatch(typeof(Player), "UpdateFood", new Type[] { typeof(float), typeof(bool) })] private static class UpdateFoodPatch { [HarmonyPriority(800)] private static void Prefix(Player __instance) { if (Eating.TryGetValue(__instance, out FoodRouting.Scope value)) { value.Finish(success: true); } } } [HarmonyPatch(typeof(Hud), "UpdateFood", new Type[] { typeof(Player) })] private static class HudFoodPatch { [HarmonyPriority(0)] private static void Prefix(Hud __instance, Player player, out FoodRouting.Scope? __state) { __state = null; if (!((Object)(object)player != (Object)(object)Player.m_localPlayer) && !Eating.ContainsKey(player)) { (((Component)__instance).GetComponent<BerryHud>() ?? ((Component)__instance).gameObject.AddComponent<BerryHud>()).Render(__instance, FoodRouting.Reserved(player.GetFoods())); __state = new FoodRouting.Scope(player.GetFoods(), null, readOnly: true); } } [HarmonyPriority(800)] private static void Postfix(FoodRouting.Scope? __state) { __state?.Finish(success: false); } private static void Finalizer(FoodRouting.Scope? __state) { __state?.Finish(success: false); } } public const string Id = "vegasx.valheim.berryslot"; private Harmony? _harmony; internal static ConfigEntry<float> OffsetX = null; internal static ConfigEntry<float> OffsetY = null; internal static readonly Dictionary<Player, FoodRouting.Scope> Eating = new Dictionary<Player, FoodRouting.Scope>(); private void Awake() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "HorizontalOffset", 0f, "Move only the berry slot horizontally, in scaled HUD units. Restart to apply."); OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "VerticalOffset", 0f, "Move only the berry slot vertically. Positive moves up. Restart to apply."); _harmony = new Harmony("vegasx.valheim.berryslot"); try { _harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Berry Slot 0.1.2 loaded: all-biome raw forage, priority with ordinary-slot fallback."); } catch (Exception ex) { _harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)("Berry Slot could not patch this game version; disabled. " + ex)); } } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } }