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 VikingMeals v1.1.2
BepInEx/plugins/VikingMeals/VikingMeals.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("VikingMeals")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("VikingMeals")] [assembly: AssemblyTitle("VikingMeals")] [assembly: AssemblyVersion("1.1.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 VikingMeals { public enum Preset { Light, Balanced, Generous } public enum IngredientKind { Staple, Protein, PreparedMeal, Seasoning } public sealed class MealRules { public readonly Dictionary<string, int> Overrides = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); public MealRules(string text) { if (text == null || text.Length > 16000) { throw new ArgumentException("Overrides exceed 16000 characters."); } string[] array = text.Split(';'); foreach (string text2 in array) { if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array2 = text2.Trim().Split('='); if (array2.Length != 2 || string.IsNullOrWhiteSpace(array2[0]) || !int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 1 || result > 100) { throw new ArgumentException("Use ItemPrefab=1..100 or ItemPrefab:IngredientPrefab=1..100, separated by semicolons."); } string text3 = array2[0].Trim(); string text4 = text3; foreach (char c in text4) { if (!char.IsLetterOrDigit(c) && c != '_' && c != ':') { throw new ArgumentException("Invalid prefab key: " + text3); } } if (text3.Split(':').Length > 2 || text3.StartsWith(":") || text3.EndsWith(":")) { throw new ArgumentException("Invalid ingredient override: " + text3); } if (Overrides.ContainsKey(text3)) { throw new ArgumentException("Duplicate override: " + text3); } Overrides.Add(text3, result); } } public int Yield(string item, int original, int maxStack, Preset preset) { string text = item.ToLowerInvariant(); bool flag = text.Contains("soup") || text.Contains("stew") || text.Contains("broth") || text.Contains("porridge"); double num = ((preset != Preset.Light) ? ((double)((preset != Preset.Balanced) ? (flag ? 4 : 3) : (flag ? 3 : 2))) : (flag ? 2.0 : 1.5)); int value; int val = (Overrides.TryGetValue(item, out value) ? value : ((int)Math.Ceiling((double)original * num))); return Math.Max(1, Math.Min(Math.Max(original, maxStack), val)); } public static IngredientKind Classify(string ingredient, bool preparedMeal) { string text = ingredient.ToLowerInvariant(); if (text.Contains("meat") || text.Contains("fish") || text.Contains("necktail") || text.Contains("entrails") || text.Contains("bloodbag") || text.Contains("blubber") || text.Contains("egg")) { return IngredientKind.Protein; } switch (text) { default: if (!text.StartsWith("spice", StringComparison.Ordinal)) { if (!preparedMeal) { return IngredientKind.Staple; } return IngredientKind.PreparedMeal; } goto case "thistle"; case "thistle": case "dandelion": case "honey": return IngredientKind.Seasoning; } } public int Cost(string item, string ingredient, int original, Preset preset, IngredientKind kind) { if (Overrides.TryGetValue(item + ":" + ingredient, out var value)) { return value; } if (original <= 0) { return original; } return Math.Min(original, kind switch { IngredientKind.Protein => preset switch { Preset.Balanced => 3, Preset.Light => 4, _ => 2, }, IngredientKind.PreparedMeal => preset switch { Preset.Balanced => 2, Preset.Light => 3, _ => 1, }, IngredientKind.Seasoning => preset switch { Preset.Balanced => 2, Preset.Light => 3, _ => 1, }, _ => preset switch { Preset.Balanced => 4, Preset.Light => 6, _ => 3, }, }); } } [BepInPlugin("com.justqol.vikingmeals", "Viking Meals", "1.1.0")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { private sealed class Original { public int Amount; public int[] Costs; } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class DatabaseCopied { private static void Postfix() { if (Object.op_Implicit((Object)(object)Instance)) { Instance.dirty = true; } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class WorldReady { private static void Postfix() { if (Object.op_Implicit((Object)(object)Instance)) { Instance.dirty = true; } } } private GameObject uiRoot; private GameObject panel; private GameObject menuButton; private Text status; private Button[] choices; private bool open; private bool blocked; private float nextUi; private string uiScene; public const string Guid = "com.justqol.vikingmeals"; private ConfigEntry<Preset> preset; private ConfigEntry<bool> mealsEnabled; private ConfigEntry<string> overrides; private ConfigEntry<KeyboardShortcut> shortcut; private readonly Dictionary<Recipe, Original> originals = new Dictionary<Recipe, Original>(); private readonly Dictionary<Piece, int[]> feastOriginals = new Dictionary<Piece, int[]>(); private bool dirty = true; private float nextScan; private string error = ""; private string report = "Recipes load when you enter a world."; private Harmony harmony; private static Plugin Instance; private void TickUi() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: 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) //IL_01fb: 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_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) if (GUIManager.IsHeadless() || Time.unscaledTime < nextUi) { return; } try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (Object.op_Implicit((Object)(object)uiRoot) && name != uiScene) { DisposeUi(); } if (Object.op_Implicit((Object)(object)GUIManager.Instance.AveriaSerif)) { if (!Object.op_Implicit((Object)(object)uiRoot)) { BuildUi(); uiScene = name; } bool flag = Object.op_Implicit((Object)(object)FejdStartup.instance) && !Object.op_Implicit((Object)(object)Player.m_localPlayer); menuButton.SetActive(flag && !open); KeyboardShortcut value = shortcut.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus())) { open = !open; } if (open && Input.GetKeyDown((KeyCode)27)) { open = false; } panel.SetActive(open); bool flag2 = open && !flag; if (blocked != flag2) { GUIManager.BlockInput(flag2); blocked = flag2; } bool flag3 = !Object.op_Implicit((Object)(object)ZNet.instance) || (flag && !Object.op_Implicit((Object)(object)Game.instance)); Button[] array = choices; for (int i = 0; i < array.Length; i++) { ((Selectable)array[i]).interactable = flag3; } status.text = report + "\n" + (flag3 ? "Choose your next world's preset below." : "World settings are locked here. Edit server config, restart and reconnect.") + ((error == "") ? "" : ("\n" + error)); Rect rect = ((RectTransform)uiRoot.transform).rect; Vector2 size = ((Rect)(ref rect)).size; if (size.x > 32f && size.y > 32f) { panel.transform.localScale = Vector3.one * Mathf.Min(1f, Mathf.Min((size.x - 32f) / 760f, (size.y - 32f) / 550f)); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Viking Meals interface will retry: " + ex.Message)); DisposeUi(); nextUi = Time.unscaledTime + 5f; } } private void BuildUi() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Expected O, but got Unknown uiRoot = new GameObject("VikingMeals UI", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Canvas component = uiRoot.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.sortingOrder = 30900; CanvasScaler component2 = uiRoot.GetComponent<CanvasScaler>(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.matchWidthOrHeight = 0.5f; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); panel = GUIManager.Instance.CreateWoodpanel(uiRoot.transform, val, val, Vector2.zero, 760f, 550f, false); ((RectTransform)panel.transform).pivot = val; Label("Viking Meals", 30f, -24f, 700f, 48f, 32); Label("Sensible servings for hungry Vikings", 30f, -80f, 700f, 36f, 23); Label("Smaller ingredient bills. Enough food to share.\n\nLight Meals x1.5 · soups and stews x2\nBalanced Meals x2 · soups and stews x3\nGenerous Meals x3 · soups and stews x4\n\nFood stats, unlocks and station levels stay the same.\nHigh ingredient costs are reduced; feasts are included.\nSimple spit cooking and fish-quality yields stay native.", 30f, -130f, 700f, 210f, 20); status = Label("", 30f, -345f, 700f, 76f, 17); choices = (Button[])(object)new Button[3]; for (int i = 0; i < 3; i++) { Preset selected = (Preset)i; choices[i] = Button(panel, selected.ToString(), 30 + i * 235, -430f, 220f, 40f, (UnityAction)delegate { preset.Value = selected; ((BaseUnityPlugin)this).Config.Save(); dirty = true; report = selected.ToString() + " selected · enter a world to apply"; }); } Button(panel, "Close", 550f, -490f, 180f, 38f, (UnityAction)delegate { open = false; }); Label("JustQOL · F7 to open · Escape to close", 30f, -497f, 500f, 30f, 17); menuButton = ((Component)Button(uiRoot, "Viking Meals", 24f, -76f, 200f, 42f, (UnityAction)delegate { open = true; })).gameObject; panel.SetActive(false); } private Text Label(string value, float x, float y, float width, float height, int size) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 1f); GameObject obj = GUIManager.Instance.CreateText(value, panel.transform, val, val, new Vector2(x, y), GUIManager.Instance.AveriaSerif, size, new Color(0.94f, 0.88f, 0.73f), false, Color.black, width, height, false); ((RectTransform)obj.transform).pivot = val; Text component = obj.GetComponent<Text>(); component.alignment = (TextAnchor)0; component.supportRichText = false; ((Graphic)component).raycastTarget = false; return component; } private Button Button(GameObject parent, string value, float x, float y, float width, float height, UnityAction action) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 1f); GameObject obj = GUIManager.Instance.CreateButton(value, parent.transform, val, val, new Vector2(x, y), width, height); ((RectTransform)obj.transform).pivot = val; Button component = obj.GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener(action); return component; } private void DisposeUi() { if (blocked) { GUIManager.BlockInput(false); blocked = false; } if (Object.op_Implicit((Object)(object)uiRoot)) { Object.Destroy((Object)(object)uiRoot); } uiRoot = null; open = false; } private void Awake() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; mealsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Server", "Enabled", true, new ConfigDescription("Enable recipe adjustments. Server controlled; restart server and reconnect clients after edits.", (AcceptableValueBase)null, new object[1] { val })); preset = ((BaseUnityPlugin)this).Config.Bind<Preset>("Server", "Preset", Preset.Balanced, new ConfigDescription("Light: yield x1.5/pots x2, ingredient caps 6 staples/4 protein/3 prepared food or seasoning. Balanced: x2/pots x3; caps 4/3/2. Generous: x3/pots x4; caps 3/2/1. Existing lower costs stay lower.", (AcceptableValueBase)null, new object[1] { val })); overrides = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "RecipeOverrides", "", new ConfigDescription("Absolute quantities: CarrotSoup=6;CarrotSoup:Carrot=2. Semicolon separated, 1..100. Use prefab names. Restart server and reconnect after edits.", (AcceptableValueBase)null, new object[1] { val })); shortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Interface", "OpenMenu", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Open Viking Meals settings and help."); ((BaseUnityPlugin)this).Config.SettingChanged += Changed; ItemManager.OnItemsRegistered += Registered; ItemManager.OnItemsRegisteredFejd += Registered; SynchronizationManager.OnConfigurationSynchronized += Synced; harmony = new Harmony("com.justqol.vikingmeals"); harmony.PatchAll(typeof(Plugin).Assembly); Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Viking Meals 1.1.0 loaded; game " + Version.GetVersionString(false))); } private void Changed(object sender, SettingChangedEventArgs e) { dirty = true; } private void Registered() { dirty = true; Apply(); } private void Synced(object sender, ConfigurationSynchronizationEventArgs e) { dirty = true; Apply(); } private void Update() { if (Time.unscaledTime >= nextScan) { nextScan = Time.unscaledTime + 1f; if (dirty || (Object.op_Implicit((Object)(object)ObjectDB.instance) && ObjectDB.instance.m_recipes.Any((Recipe r) => Object.op_Implicit((Object)(object)r) && Object.op_Implicit((Object)(object)r.m_item) && !originals.ContainsKey(r)))) { Apply(); } } TickUi(); } private void Apply() { if (!Object.op_Implicit((Object)(object)ObjectDB.instance) || ObjectDB.instance.m_recipes.Count == 0) { return; } MealRules mealRules; try { mealRules = new MealRules(overrides.Value); error = ""; } catch (Exception ex) { error = "Invalid overrides: " + ex.Message; ((BaseUnityPlugin)this).Logger.LogWarning((object)(error + " Using preset without overrides.")); mealRules = new MealRules(""); } HashSet<string> prepared = new HashSet<string>(StringComparer.OrdinalIgnoreCase); CookingStation[] array = Resources.FindObjectsOfTypeAll<CookingStation>(); for (int i = 0; i < array.Length; i++) { foreach (ItemConversion item in array[i].m_conversion) { if (Object.op_Implicit((Object)(object)item.m_from) && Object.op_Implicit((Object)(object)item.m_to) && IsFood(item.m_to)) { prepared.Add(((Object)item.m_from).name); } } } Piece[] array2 = (from f in Resources.FindObjectsOfTypeAll<Feast>() select ((Component)f).GetComponent<Piece>() into p where Object.op_Implicit((Object)(object)p) && !((Object)p).name.EndsWith("(Clone)", StringComparison.Ordinal) select p).ToArray(); HashSet<string> hashSet = new HashSet<string>(from r in array2.SelectMany((Piece p) => p.m_resources) where Object.op_Implicit((Object)(object)r.m_resItem) && !IsFood(r.m_resItem) select ((Object)r.m_resItem).name, StringComparer.OrdinalIgnoreCase); HashSet<string> hashSet2 = new HashSet<string>(from r in ObjectDB.instance.m_recipes where Object.op_Implicit((Object)(object)r) && Object.op_Implicit((Object)(object)r.m_item) && (IsFood(r.m_item) || prepared.Contains(((Object)r.m_item).name)) select ((Object)r.m_item).name, StringComparer.OrdinalIgnoreCase); int num = 0; foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) { continue; } if (!originals.TryGetValue(recipe, out var value)) { value = new Original { Amount = recipe.m_amount, Costs = recipe.m_resources.Select((Requirement r) => r.m_amount).ToArray() }; originals.Add(recipe, value); } if (recipe.m_enabled && value.Costs.Length == recipe.m_resources.Length && (IsFood(recipe.m_item) || prepared.Contains(((Object)recipe.m_item).name) || hashSet.Contains(((Object)recipe.m_item).name))) { recipe.m_amount = ((mealsEnabled.Value && !recipe.m_requireOnlyOneIngredient && !hashSet.Contains(((Object)recipe.m_item).name)) ? mealRules.Yield(((Object)recipe.m_item).name, value.Amount, recipe.m_item.m_itemData.m_shared.m_maxStackSize, preset.Value) : value.Amount); for (int num2 = 0; num2 < value.Costs.Length; num2++) { Requirement val = recipe.m_resources[num2]; val.m_amount = ((mealsEnabled.Value && Object.op_Implicit((Object)(object)val.m_resItem)) ? mealRules.Cost(((Object)recipe.m_item).name, ((Object)val.m_resItem).name, value.Costs[num2], preset.Value, MealRules.Classify(((Object)val.m_resItem).name, hashSet2.Contains(((Object)val.m_resItem).name))) : value.Costs[num2]); } num++; } } Piece[] array3 = array2; foreach (Piece val2 in array3) { if (!feastOriginals.TryGetValue(val2, out var value2)) { value2 = val2.m_resources.Select((Requirement r) => r.m_amount).ToArray(); feastOriginals.Add(val2, value2); } if (value2.Length == val2.m_resources.Length) { for (int num3 = 0; num3 < value2.Length; num3++) { Requirement val3 = val2.m_resources[num3]; val3.m_amount = ((mealsEnabled.Value && Object.op_Implicit((Object)(object)val3.m_resItem)) ? mealRules.Cost(((Object)val2).name, ((Object)val3.m_resItem).name, value2[num3], preset.Value, MealRules.Classify(((Object)val3.m_resItem).name, hashSet2.Contains(((Object)val3.m_resItem).name))) : value2[num3]); } } } dirty = false; report = (mealsEnabled.Value ? (preset.Value.ToString() + " · " + num + " food recipes adjusted") : "Disabled · original recipe quantities restored"); ((BaseUnityPlugin)this).Logger.LogInfo((object)report); } private static bool IsFood(ItemDrop item) { if (Object.op_Implicit((Object)(object)item) && item.m_itemData.m_shared.m_food > 0f) { return item.m_itemData.m_shared.m_foodBurnTime > 0f; } return false; } private void OnDestroy() { ((BaseUnityPlugin)this).Config.SettingChanged -= Changed; ItemManager.OnItemsRegistered -= Registered; ItemManager.OnItemsRegisteredFejd -= Registered; SynchronizationManager.OnConfigurationSynchronized -= Synced; foreach (KeyValuePair<Recipe, Original> original in originals) { if (Object.op_Implicit((Object)(object)original.Key)) { original.Key.m_amount = original.Value.Amount; for (int i = 0; i < Math.Min(original.Value.Costs.Length, original.Key.m_resources.Length); i++) { original.Key.m_resources[i].m_amount = original.Value.Costs[i]; } } } foreach (KeyValuePair<Piece, int[]> feastOriginal in feastOriginals) { if (Object.op_Implicit((Object)(object)feastOriginal.Key)) { for (int j = 0; j < Math.Min(feastOriginal.Value.Length, feastOriginal.Key.m_resources.Length); j++) { feastOriginal.Key.m_resources[j].m_amount = feastOriginal.Value[j]; } } } DisposeUi(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Instance = null; } } }