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 ComputerModpack v0.0.2
plugins/Computer-classes/Computer-classes.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ComputerMod.Core; using ComputerMod.Foundation.Classes; using ComputerMod.Foundation.Compatibility; using ComputerMod.Foundation.Items; using ComputerMod.Foundation.UI; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; 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("Computer-classes")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("0.3.1.0")] [assembly: AssemblyInformationalVersion("0.3.1")] [assembly: AssemblyProduct("Computer-classes")] [assembly: AssemblyTitle("Computer-classes")] [assembly: AssemblyVersion("0.3.1.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 ComputerMod.Foundation { [BepInPlugin("com.froze.computermod.foundation", "Computer-classes", "0.3.1")] [BepInDependency("com.jotunn.jotunn", "2.30.1")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class ComputerModPlugin : BaseUnityPlugin { private Harmony? _harmony; internal static ManualLogSource Logger { get; private set; } private void Awake() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; ClassService.Initialize(((BaseUnityPlugin)this).Config, Logger); try { _harmony = new Harmony("com.froze.computermod.foundation"); _harmony.PatchAll(typeof(ComputerModPlugin).Assembly); ClassStone.Install(Logger); if (!Application.isBatchMode) { ClassMenu.Install(); } Logger.LogInfo((object)"Computer-classes 0.3.1 loaded; patches active. Class Stone registration queued."); } catch (Exception arg) { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } ClassStone.Uninstall(); Logger.LogError((object)string.Format("{0} initialization failed; patches removed: {1}", "Computer-classes", arg)); } } private void OnDestroy() { ClassService.Save(Player.m_localPlayer); ClassStone.Uninstall(); ClassMenu.Uninstall(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace ComputerMod.Foundation.UI { internal sealed class ClassMenu : MonoBehaviour { private static ClassMenu? _instance; private bool _visible; private bool _confirm; private PlayerClass _selected = PlayerClass.Tank; private int _tab; private string? _backup; private string[] _backups = Array.Empty<string>(); private Vector2 _scroll; private Texture2D? _pixel; private GUIStyle? _title; private GUIStyle? _body; private GUIStyle? _section; private GUIStyle? _button; private CursorLockMode _oldLock; private bool _oldCursor; private Player? _openedFor; internal static void Install() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("ComputerMod.ClassMenu"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent<ClassMenu>(); } } internal static void Uninstall() { if (!((Object)(object)_instance == (Object)null)) { _instance.Close(); Object.Destroy((Object)(object)((Component)_instance).gameObject); _instance = null; } } internal static void Toggle() { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { if (_instance._visible) { _instance.Close(); return; } ClassMenu instance = _instance; instance._openedFor = Player.m_localPlayer; PlayerClass current = ClassService.Current; instance._selected = ((current == PlayerClass.None) ? PlayerClass.Tank : current); instance._confirm = false; instance._tab = ((!ClassService.TutorialSeen) ? 3 : 0); instance._backups = ClassService.Backups(); instance._backup = null; instance._scroll = Vector2.zero; instance._oldLock = Cursor.lockState; instance._oldCursor = Cursor.visible; instance._visible = true; GUIManager.BlockInput(true); } } private void Close() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (_visible) { _visible = false; GUIManager.BlockInput(false); Cursor.lockState = _oldLock; Cursor.visible = _oldCursor; } } private void Update() { if (_visible && ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)_openedFor || ((Character)Player.m_localPlayer).IsDead())) { Close(); } } private void LateUpdate() { if (_visible) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } } private void OnDestroy() { Close(); if ((Object)(object)_pixel != (Object)null) { Object.Destroy((Object)(object)_pixel); } } private void Styles() { //IL_001e: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_009c: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (_title == null) { _title = new GUIStyle(GUI.skin.label) { fontSize = 25, fontStyle = (FontStyle)1, wordWrap = true }; _body = new GUIStyle(GUI.skin.label) { fontSize = 15, wordWrap = true, richText = false }; _section = new GUIStyle(_body) { fontSize = 17, fontStyle = (FontStyle)1 }; _button = new GUIStyle(GUI.skin.button) { fontSize = 16, wordWrap = true }; _pixel = new Texture2D(1, 1); _pixel.SetPixel(0, 0, Color.white); _pixel.Apply(); } } private void Fill(Rect rect, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(rect, (Texture)(object)_pixel); GUI.color = color2; } private void OnGUI() { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) if (!_visible) { return; } if ((int)Event.current.type == 4 && (int)Event.current.keyCode == 27) { if (_confirm) { _confirm = false; } else { Close(); } Event.current.Use(); return; } Styles(); Matrix4x4 matrix = GUI.matrix; Color color = GUI.color; bool enabled = GUI.enabled; try { GUI.color = Color.white; Fill(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), new Color(0f, 0f, 0f, 0.82f)); float num = Mathf.Min(1.2f, Mathf.Min((float)Screen.width / 960f, (float)Screen.height / 730f)); GUI.matrix = Matrix4x4.TRS(new Vector3(((float)Screen.width - 920f * num) / 2f, ((float)Screen.height - 690f * num) / 2f), Quaternion.identity, Vector3.one * num); DrawPanel(); } finally { GUI.matrix = matrix; GUI.color = color; GUI.enabled = enabled; } } private void DrawPanel() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_05b8: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_0628: Unknown result type (might be due to invalid IL or missing references) //IL_062d: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_07ad: Unknown result type (might be due to invalid IL or missing references) //IL_06ce: Unknown result type (might be due to invalid IL or missing references) //IL_06f8: Unknown result type (might be due to invalid IL or missing references) //IL_0774: Unknown result type (might be due to invalid IL or missing references) Fill(new Rect(0f, 0f, 920f, 690f), new Color(0.07f, 0.085f, 0.105f, 1f)); Fill(new Rect(0f, 0f, 920f, 4f), new Color(0.8f, 0.61f, 0.3f, 1f)); GUI.Label(new Rect(24f, 20f, 760f, 40f), "Computer-classes v0.3.1", _title); GUI.Label(new Rect(24f, 67f, 865f, 44f), $"Active: {ClassService.Current} | {ClassService.SaveStatus}", _body); if (GUI.Button(new Rect(828f, 20f, 68f, 34f), "Close", _button)) { Close(); } string[] array = new string[4] { "Choose class", "Saved skills", "Backups", "How it works" }; for (int i = 0; i < array.Length; i++) { GUI.color = (Color)((i == _tab) ? new Color(1f, 0.82f, 0.48f) : Color.white); if (GUI.Button(new Rect((float)(24 + i * 221), 117f, 214f, 36f), array[i], _button)) { _tab = i; _confirm = false; _scroll = Vector2.zero; } } GUI.color = Color.white; if (_tab <= 1) { for (int j = 0; j < ClassDefinition.All.Length; j++) { ClassDefinition classDefinition = ClassDefinition.All[j]; GUI.color = (Color)((classDefinition.Id == _selected) ? new Color(1f, 0.82f, 0.48f) : Color.white); string text = classDefinition.Title + ((classDefinition.Id == ClassService.Current) ? " [active]" : ""); if (GUI.Button(new Rect(24f, (float)(173 + j * 49), 218f, 41f), text, _button)) { _selected = classDefinition.Id; _confirm = false; _scroll = Vector2.zero; } } } GUI.color = Color.white; Rect val = ((_tab <= 1) ? new Rect(263f, 174f, 630f, 380f) : new Rect(24f, 174f, 870f, 380f)); GUILayout.BeginArea(val); _scroll = GUILayout.BeginScrollView(_scroll, Array.Empty<GUILayoutOption>()); if (_tab == 0) { Details(); } else if (_tab == 1) { GUILayout.Label($"{_selected} — saved progress", _section, Array.Empty<GUILayoutOption>()); foreach (string item in ClassService.SkillSummary(_selected)) { GUILayout.Label(item, _body, Array.Empty<GUILayoutOption>()); } } else if (_tab == 2) { GUILayout.Label("Restore a previous class snapshot", _section, Array.Empty<GUILayoutOption>()); GUILayout.Label("Backups belong to this character only. Restoring replaces all saved class progress with that snapshot. A backup of your current progress is made first.", _body, Array.Empty<GUILayoutOption>()); if (_backups.Length == 0) { GUILayout.Label("No backups yet. One is created before your first class choice.", _body, Array.Empty<GUILayoutOption>()); } string[] backups = _backups; foreach (string text2 in backups) { GUI.color = (Color)((_backup == text2) ? new Color(1f, 0.82f, 0.48f) : Color.white); if (GUILayout.Button(Path.GetFileNameWithoutExtension(text2), _button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { _backup = text2; _confirm = false; } } GUI.color = Color.white; } else { GUILayout.Label("Your class can change. Your progress stays.", _title, Array.Empty<GUILayoutOption>()); GUILayout.Label("1. Craft a Class Stone at a workbench using 1 Stone. Use it from your inventory or hotbar; it is reusable.", _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label("2. Each class has separate skills, including modded skills and partial XP. A new class starts at zero. Returning restores its previous progress. Your pre-class skills are backed up on your first choice.", _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label("3. The class preview lists XP bonuses and restrictions. Gathering remains available to everyone. Shields require Tank; bombs require Thrower. Other weapons remain usable, with class XP bonuses only for matching weapons.", _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label("4. Restricted equipped items move into your normal inventory. If there is insufficient space, the change is refused. Attacking or taking damage delays switching; changes also have a 10-second cooldown.", _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label("5. Multiplayer requires this exact mod version on every player and the server. Server-controlled balance settings synchronize through Jotunn. Character skill saves are client-owned, not an anti-cheat system.", _body, Array.Empty<GUILayoutOption>()); } GUILayout.EndScrollView(); GUILayout.EndArea(); string text3 = ClassService.ChangeBlockReason(); GUI.Label(new Rect(24f, 565f, 870f, 48f), (text3.Length > 0) ? text3 : ClassService.LastMessage, _body); if (_tab == 3) { if (GUI.Button(new Rect(24f, 626f, 870f, 42f), "Understood — choose a class", _button)) { ClassService.DismissTutorial(); _tab = 0; _scroll = Vector2.zero; } } else { if (_tab == 1) { return; } bool enabled = text3.Length == 0 && ((_tab == 2) ? (_backup != null) : (_selected != ClassService.Current)); GUI.enabled = enabled; string text4 = ((_tab == 2) ? "Restore selected backup" : $"Change to {_selected}"); if (_confirm) { GUI.Label(new Rect(24f, 607f, 870f, 22f), "Confirm: current skills will be saved, then selected progress will replace active skills.", _body); if (GUI.Button(new Rect(24f, 635f, 620f, 36f), "Confirm — " + text4, _button)) { bool flag = ((_tab == 2) ? ClassService.RestoreBackup(_backup) : ClassService.Select(_selected)); _confirm = false; if (flag) { _backups = ClassService.Backups(); } } GUI.enabled = true; if (GUI.Button(new Rect(657f, 635f, 237f, 36f), "Cancel", _button)) { _confirm = false; } } else if (GUI.Button(new Rect(24f, 626f, 870f, 42f), text4, _button)) { _confirm = true; } GUI.enabled = true; } } private void Details() { ClassDefinition classDefinition = ClassDefinition.All.First((ClassDefinition c) => c.Id == _selected); GUILayout.Label(classDefinition.Title, _title, Array.Empty<GUILayoutOption>()); GUILayout.Label(classDefinition.Summary, _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label($"+{(ClassService.ExperienceMultiplier - 1f) * 100f:0.#}% experience", _section, Array.Empty<GUILayoutOption>()); GUILayout.Label(classDefinition.BonusSkills, _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label("Active effects", _section, Array.Empty<GUILayoutOption>()); GUILayout.Label((_selected == PlayerClass.Tank) ? $"{ClassService.TankReduction * 100f:0.#}% less stamina for blocks and parries." : ((_selected == PlayerClass.Berserker) ? $"+{ClassService.RadiusBonus:0.#}m two-handed melee hit radius, including area attacks. This widens the existing attack; it does not create a separate explosion." : "No additional class combat effect."), _body, Array.Empty<GUILayoutOption>()); GUILayout.Label("Direct damage bonus: 0%. Direct damage penalty: 0%. Normal skill-based damage still applies.", _body, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.Label("Equipment and skill rules", _section, Array.Empty<GUILayoutOption>()); GUILayout.Label("Shields: Tank only. Bombs: Thrower only. All classes may gather, craft, farm, cook and fish.", _body, Array.Empty<GUILayoutOption>()); GUILayout.Label("Dual weapons, lances and tridents use Warrior bonuses. Other two-handed melee weapons use Berserker bonuses. Blood Magic receives no class XP bonus.", _body, Array.Empty<GUILayoutOption>()); if (_selected == PlayerClass.Support) { GUILayout.Label("Also supports Mining, Lumberjacking, Blacksmithing, Sailing and PackHorse, plus Run, Jump, Swim, Sneak, Dodge and Ride. Unknown custom skills are not automatically treated as gathering.", _body, Array.Empty<GUILayoutOption>()); } } } } namespace ComputerMod.Foundation.Patches { [HarmonyPatch] internal static class BerserkerMeleeRadiusPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Attack), "DoMeleeAttack", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Attack), "DoAreaAttack", (Type[])null, (Type[])null); } private static void Prefix(Attack __instance, Humanoid ___m_character, ItemData ___m_weapon, out float? __state) { __state = null; if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)___m_character != (Object)(object)Player.m_localPlayer) && ClassService.Current == PlayerClass.Berserker && ___m_weapon != null && WeaponRules.IsBerserker(___m_weapon)) { __state = __instance.m_attackRayWidth; __instance.m_attackRayWidth += ClassService.RadiusBonus; } } private static void Finalizer(Attack __instance, float? __state) { if (__state.HasValue) { __instance.m_attackRayWidth = __state.Value; } } } [HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[] { typeof(ItemData), typeof(bool) })] internal static class ClassEquipmentRestrictionPatch { private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || item == null || !WeaponRules.Locked(item, ClassService.Current)) { return true; } __result = false; ((Character)__instance).Message((MessageType)2, ((int)item.m_shared.m_itemType == 5) ? "Only Tanks can equip shields." : "Only Throwers can equip bombs.", 0, (Sprite)null, false); return false; } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] internal static class ClassExperiencePatch { private static void Prefix(Skills __instance, SkillType skillType, ref float factor) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) factor *= ClassService.GetExperienceMultiplier(__instance, skillType); } } [HarmonyPatch(typeof(Player), "Save", new Type[] { typeof(ZPackage) })] internal static class ClassSavePatch { [HarmonyPriority(800)] private static void Prefix(Player __instance) { ClassService.Save(__instance); } } [HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })] internal static class ClassLoadPatch { private static void Postfix(Player __instance) { ClassService.OnLoaded(__instance); } } [HarmonyPatch(typeof(Player), "OnDamaged", new Type[] { typeof(HitData) })] internal static class ClassDamageLockPatch { private static void Postfix(Player __instance, HitData hit) { if (hit.GetTotalDamage() > 0f) { ClassService.MarkCombat(__instance); } } } [HarmonyPatch(typeof(Humanoid), "StartAttack", new Type[] { typeof(Character), typeof(bool) })] internal static class ClassAttackLockPatch { private static void Postfix(Humanoid __instance, bool __result) { if (__result) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { ClassService.MarkCombat(val); } } } } [HarmonyPatch(typeof(Humanoid), "UseItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(bool) })] internal static class ClassStoneUsePatch { private static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { object obj; if (item == null) { obj = null; } else { GameObject dropPrefab = item.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if (!((string?)obj != "ComputerMod_ClassStone")) { if ((inventory ?? __instance.GetInventory()).ContainsItem(item)) { ClassMenu.Toggle(); } return false; } } return true; } } [HarmonyPatch(typeof(Player), "GetEquipmentBlockStaminaModifier")] internal static class TankBlockStaminaPatch { private static void Postfix(Player __instance, ref float __result) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ClassService.Current == PlayerClass.Tank) { __result = (1f + __result) * (1f - ClassService.TankReduction) - 1f; } } } } namespace ComputerMod.Foundation.Items { internal static class ClassStone { internal const string PrefabName = "ComputerMod_ClassStone"; private static bool _registered; private static ManualLogSource _logger; internal static void Install(ManualLogSource logger) { _logger = logger; PrefabManager.OnVanillaPrefabsAvailable -= Register; PrefabManager.OnVanillaPrefabsAvailable += Register; } internal static void Uninstall() { PrefabManager.OnVanillaPrefabsAvailable -= Register; } private static void Register() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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) //IL_0065: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (_registered) { return; } try { ItemConfig val = new ItemConfig(); val.Name = "Class Stone"; val.Description = "Use to choose a class, inspect bonuses and saved skills, or restore a class backup. Reusable."; val.CraftingStation = "piece_workbench"; val.StackSize = 1; val.Requirements = (RequirementConfig[])(object)new RequirementConfig[1] { new RequirementConfig { Item = "Stone", Amount = 1 } }; ItemConfig val2 = val; CustomItem val3 = new CustomItem("ComputerMod_ClassStone", "Stone", val2); if ((Object)(object)val3.ItemDrop == (Object)null) { throw new InvalidOperationException("The Stone prefab is not available."); } val3.ItemDrop.m_itemData.m_shared.m_itemType = (ItemType)2; if (!ItemManager.Instance.AddItem(val3)) { throw new InvalidOperationException("Jotunn rejected the Class Stone."); } _registered = true; Uninstall(); _logger.LogInfo((object)"Registered Class Stone: craft from 1 Stone at a workbench; use to open the class menu."); } catch (Exception arg) { _logger.LogError((object)$"Class Stone registration failed: {arg}"); } } } } namespace ComputerMod.Foundation.Compatibility { internal sealed class EquipmentAdapter { private readonly Player _player; private readonly List<(ItemData Item, Vector2i Destination)> _moves = new List<(ItemData, Vector2i)>(); private EquipmentAdapter(Player player) { _player = player; } internal static bool TryPlan(Player player, PlayerClass target, out EquipmentAdapter plan, out string error) { //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) plan = new EquipmentAdapter(player); error = ""; Inventory inventory = ((Humanoid)player).GetInventory(); int width = inventory.GetWidth(); int num = inventory.GetHeight(); Type type = AccessTools.TypeByName("AzuEPI.Core.InventoryHandlers.Layout"); if (type != null) { try { num = Math.Min(num, (int)AccessTools.Property(type, "NormalInventoryRows").GetValue(null, null)); } catch { error = "Cannot read equipment slots. Unequip restricted equipment manually before switching."; return false; } } List<ItemData> allItems = inventory.GetAllItems(); HashSet<(int, int)> hashSet = new HashSet<(int, int)>(allItems.Select((ItemData i) => (x: i.m_gridPos.x, y: i.m_gridPos.y))); ItemData[] array = allItems.Where((ItemData i) => i.m_equipped && WeaponRules.Locked(i, target)).ToArray(); foreach (ItemData val in array) { if (val.m_gridPos.y >= 0 && val.m_gridPos.y < num && val.m_gridPos.x >= 0 && val.m_gridPos.x < width) { plan._moves.Add((val, val.m_gridPos)); continue; } Vector2i? val2 = null; for (int num3 = 0; num3 < num; num3++) { if (val2.HasValue) { break; } for (int num4 = 0; num4 < width; num4++) { if (hashSet.Add((num4, num3))) { val2 = new Vector2i(num4, num3); break; } } } if (!val2.HasValue) { error = "Cannot change class: clear space in your normal inventory for restricted equipment."; return false; } plan._moves.Add((val, val2.Value)); } return true; } internal void Apply() { //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) foreach (var move in _moves) { ((Humanoid)_player).UnequipItem(move.Item, false); if (move.Item.m_equipped) { throw new InvalidOperationException("Another mod prevented unequipping."); } if (!((Humanoid)_player).GetInventory().ContainsItem(move.Item) || ((Humanoid)_player).GetInventory().GetAllItems().Any((ItemData i) => i != move.Item && i.m_gridPos.x == move.Destination.x && i.m_gridPos.y == move.Destination.y)) { throw new InvalidOperationException("Inventory changed while unequipping. No occupied slot was overwritten."); } move.Item.m_gridPos = move.Destination; } if (_moves.Count > 0) { AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null).Invoke(((Humanoid)_player).GetInventory(), new object[2] { false, false }); } } } internal static class WeaponRules { private static readonly string[] LifeNames = new string[9] { "Mining", "Lumberjacking", "Blacksmithing", "Cooking", "Farming", "Sailing", "PackHorse", "Fishing", "Crafting" }; private static readonly Dictionary<SkillType, string> CustomLife = LifeNames.ToDictionary((string n) => (SkillType)StableId(n), (string n) => n); internal static readonly SkillType Throwing = (SkillType)StableId("Throwing"); private static int StableId(string text) { int num = 5381; int num2 = num; for (int i = 0; i < text.Length; i += 2) { num = ((num << 5) + num) ^ text[i]; if (i == text.Length - 1) { break; } num2 = ((num2 << 5) + num2) ^ text[i + 1]; } return Math.Abs(num + num2 * 1566083941); } private static bool Has(ItemData item, params string[] names) { string name = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name); return names.Any((string n) => name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0); } internal static bool IsBomb(ItemData item) { return Has(item, "Bomb", "Grenade"); } internal static bool IsWarrior(ItemData item) { return Has(item, "Dual", "Lance", "Trident") || ((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString().Equals("TwoHandedWeaponLeft"); } internal static bool IsBerserker(ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 return (int)item.m_shared.m_itemType == 14 && !IsWarrior(item) && (int)item.m_shared.m_skillType != 9 && (int)item.m_shared.m_skillType != 10 && (int)item.m_shared.m_skillType != 12 && (int)item.m_shared.m_skillType != 8 && (int)item.m_shared.m_skillType != 14; } internal static bool Locked(ItemData item, PlayerClass target) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 return target != PlayerClass.None && (((int)item.m_shared.m_itemType == 5 && target != PlayerClass.Tank) || (IsBomb(item) && target != PlayerClass.Thrower)); } internal static bool IsLife(SkillType skill) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 bool flag = CustomLife.ContainsKey(skill); bool flag2 = flag; if (!flag2) { bool flag3 = ((skill - 12 <= 1 || skill - 100 <= 8 || (int)skill == 110) ? true : false); flag2 = flag3; } return flag2; } internal static bool Boosts(PlayerClass cls, SkillType skill, ItemData? weapon) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_0065: 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) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Invalid comparison between Unknown and I4 //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Invalid comparison between Unknown and I4 //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Invalid comparison between Unknown and I4 switch (cls) { case PlayerClass.Support: return IsLife(skill); case PlayerClass.Tank: return (int)skill == 6; case PlayerClass.Ranger: return ((int)skill == 8 || (int)skill == 14) ? true : false; case PlayerClass.Magician: return (int)skill == 9; case PlayerClass.Thrower: if (skill == Throwing) { return true; } break; } bool flag = cls == PlayerClass.Rogue; bool flag2 = flag; bool flag3; if (flag2) { flag3 = (((int)skill == 11 || (int)skill == 101) ? true : false); flag2 = flag3; } if (flag2) { return true; } if (weapon == null || weapon.m_shared.m_skillType != skill || IsLife(skill)) { return false; } if (1 == 0) { } flag3 = cls switch { PlayerClass.Rogue => !IsWarrior(weapon) && !IsBomb(weapon) && ((int)weapon.m_shared.m_itemType == 3 || (int)skill == 5), PlayerClass.Berserker => IsBerserker(weapon), PlayerClass.Warrior => IsWarrior(weapon), PlayerClass.Thrower => IsBomb(weapon) || Has(weapon, "Throw"), _ => false, }; if (1 == 0) { } return flag3; } internal unsafe static string SkillName(SkillType skill) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected I4, but got Unknown if (CustomLife.TryGetValue(skill, out string value)) { return value; } if (skill == Throwing) { return "Throwing"; } string text = "$skill_" + (Enum.IsDefined(typeof(SkillType), skill) ? ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() : ((int)skill).ToString()); Localization instance = Localization.instance; string text2 = ((instance != null) ? instance.Localize(text) : null); return (string.IsNullOrEmpty(text2) || text2.Contains("skill_")) ? ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString() : text2; } } } namespace ComputerMod.Foundation.Classes { public sealed class ClassDefinition { public static readonly ClassDefinition[] All = new ClassDefinition[8] { new ClassDefinition(PlayerClass.Tank, "Tank", "Defensive shield specialist.", "Blocking", "Only Tanks can equip shields. Blocking costs 15% less stamina."), new ClassDefinition(PlayerClass.Rogue, "Rogue", "Fast one-handed fighter and stealth user.", "One-handed weapons, Spears, Unarmed, Sneak", "Cannot equip shields."), new ClassDefinition(PlayerClass.Ranger, "Ranger", "Dedicated ranged weapon user.", "Bows, Crossbows", "Shields require Tank; bombs require Thrower."), new ClassDefinition(PlayerClass.Thrower, "Thrower", "Thrown-weapon and explosive specialist.", "Throwing weapons", "Only Throwers can equip bombs and thrown explosives."), new ClassDefinition(PlayerClass.Support, "Support", "Gathering, crafting, and life-skill specialist.", "Fishing, Cooking, Farming, Pickaxes, WoodCutting, Mining, Lumberjacking, Blacksmithing, Crafting, Sailing, PackHorse, Run, Jump, Swim, Sneak, Dodge, Ride", "Shields require Tank; bombs require Thrower."), new ClassDefinition(PlayerClass.Magician, "Magician", "Elemental spellcaster.", "Elemental Magic", "Shields require Tank; bombs require Thrower."), new ClassDefinition(PlayerClass.Berserker, "Berserker", "Two-handed melee area-damage specialist.", "Two-handed melee weapons, excluding Warrior weapons, magic, bows and gathering tools", "Shields require Tank; bombs require Thrower. Radius bonus is configurable."), new ClassDefinition(PlayerClass.Warrior, "Warrior", "Dual-wield, lance, and trident specialist.", "Dual-wield weapons, Lances, Tridents", "Shields require Tank; bombs require Thrower.") }; public PlayerClass Id { get; } public string Title { get; } public string Summary { get; } public string BonusSkills { get; } public string Restrictions { get; } public ClassDefinition(PlayerClass id, string title, string summary, string bonusSkills, string restrictions) { Id = id; Title = title; Summary = summary; BonusSkills = bonusSkills; Restrictions = restrictions; } } public static class ClassService { private const string SaveKey = "ComputerMod.Classes.v1"; private static readonly MethodInfo GetSkill = AccessTools.Method(typeof(Skills), "GetSkill", (Type[])null, (Type[])null); private static readonly MethodInfo GetSkillDef = AccessTools.Method(typeof(Skills), "GetSkillDef", (Type[])null, (Type[])null); private static ConfigFile _config = null; private static ManualLogSource _logger = null; private static Player? _owner; private static ClassProgress? _progress; private static string? _loadError; private static float _nextChange; private static float _combatUntil; private static ConfigEntry<float> _xp = null; private static ConfigEntry<float> _tank = null; private static ConfigEntry<float> _radius = null; private static ConfigEntry<float> _combatLock = null; public static string LastMessage { get; private set; } = ""; public static PlayerClass Current { get { EnsurePlayer(); ClassProgress? progress = _progress; return (progress != null) ? ((PlayerClass)progress.CurrentClass) : PlayerClass.None; } } public static float ExperienceMultiplier => _xp.Value; public static float TankReduction => _tank.Value; public static float RadiusBonus => _radius.Value; public static bool TutorialSeen { get { ClassProgress? progress = _progress; return progress != null && progress.TutorialSeen; } } public static string SaveStatus => _loadError ?? "Class levels and partial XP are saved with this character."; private static string BackupDirectory => Path.Combine(Path.GetDirectoryName(_config.ConfigFilePath), "ComputerModBackups"); public static void Initialize(ConfigFile config, ManualLogSource logger) { _config = config; _logger = logger; _xp = Setting("Class XP multiplier", 1.5f, 1f, 5f, "XP multiplier for skills supported by your class."); _tank = Setting("Tank block stamina reduction", 0.15f, 0f, 0.9f, "Reduces blocking and parrying stamina cost. 0.15 = 15%."); _radius = Setting("Berserker melee radius bonus", 1f, 0f, 5f, "Extra metres of hit radius for two-handed melee and area attacks."); _combatLock = Setting("Combat class change lock", 30f, 0f, 300f, "Seconds after attacking or taking damage before switching or restoring."); } private static ConfigEntry<float> Setting(string key, float value, float min, float max, string description) { //IL_001d: 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_002b: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown return _config.Bind<float>("Class Bonuses", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); } private static void EnsurePlayer() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)(object)_owner) { return; } _owner = localPlayer; _progress = null; _loadError = null; _nextChange = (_combatUntil = 0f); if ((Object)(object)localPlayer == (Object)null) { return; } try { _progress = (ClassProgress?)(localPlayer.m_customData.TryGetValue("ComputerMod.Classes.v1", out var value) ? ((object)ClassProgress.Parse(value, localPlayer.GetPlayerID())) : ((object)new ClassProgress { PlayerId = localPlayer.GetPlayerID() })); } catch (Exception arg) { _loadError = "Class save could not be read. Switching is disabled to preserve your data."; _logger.LogError((object)$"Class data for character {localPlayer.GetPlayerID()} was preserved: {arg}"); } } public static void OnLoaded(Player player) { if ((Object)(object)player == (Object)(object)_owner) { _owner = null; _progress = null; } } public static void MarkCombat(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { EnsurePlayer(); _combatUntil = Time.unscaledTime + _combatLock.Value; } } public static string ChangeBlockReason() { EnsurePlayer(); if ((Object)(object)_owner == (Object)null) { return "Enter a world to choose a class."; } if (_loadError != null) { return _loadError; } if (((Character)_owner).IsDead() || ((Character)_owner).IsTeleporting() || ((Character)_owner).InAttack()) { return "Finish your current action before changing class."; } int num = Mathf.CeilToInt(Mathf.Max(_combatUntil, _nextChange) - Time.unscaledTime); return (num > 0) ? $"Class changes available in {num}s." : ""; } public static bool Select(PlayerClass target) { if (!Enum.IsDefined(typeof(PlayerClass), target) || target == PlayerClass.None) { return false; } string text = ChangeBlockReason(); if (text.Length > 0) { return Message(text); } if (Current == target) { return Message("This class is already active."); } return ApplyChange(null, target); } private static bool ApplyChange(ClassProgress? restore, PlayerClass target) { //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) Player owner = _owner; ClassProgress progress = _progress; if (!EquipmentAdapter.TryPlan(owner, target, out EquipmentAdapter plan, out string error)) { return Message(error); } Capture(owner, progress); string text = progress.Serialize(); Dictionary<Skill, SkillProgress> dictionary = ((IEnumerable<Skill>)((Character)owner).GetSkills().GetSkillList()).ToDictionary((Func<Skill, Skill>)((Skill s) => s), (Func<Skill, SkillProgress>)((Skill s) => new SkillProgress(s.m_level, s.m_accumulator))); try { Directory.CreateDirectory(BackupDirectory); File.WriteAllText(Path.Combine(BackupDirectory, $"{owner.GetPlayerID()}-{DateTime.UtcNow:yyyyMMdd-HHmmss-fffffff}.xml"), text); ClassProgress val = restore ?? ClassProgress.Parse(text, owner.GetPlayerID()); val.CurrentClass = (int)target; Dictionary<Skill, SkillProgress> dictionary2 = BuildTargets(owner, val, target); plan.Apply(); foreach (KeyValuePair<Skill, SkillProgress> item in dictionary2) { Skill key = item.Key; SkillProgress value = item.Value; key.m_level = ((SkillProgress)(ref value)).Level; Skill key2 = item.Key; value = item.Value; key2.m_accumulator = ((SkillProgress)(ref value)).Experience; } _progress = val; owner.m_customData["ComputerMod.Classes.v1"] = val.Serialize(); _nextChange = Time.unscaledTime + 10f; _logger.LogInfo((object)$"Character {owner.GetPlayerID()} selected {target}; skill levels and partial XP restored."); LastMessage = $"{target} active. Your previous class skills are saved."; return true; } catch (Exception arg) { _progress = progress; foreach (Skill skill in ((Character)owner).GetSkills().GetSkillList()) { SkillProgress value2; SkillProgress val2 = (SkillProgress)(dictionary.TryGetValue(skill, out value2) ? value2 : new SkillProgress(0f, 0f)); skill.m_level = ((SkillProgress)(ref val2)).Level; skill.m_accumulator = ((SkillProgress)(ref val2)).Experience; } owner.m_customData["ComputerMod.Classes.v1"] = text; _logger.LogError((object)$"Class switch failed: {arg}"); return Message("Could not finish changing class. Your previous skills were restored; see the log."); } } private static Dictionary<Skill, SkillProgress> BuildTargets(Player player, ClassProgress data, PlayerClass target) { //IL_00a8: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) Skills skills = ((Character)player).GetSkills(); Dictionary<Skill, SkillProgress> dictionary = (from s in skills.GetSkillList() where s.m_info != null select s).ToDictionary((Func<Skill, Skill>)((Skill s) => s), (Func<Skill, SkillProgress>)((Skill _) => new SkillProgress(0f, 0f))); if (data.Classes.TryGetValue((int)target, out var value)) { foreach (KeyValuePair<int, SkillProgress> item in value) { SkillType val = (SkillType)item.Key; if (GetSkillDef.Invoke(skills, new object[1] { val }) != null) { Skill key = (Skill)GetSkill.Invoke(skills, new object[1] { val }); dictionary[key] = item.Value; } } } return dictionary; } private static void Capture(Player player, ClassProgress data) { //IL_007a: 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_0095: Expected I4, but got Unknown if (!data.Classes.TryGetValue(data.CurrentClass, out var value)) { value = (data.Classes[data.CurrentClass] = new Dictionary<int, SkillProgress>()); } foreach (Skill item in from s in ((Character)player).GetSkills().GetSkillList() where s.m_info != null select s) { value[(int)item.m_info.m_skill] = new SkillProgress(item.m_level, item.m_accumulator); } } public static void Save(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } EnsurePlayer(); if (_progress == null) { return; } try { Capture(player, _progress); player.m_customData["ComputerMod.Classes.v1"] = _progress.Serialize(); } catch (Exception arg) { _logger.LogError((object)$"Could not update class save: {arg}"); } } public static void DismissTutorial() { EnsurePlayer(); if (_progress != null) { _progress.TutorialSeen = true; Save(_owner); } } public static string[] Backups() { EnsurePlayer(); if ((Object)(object)_owner == (Object)null || !Directory.Exists(BackupDirectory)) { return Array.Empty<string>(); } return Directory.GetFiles(BackupDirectory, $"{_owner.GetPlayerID()}-*.xml").OrderByDescending(Path.GetFileName).Take(20) .ToArray(); } public static bool RestoreBackup(string path) { string text = ChangeBlockReason(); if (text.Length > 0) { return Message(text); } if (!Backups().Contains(path)) { return Message("Choose a backup for this character."); } try { ClassProgress val = ClassProgress.Parse(File.ReadAllText(path), _owner.GetPlayerID()); return ApplyChange(val, (PlayerClass)val.CurrentClass); } catch (Exception ex) { _logger.LogError((object)ex); return Message("Backup is invalid; nothing was restored."); } } public static IEnumerable<string> SkillSummary(PlayerClass target) { EnsurePlayer(); if ((Object)(object)_owner == (Object)null || _progress == null) { return Array.Empty<string>(); } if (target == Current) { Capture(_owner, _progress); } if (!_progress.Classes.TryGetValue((int)target, out var value) || value.Count == 0) { return new string[1] { "New class: all skills start at level 0." }; } return value.OrderBy((KeyValuePair<int, SkillProgress> p) => WeaponRules.SkillName((SkillType)p.Key)).Select(delegate(KeyValuePair<int, SkillProgress> p) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) string arg = WeaponRules.SkillName((SkillType)p.Key); SkillProgress value2 = p.Value; object arg2 = ((SkillProgress)(ref value2)).Level; value2 = p.Value; return $"{arg}: {arg2:0.#} / 100 | XP progress: {((SkillProgress)(ref value2)).Experience:0.##}"; }).ToArray(); } public static float GetExperienceMultiplier(Skills skills, SkillType type) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) return ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)((Character)Player.m_localPlayer).GetSkills() == (Object)(object)skills && WeaponRules.Boosts(Current, type, ((Humanoid)Player.m_localPlayer).GetCurrentWeapon())) ? ExperienceMultiplier : 1f; } private static bool Message(string text) { LastMessage = text; Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false); } return false; } } public enum PlayerClass { None, Tank, Rogue, Ranger, Thrower, Support, Magician, Berserker, Warrior } }
plugins/Computer-classes/Computer-classes.Core.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Xml; using System.Xml.Linq; using Microsoft.CodeAnalysis; [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("Computer-classes.Core")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("0.3.1.0")] [assembly: AssemblyInformationalVersion("0.3.1")] [assembly: AssemblyProduct("Computer-classes.Core")] [assembly: AssemblyTitle("Computer-classes.Core")] [assembly: AssemblyVersion("0.3.1.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 ComputerMod.Core { public sealed class ClassProgress { public long PlayerId { get; set; } public int CurrentClass { get; set; } public bool TutorialSeen { get; set; } public Dictionary<int, Dictionary<int, SkillProgress>> Classes { get; } = new Dictionary<int, Dictionary<int, SkillProgress>>(); public string Serialize() { XElement xElement = new XElement("ComputerMod", new XAttribute("version", 1), new XAttribute("player", PlayerId), new XAttribute("class", CurrentClass), new XAttribute("tutorial", TutorialSeen)); foreach (KeyValuePair<int, Dictionary<int, SkillProgress>> item in Classes.OrderBy<KeyValuePair<int, Dictionary<int, SkillProgress>>, int>((KeyValuePair<int, Dictionary<int, SkillProgress>> pair) => pair.Key)) { xElement.Add(new XElement("Class", new XAttribute("id", item.Key), from skill in item.Value orderby skill.Key select new XElement("Skill", new XAttribute("id", skill.Key), new XAttribute("level", skill.Value.Level), new XAttribute("xp", skill.Value.Experience)))); } return xElement.ToString(SaveOptions.DisableFormatting); } public static ClassProgress Parse(string text, long expectedPlayerId) { using XmlReader reader = XmlReader.Create(new StringReader(text), new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit, XmlResolver = null, MaxCharactersInDocument = 4000000L }); XElement xElement = XElement.Load(reader); if (xElement.Name != "ComputerMod" || (int?)xElement.Attribute("version") != 1) { throw new FormatException("Unsupported class save format."); } ClassProgress classProgress = new ClassProgress { PlayerId = (long)xElement.Attribute("player"), CurrentClass = (int)xElement.Attribute("class"), TutorialSeen = ((bool?)xElement.Attribute("tutorial") == true) }; if (classProgress.PlayerId != expectedPlayerId || classProgress.CurrentClass < 0 || classProgress.CurrentClass > 8) { throw new FormatException("Class save belongs to another character or has an invalid class."); } foreach (XElement item in xElement.Elements("Class")) { int num = (int)item.Attribute("id"); if (num < 0 || num > 8) { throw new FormatException("Invalid saved class."); } Dictionary<int, SkillProgress> dictionary = new Dictionary<int, SkillProgress>(); foreach (XElement item2 in item.Elements("Skill")) { float num2 = float.Parse((string?)item2.Attribute("level"), CultureInfo.InvariantCulture); float num3 = float.Parse((string?)item2.Attribute("xp"), CultureInfo.InvariantCulture); if (!ValidNumber(num2) || !ValidNumber(num3) || num2 > 100f) { throw new FormatException("Invalid saved skill progress."); } dictionary.Add((int)item2.Attribute("id"), new SkillProgress(num2, num3)); } classProgress.Classes.Add(num, dictionary); } return classProgress; } private static bool ValidNumber(float value) { return !float.IsNaN(value) && !float.IsInfinity(value) && value >= 0f; } } public readonly struct SkillProgress { public float Level { get; } public float Experience { get; } public SkillProgress(float level, float experience) { Level = level; Experience = experience; } } public static class PluginMetadata { public const string Guid = "com.froze.computermod.foundation"; public const string Name = "Computer-classes"; public const string Version = "0.3.1"; } }
plugins/Computer-TamedAnimals/Computer-TamedAnimals.dll
Decompiled a month 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.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Computer-TamedAnimals")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Expanded taming, riding, and animal cart hauling for Valheim.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("Computer-TamedAnimals")] [assembly: AssemblyTitle("Computer-TamedAnimals")] [assembly: AssemblyVersion("0.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 Computer.TamedAnimals { public sealed class AnimalCompanion : MonoBehaviour { internal const string CartKey = "CTA.Cart"; internal const string RequestRpc = "CTA.RequestCart"; internal const string ReleaseRpc = "CTA.ReleaseCart"; internal const string FollowRpc = "CTA.Follow"; private static readonly HashSet<string> Reported = new HashSet<string>(); internal Character Character; internal MonsterAI Ai; internal Tameable Tame; internal ZNetView View; internal string Prefab; internal bool OwnSaddle; private float _reservationUntil; private float _nextCheck; internal bool CanPull { get { if (Plugin.Settings.Carts && Object.op_Implicit((Object)(object)Character) && !Character.IsDead() && Character.IsTamed() && Plugin.Settings.Pullers.Contains(Prefab) && !Character.IsFlying()) { return !Ai.m_avoidLand; } return false; } } internal bool CanRide { get { if (Plugin.Settings.Riding && Object.op_Implicit((Object)(object)Character) && !Character.IsDead() && Character.IsTamed() && Plugin.Settings.Riders.Contains(Prefab) && Object.op_Implicit((Object)(object)Tame) && Object.op_Implicit((Object)(object)Tame.m_saddle)) { return ((Behaviour)Tame.m_saddle).isActiveAndEnabled; } return false; } } internal bool IsHauling { get { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)View) && View.IsValid()) { return (Object)(object)CartHarness.FindFor(Character.GetZDOID()) != (Object)null; } return false; } } internal Vector3 HitchPosition => ((Component)this).transform.TransformPoint(new Vector3(0f, Mathf.Clamp(Character.GetHeight() * 0.35f, 0.45f, 1.6f), 0f - Mathf.Max(0.6f, Character.GetRadius()))); internal static void AddTo(MonsterAI ai) { if (!Plugin.Settings.Enabled || Object.op_Implicit((Object)(object)((Component)ai).GetComponent<AnimalCompanion>())) { return; } Humanoid component = ((Component)ai).GetComponent<Humanoid>(); ZNetView component2 = ((Component)ai).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || ((Character)component).IsPlayer() || ((Character)component).IsBoss() || ((Character)component).m_flying || ((BaseAI)ai).m_randomFly || ai.m_avoidLand || !Object.op_Implicit((Object)(object)component2) || !component2.IsValid()) { return; } string prefabName = Utils.GetPrefabName(((Component)ai).gameObject); Settings settings = Plugin.Settings; if (!settings.Extra.Contains(prefabName) && !settings.Riders.Contains(prefabName) && !settings.Pullers.Contains(prefabName)) { return; } Tameable val = ((Component)ai).GetComponent<Tameable>(); if (!Object.op_Implicit((Object)(object)val)) { if (!settings.Extra.Contains(prefabName)) { return; } List<ItemDrop> list = new List<ItemDrop>(); if (settings.Foods.TryGetValue(prefabName, out var value)) { string[] array = value; foreach (string text in array) { GameObject val2 = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(text) : null); if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.GetComponent<ItemDrop>())) { list.Add(val2.GetComponent<ItemDrop>()); } else if (Reported.Add("food:" + text)) { Plugin.Warn("Food prefab not found: " + text); } } } if (list.Count == 0) { if (Reported.Add("diet:" + prefabName)) { Plugin.Warn("No valid food configured for " + prefabName + "; taming was not added."); } return; } ai.m_consumeItems = list; ai.m_consumeSearchRange = Mathf.Max(10f, ai.m_consumeSearchRange); val = ((Component)ai).gameObject.AddComponent<Tameable>(); val.m_tamingTime = settings.TamingTime; val.m_fedDuration = settings.FedTime; GameObject prefab = ZNetScene.instance.GetPrefab("Boar"); Tameable val3 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Tameable>() : null); if (Object.op_Implicit((Object)(object)val3)) { val.m_tamedEffect = val3.m_tamedEffect; val.m_sootheEffect = val3.m_sootheEffect; val.m_petEffect = val3.m_petEffect; } } GameApi.AiTame.Invoke((BaseAI)(object)ai) = val; val.m_commandable = true; AnimalCompanion animalCompanion = ((Component)ai).gameObject.AddComponent<AnimalCompanion>(); animalCompanion.Character = (Character)(object)component; animalCompanion.Ai = ai; animalCompanion.Tame = val; animalCompanion.View = component2; animalCompanion.Prefab = prefabName; animalCompanion.Initialize(); } private void Initialize() { View.Register<ZDOID, ZDOID>("CTA.RequestCart", (Action<long, ZDOID, ZDOID>)RequestCartRpc); View.Register<ZDOID>("CTA.ReleaseCart", (Action<long, ZDOID>)ReleaseCartRpc); View.Register<ZDOID, ZDOID>("CTA.Follow", (Action<long, ZDOID, ZDOID>)FollowCartRpc); if (Plugin.Settings.Riding && Plugin.Settings.Riders.Contains(Prefab) && !Object.op_Implicit((Object)(object)Tame.m_saddle)) { CreateSeat(); } if (Reported.Add("ready:" + Prefab)) { Plugin.Log.LogInfo((object)("Enabled creature: " + Prefab)); } } private void CreateSeat() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CTA_BarebackSeat"); val.SetActive(false); val.transform.SetParent(((Component)this).transform, false); Transform transform = new GameObject("AttachPoint").transform; transform.SetParent(val.transform, false); if (Plugin.Settings.Offsets.TryGetValue(Prefab, out var value)) { transform.localPosition = new Vector3(value[0], value[1], value[2]); } else { CapsuleCollider component = ((Component)this).GetComponent<CapsuleCollider>(); transform.localPosition = (Object.op_Implicit((Object)(object)component) ? (component.center + Vector3.up * component.height * 0.35f) : Vector3.up); } Sadle val2 = val.AddComponent<Sadle>(); val2.m_attachPoint = transform; val2.m_hoverText = Character.m_name; val2.m_maxUseRange = 10f; val2.m_detachOffset = new Vector3(Character.GetRadius() + 1f, 0.5f, 0f); GameObject prefab = ZNetScene.instance.GetPrefab("Lox"); Sadle val3 = ((!Object.op_Implicit((Object)(object)prefab)) ? null : prefab.GetComponent<Tameable>()?.m_saddle); if (Object.op_Implicit((Object)(object)val3)) { val2.m_attachAnimation = val3.m_attachAnimation; val2.m_mountIcon = val3.m_mountIcon; } Tame.m_saddle = val2; Tame.m_dropSaddleOnDeath = false; OwnSaddle = true; val.SetActive(true); } private void Update() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00b9: 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) if (!Object.op_Implicit((Object)(object)View) || !View.IsValid() || !View.IsOwner() || Time.time < _nextCheck) { return; } _nextCheck = Time.time + 1f; if (Character.IsTamed()) { ((BaseAI)Ai).SetHuntPlayer(false); Ai.SetDespawnInDay(false); Ai.SetEventCreature(false); } ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Cart"); if (!((ZDOID)(ref zDOID)).IsNone() && !(Time.time < _reservationUntil)) { ZDO zDO = ZDOMan.instance.GetZDO(zDOID); if (zDO == null || zDO.GetZDOID("CTA.Animal") != Character.GetZDOID()) { View.GetZDO().Set("CTA.Cart", ZDOID.None); } } } internal void RequestCart(CartHarness cart, Player player) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) View.InvokeRPC("CTA.RequestCart", new object[2] { cart.View.GetZDO().m_uid, ((Character)player).GetZDOID() }); } private void RequestCartRpc(long sender, ZDOID cartId, ZDOID playerId) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) if (!View.IsValid() || !View.IsOwner() || !CanPull) { return; } Player val = GameApi.Requester(sender, playerId, ((Component)this).transform.position); GameObject val2 = ZNetScene.instance.FindInstance(cartId); CartHarness cartHarness = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<CartHarness>() : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)cartHarness) && cartHarness.View.IsValid()) { ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Cart"); ZDO val3 = (((ZDOID)(ref zDOID)).IsNone() ? null : ZDOMan.instance.GetZDO(zDOID)); bool num = Time.time < _reservationUntil; bool flag = val3 != null && val3.GetZDOID("CTA.Animal") == Character.GetZDOID(); if (num || flag || cartHarness.HasLink || cartHarness.Cart.InUse() || Vector3.Distance(((Component)val).transform.position, ((Component)cartHarness).transform.position) > 10f || Vector3.Distance(cartHarness.Cart.m_attachPoint.position, HitchPosition) > Plugin.Settings.HitchRange) { cartHarness.Reply(sender, "Cannot hitch: move the cart shafts closer, and detach any existing hitch first."); return; } View.GetZDO().Set("CTA.Cart", cartId); _reservationUntil = Time.time + 8f; cartHarness.View.InvokeRPC("CTA.Connect", new object[2] { Character.GetZDOID(), playerId }); } } private void ReleaseCartRpc(long sender, ZDOID cartId) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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) if (View.IsValid() && View.IsOwner() && !(View.GetZDO().GetZDOID("CTA.Cart") != cartId)) { ZDO zDO = ZDOMan.instance.GetZDO(cartId); if (zDO == null || zDO.GetOwner() == sender) { View.GetZDO().Set("CTA.Cart", ZDOID.None); _reservationUntil = 0f; } } } private void FollowCartRpc(long sender, ZDOID cartId, ZDOID playerId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (!View.IsValid() || !View.IsOwner() || !CanPull) { return; } ZDO zDO = ZDOMan.instance.GetZDO(cartId); if (zDO != null && zDO.GetOwner() == sender && !(View.GetZDO().GetZDOID("CTA.Cart") != cartId)) { GameObject val = ZNetScene.instance.FindInstance(playerId); Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Player>() : null); if (Object.op_Implicit((Object)(object)val2) && !((Character)val2).IsDead() && !(Vector3.Distance(((Component)val2).transform.position, ((Component)this).transform.position) > 12f)) { ((BaseAI)Ai).ResetPatrolPoint(); Ai.SetFollowTarget(((Component)val2).gameObject); View.GetZDO().Set(ZDOVars.s_follow, val2.GetPlayerName()); } } } private void OnDestroy() { Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { IDoodadController doodadController = localPlayer.GetDoodadController(); Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null); if (val != null && (Object)(object)val == (Object)(object)Tame?.m_saddle) { localPlayer.StopDoodadControl(); } } } } public sealed class CartHarness : MonoBehaviour { internal const string AnimalKey = "CTA.Animal"; internal const string DriverKey = "CTA.Driver"; internal const string ConnectRpc = "CTA.Connect"; private const string DetachRpc = "CTA.Detach"; private const string MessageRpc = "CTA.Message"; private static readonly HashSet<CartHarness> Instances = new HashSet<CartHarness>(); internal Vagon Cart; internal ZNetView View; private AnimalCompanion _animal; private Rigidbody _anchor; private ConfigurableJoint _joint; private float _missingSince; private readonly List<(Collider cart, Collider animal)> _ignored = new List<(Collider, Collider)>(); internal bool HasLink { get { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)View) && View.IsValid()) { ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Animal"); return !((ZDOID)(ref zDOID)).IsNone(); } return false; } } internal bool IsConnected { get { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (HasLink && Object.op_Implicit((Object)(object)_joint) && Object.op_Implicit((Object)(object)_anchor) && (Object)(object)((Joint)_joint).connectedBody == (Object)(object)_anchor && Object.op_Implicit((Object)(object)_animal) && _animal.CanPull) { return Vector3.Distance(Cart.m_attachPoint.position, _animal.HitchPosition) < Plugin.Settings.BreakDistance; } return false; } } internal bool Protects { get { if (Plugin.Settings.Protection) { return IsConnected; } return false; } } internal bool IsOurAnchor(GameObject go) { if (Object.op_Implicit((Object)(object)_anchor)) { return (Object)(object)go == (Object)(object)((Component)_anchor).gameObject; } return false; } private void Awake() { Cart = ((Component)this).GetComponent<Vagon>(); View = ((Component)this).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)Cart) || !Object.op_Implicit((Object)(object)View) || !View.IsValid()) { ((Behaviour)this).enabled = false; return; } Instances.Add(this); View.Register<ZDOID, ZDOID>("CTA.Connect", (Action<long, ZDOID, ZDOID>)Connect); View.Register<ZDOID>("CTA.Detach", (Action<long, ZDOID>)DetachRequest); View.Register<string>("CTA.Message", (Action<long, string>)ReceiveMessage); } internal static CartHarness FindFor(ZDOID animalId) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) foreach (CartHarness instance in Instances) { if (Object.op_Implicit((Object)(object)instance) && instance.HasLink && instance.View.GetZDO().GetZDOID("CTA.Animal") == animalId) { return instance; } } return null; } internal void RequestDetach(Player player) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) View.InvokeRPC("CTA.Detach", new object[1] { ((Character)player).GetZDOID() }); } internal void Reply(long peer, string message) { View.InvokeRPC(peer, "CTA.Message", new object[1] { message }); } private void ReceiveMessage(long sender, string message) { if (message.Length <= 240) { Plugin.Say(message); } } private void Connect(long sender, ZDOID animalId, ZDOID playerId) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0156: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0190: 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_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) if (!View.IsValid() || !View.IsOwner()) { return; } GameObject val = ZNetScene.instance.FindInstance(animalId); AnimalCompanion animalCompanion = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<AnimalCompanion>() : null); GameObject val2 = ZNetScene.instance.FindInstance(playerId); Player val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Player>() : null); if (!Object.op_Implicit((Object)(object)animalCompanion) || !animalCompanion.View.IsValid() || animalCompanion.Character.GetOwner() != sender) { return; } if (!Object.op_Implicit((Object)(object)val3) || ((Character)val3).IsDead() || ((Character)val3).IsTeleporting() || !animalCompanion.CanPull || HasLink || Cart.InUse() || (Object)(object)FindFor(animalId) != (Object)null || Vector3.Distance(((Component)val3).transform.position, ((Component)this).transform.position) > 10f || Vector3.Distance(((Component)val3).transform.position, ((Component)animalCompanion).transform.position) > 10f || Vector3.Distance(Cart.m_attachPoint.position, animalCompanion.HitchPosition) > Plugin.Settings.HitchRange) { animalCompanion.View.InvokeRPC("CTA.ReleaseCart", new object[1] { View.GetZDO().m_uid }); if (Object.op_Implicit((Object)(object)val3)) { Reply(((Character)val3).GetOwner(), "The cart could not connect. Move closer or detach its existing user."); } } else { View.GetZDO().Set("CTA.Animal", animalId); View.GetZDO().Set("CTA.Driver", playerId); animalCompanion.View.InvokeRPC("CTA.Follow", new object[2] { View.GetZDO().m_uid, playerId }); Reply(((Character)val3).GetOwner(), "Cart hitched. Your animal will follow; pet it to toggle follow/stay."); } } private void DetachRequest(long sender, ZDOID playerId) { //IL_001b: 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) if (View.IsValid() && View.IsOwner() && Object.op_Implicit((Object)(object)GameApi.Requester(sender, playerId, ((Component)this).transform.position))) { Disconnect(); Reply(sender, "Cart detached. Normal cart damage is restored."); } } private void FixedUpdate() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)View) || !View.IsValid()) { ClearLocal(); return; } if (!HasLink) { ClearLocal(); return; } ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Animal"); if (!Object.op_Implicit((Object)(object)_animal) || _animal.Character.GetZDOID() != zDOID) { GameObject val = ZNetScene.instance.FindInstance(zDOID); _animal = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<AnimalCompanion>() : null); } if (!View.IsOwner()) { ClearLocal(); return; } if (!Object.op_Implicit((Object)(object)_animal)) { ClearLocal(); if (_missingSince == 0f) { _missingSince = Time.time; } if (Time.time - _missingSince > 5f) { Disconnect(); } return; } _missingSince = 0f; GameObject val2 = ZNetScene.instance.FindInstance(View.GetZDO().GetZDOID("CTA.Driver")); Player val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Player>() : null); if (!_animal.CanPull || (Object.op_Implicit((Object)(object)val3) && (((Character)val3).IsDead() || ((Character)val3).IsTeleporting() || Vector3.Distance(((Component)val3).transform.position, ((Component)_animal).transform.position) > 64f)) || (!Object.op_Implicit((Object)(object)val3) && !TameHasRider()) || Vector3.Distance(Cart.m_attachPoint.position, _animal.HitchPosition) > Plugin.Settings.BreakDistance || ((Component)this).transform.up.y < 0.1f) { Disconnect(); return; } long owner = _animal.Character.GetOwner(); if (owner != 0L && owner != View.GetZDO().GetOwner()) { ClearLocal(); View.GetZDO().SetOwner(owner); return; } if (!Object.op_Implicit((Object)(object)_joint)) { CreateJoint(); } if (Object.op_Implicit((Object)(object)_anchor)) { _anchor.MovePosition(_animal.HitchPosition); _anchor.MoveRotation(((Component)_animal).transform.rotation); } } private bool TameHasRider() { if (Object.op_Implicit((Object)(object)_animal) && Object.op_Implicit((Object)(object)_animal.Tame)) { return _animal.Tame.HaveRider(); } return false; } private void CreateJoint() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_002b: 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) //IL_009d: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)_animal)) { return; } ClearLocal(); GameObject val = new GameObject("CTA_CartHitch"); val.transform.SetPositionAndRotation(_animal.HitchPosition, ((Component)_animal).transform.rotation); _anchor = val.AddComponent<Rigidbody>(); _anchor.isKinematic = true; _anchor.useGravity = false; _joint = ((Component)this).gameObject.AddComponent<ConfigurableJoint>(); ((Joint)_joint).autoConfigureConnectedAnchor = false; ((Joint)_joint).anchor = ((Component)this).transform.InverseTransformPoint(Cart.m_attachPoint.position); ((Joint)_joint).connectedBody = _anchor; ((Joint)_joint).connectedAnchor = Vector3.zero; _joint.xMotion = (ConfigurableJointMotion)1; _joint.yMotion = (ConfigurableJointMotion)1; _joint.zMotion = (ConfigurableJointMotion)1; ConfigurableJoint joint = _joint; SoftJointLimit linearLimit = default(SoftJointLimit); ((SoftJointLimit)(ref linearLimit)).limit = 0.15f; joint.linearLimit = linearLimit; ConfigurableJoint joint2 = _joint; SoftJointLimitSpring linearLimitSpring = default(SoftJointLimitSpring); ((SoftJointLimitSpring)(ref linearLimitSpring)).spring = Cart.m_spring; ((SoftJointLimitSpring)(ref linearLimitSpring)).damper = Cart.m_springDamping; joint2.linearLimitSpring = linearLimitSpring; ((Joint)_joint).breakForce = float.PositiveInfinity; GameApi.CartJoint.Invoke(Cart) = _joint; GameApi.CartObject.Invoke(Cart) = ((Component)_animal).gameObject; View.GetZDO().Set(ZDOVars.s_attachJointHash, true); Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren<Collider>(); foreach (Collider val2 in componentsInChildren) { Collider[] componentsInChildren2 = ((Component)_animal).GetComponentsInChildren<Collider>(); foreach (Collider val3 in componentsInChildren2) { if (!val2.isTrigger && !val3.isTrigger && !Physics.GetIgnoreCollision(val2, val3)) { Physics.IgnoreCollision(val2, val3, true); _ignored.Add((val2, val3)); } } } } internal void Disconnect() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)View) && View.IsValid() && View.IsOwner()) { ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Animal"); View.GetZDO().Set("CTA.Animal", ZDOID.None); View.GetZDO().Set("CTA.Driver", ZDOID.None); View.GetZDO().Set(ZDOVars.s_attachJointHash, false); GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(zDOID) : null); ZNetView val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ZNetView>() : null); if (Object.op_Implicit((Object)(object)val2) && val2.IsValid()) { val2.InvokeRPC("CTA.ReleaseCart", new object[1] { View.GetZDO().m_uid }); } } ClearLocal(); } private void ClearLocal() { if (Object.op_Implicit((Object)(object)_joint)) { if (Object.op_Implicit((Object)(object)Cart) && (Object)(object)GameApi.CartJoint.Invoke(Cart) == (Object)(object)_joint) { GameApi.CartJoint.Invoke(Cart) = null; GameApi.CartObject.Invoke(Cart) = null; } Object.Destroy((Object)(object)_joint); } _joint = null; if (Object.op_Implicit((Object)(object)_anchor)) { Object.Destroy((Object)(object)((Component)_anchor).gameObject); } _anchor = null; foreach (var item in _ignored) { if (Object.op_Implicit((Object)(object)item.cart) && Object.op_Implicit((Object)(object)item.animal)) { Physics.IgnoreCollision(item.cart, item.animal, false); } } _ignored.Clear(); } private void OnDestroy() { ClearLocal(); Instances.Remove(this); } } internal static class CreatureRules { internal const string ExtraDefaults = "Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring"; internal const string WorkingDefaults = "Boar,Wolf,Lox,Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring"; internal static HashSet<string> Names(string value) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = (value ?? "").Split(','); foreach (string text in array) { if (text.Trim().Length > 0) { hashSet.Add(text.Trim()); } } return hashSet; } internal static Dictionary<string, string[]> Foods(string value, Action<string> warn) { Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal); string[] array = (value ?? "").Split(';'); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text)) { string[] array2 = text.Split('='); if (array2.Length != 2 || array2[0].Trim().Length == 0 || Names(array2[1]).Count == 0) { warn("Invalid food rule: " + text); } else { dictionary[array2[0].Trim()] = new List<string>(Names(array2[1])).ToArray(); } } } return dictionary; } internal static Dictionary<string, float[]> Offsets(string value, Action<string> warn) { Dictionary<string, float[]> dictionary = new Dictionary<string, float[]>(StringComparer.Ordinal); string[] array = (value ?? "").Split(';'); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text)) { string[] array2 = text.Split('='); string[] array3 = ((array2.Length == 2) ? array2[1].Split(',') : Array.Empty<string>()); float[] array4 = new float[3]; bool flag = array2.Length == 2 && array2[0].Trim().Length > 0 && array3.Length == 3; int num = 0; while (flag && num < 3) { flag = float.TryParse(array3[num], NumberStyles.Float, CultureInfo.InvariantCulture, out array4[num]) && !float.IsNaN(array4[num]) && !float.IsInfinity(array4[num]) && Math.Abs(array4[num]) <= 15f; num++; } if (flag) { dictionary[array2[0].Trim()] = array4; } else { warn("Invalid riding offset: " + text); } } } return dictionary; } } [HarmonyPatch(typeof(MonsterAI), "Start")] internal static class CreatureStartPatch { [HarmonyPostfix] private static void Postfix(MonsterAI __instance) { AnimalCompanion.AddTo(__instance); } } [HarmonyPatch(typeof(MonsterAI), "MakeTame")] internal static class TamedPersistencePatch { [HarmonyPostfix] private static void Postfix(MonsterAI __instance) { AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>(); if (Object.op_Implicit((Object)(object)component) && component.View.IsOwner()) { ((BaseAI)__instance).SetHuntPlayer(false); __instance.SetDespawnInDay(false); __instance.SetEventCreature(false); } } } [HarmonyPatch(typeof(MonsterAI), "UpdateTarget")] internal static class HaulingTargetPatch { [HarmonyPostfix] private static void Postfix(MonsterAI __instance) { AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>(); if (Object.op_Implicit((Object)(object)component) && component.Character.IsTamed() && (component.IsHauling || component.Tame.HaveRider())) { GameApi.TargetCreature.Invoke(__instance) = null; GameApi.TargetStatic.Invoke(__instance) = null; } } } [HarmonyPatch(typeof(Tameable), "GetHoverText")] internal static class CreatureHoverPatch { [HarmonyPostfix] private static void Postfix(Tameable __instance, ref string __result) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>(); if (Object.op_Implicit((Object)(object)component) && component.Character.IsTamed()) { __result += "\nPet to toggle follow / stay"; if (component.CanRide) { __result = __result + "\n[<color=yellow>" + ((object)Plugin.Settings.RideKey/*cast due to .constrained prefix*/).ToString() + "</color>] Ride"; } if (component.CanPull) { __result = __result + "\n[<color=yellow>" + ((object)Plugin.Settings.HitchKey/*cast due to .constrained prefix*/).ToString() + "</color>] Select for cart"; } } } } [HarmonyPatch(typeof(Sadle), "UpdateRiding")] internal static class BarebackStopPatch { [HarmonyPostfix] private static void Postfix(Sadle __instance, ref bool __result) { AnimalCompanion componentInParent = ((Component)__instance).GetComponentInParent<AnimalCompanion>(); if (!__result && Object.op_Implicit((Object)(object)componentInParent) && componentInParent.OwnSaddle && componentInParent.Character.IsTamed() && __instance.HaveValidUser()) { ((BaseAI)componentInParent.Ai).StopMoving(); __result = true; } } } [HarmonyPatch(typeof(Tameable), "DropSaddle")] internal static class BarebackDropPatch { [HarmonyPrefix] private static bool Prefix(Tameable __instance, ref bool __result) { AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>(); if (!Object.op_Implicit((Object)(object)component) || !component.OwnSaddle) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Vagon), "Awake")] internal static class CartAwakePatch { [HarmonyPostfix] private static void Postfix(Vagon __instance) { if (Plugin.Settings.Carts && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<CartHarness>())) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { ((Component)__instance).gameObject.AddComponent<CartHarness>(); } } } } [HarmonyPatch(typeof(Vagon), "GetHoverText")] internal static class CartHoverPatch { [HarmonyPostfix] private static void Postfix(Vagon __instance, ref string __result) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); if (Object.op_Implicit((Object)(object)component)) { __result = __result + "\n[<color=yellow>" + ((object)Plugin.Settings.HitchKey/*cast due to .constrained prefix*/).ToString() + "</color>] " + (component.HasLink ? "Detach animal" : "Hitch selected animal"); } } } [HarmonyPatch(typeof(Vagon), "Interact")] internal static class CartInteractPatch { [HarmonyPrefix] private static bool Prefix(Vagon __instance, Humanoid character, bool hold, ref bool __result) { CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); if (!Object.op_Implicit((Object)(object)component) || !component.HasLink) { return true; } if (!hold) { Player val = (Player)(object)((character is Player) ? character : null); if (val != null) { component.RequestDetach(val); } } __result = !hold; return false; } } [HarmonyPatch(typeof(Vagon), "InUse")] internal static class CartInUsePatch { [HarmonyPostfix] private static void Postfix(Vagon __instance, ref bool __result) { CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); __result |= Object.op_Implicit((Object)(object)component) && component.HasLink; } } [HarmonyPatch(typeof(Vagon), "CanAttach")] internal static class CartCanAttachPatch { [HarmonyPrefix] private static bool Prefix(Vagon __instance, GameObject go, ref bool __result) { CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsOurAnchor(go)) { return true; } __result = component.IsConnected; return false; } } [HarmonyPatch(typeof(Vagon), "Detach")] internal static class CartDetachPatch { [HarmonyPrefix] private static bool Prefix(Vagon __instance) { CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); if (Object.op_Implicit((Object)(object)component)) { return !component.HasLink; } return true; } } [HarmonyPatch(typeof(Vagon), "LateUpdate")] internal static class CartRopePatch { [HarmonyPostfix] private static void Postfix(Vagon __instance, LineRenderer ___m_lineRenderer) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); if (Object.op_Implicit((Object)(object)component) && component.HasLink && Object.op_Implicit((Object)(object)___m_lineRenderer)) { GameObject val = ZNetScene.instance.FindInstance(component.View.GetZDO().GetZDOID("CTA.Animal")); AnimalCompanion animalCompanion = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<AnimalCompanion>() : null); if (Object.op_Implicit((Object)(object)animalCompanion) && animalCompanion.CanPull && Object.op_Implicit((Object)(object)__instance.m_lineAttachPoints0) && Object.op_Implicit((Object)(object)__instance.m_lineAttachPoints1)) { ((Renderer)___m_lineRenderer).enabled = true; ___m_lineRenderer.SetPosition(0, __instance.m_lineAttachPoints0.position); ___m_lineRenderer.SetPosition(1, animalCompanion.HitchPosition); ___m_lineRenderer.SetPosition(2, __instance.m_lineAttachPoints1.position); } } } } [HarmonyPatch(typeof(WearNTear), "ApplyDamage")] internal static class AttachedCartDamagePatch { [HarmonyPrefix] private static bool Prefix(WearNTear __instance, ref bool __result) { CartHarness component = ((Component)__instance).GetComponent<CartHarness>(); if (!Object.op_Implicit((Object)(object)component) || !component.Protects) { return true; } __result = false; return false; } } [BepInPlugin("com.computer.tamedanimals", "Computer-TamedAnimals", "0.1.0")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "com.computer.tamedanimals"; public const string Name = "Computer-TamedAnimals"; public const string Version = "0.1.0"; internal static Settings Settings; internal static ManualLogSource Log; private Harmony _harmony; private AnimalCompanion _selected; private float _selectionExpires; private void Awake() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Settings = new Settings(((BaseUnityPlugin)this).Config); if (!Settings.Enabled) { return; } try { RuntimeHelpers.RunClassConstructor(typeof(GameApi).TypeHandle); _harmony = new Harmony("com.computer.tamedanimals"); _harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Computer-TamedAnimals 0.1.0 loaded. Ride: " + ((object)Settings.RideKey/*cast due to .constrained prefix*/).ToString() + "; hitch: " + ((object)Settings.HitchKey/*cast due to .constrained prefix*/).ToString())); } catch (Exception ex) { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("Could not patch this Valheim version; mod disabled. " + ex)); ((Behaviour)this).enabled = false; } } private unsafe void Update() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0225: 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_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Settings.Enabled || !Object.op_Implicit((Object)(object)localPlayer) || !GameApi.TakeInput(localPlayer)) { return; } if (Object.op_Implicit((Object)(object)_selected) && Time.time > _selectionExpires) { _selected = null; } KeyboardShortcut val; if (Settings.Riding) { val = Settings.RideKey; if (((KeyboardShortcut)(ref val)).IsDown()) { if (localPlayer.GetDoodadController() is Sadle) { localPlayer.StopDoodadControl(); return; } if (((Character)localPlayer).IsAttached() || localPlayer.GetDoodadController() != null) { return; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); AnimalCompanion animalCompanion = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<AnimalCompanion>() : null); if (Object.op_Implicit((Object)(object)animalCompanion) && animalCompanion.CanRide && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)animalCompanion).transform.position) <= 8f) { animalCompanion.Tame.m_saddle.Interact((Humanoid)(object)localPlayer, false, false); } else { Say("Look at a nearby tamed, rideable creature."); } } } if (!Settings.Carts) { return; } val = Settings.HitchKey; if (!((KeyboardShortcut)(ref val)).IsDown() || ((Character)localPlayer).IsAttached()) { return; } GameObject hoverObject2 = ((Humanoid)localPlayer).GetHoverObject(); if (!Object.op_Implicit((Object)(object)hoverObject2)) { _selected = null; Say("Look at a tame, then the cart shafts."); return; } CartHarness componentInParent = hoverObject2.GetComponentInParent<CartHarness>(); if (Object.op_Implicit((Object)(object)componentInParent)) { if (componentInParent.HasLink) { componentInParent.RequestDetach(localPlayer); } else if (Object.op_Implicit((Object)(object)_selected) && _selected.CanPull) { _selected.RequestCart(componentInParent, localPlayer); } else { val = Settings.HitchKey; Say("First select a tamed creature with " + ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString() + "."); } _selected = null; return; } AnimalCompanion componentInParent2 = hoverObject2.GetComponentInParent<AnimalCompanion>(); if (!Object.op_Implicit((Object)(object)componentInParent2) || !componentInParent2.CanPull) { Say("This creature cannot pull a cart."); return; } _selected = componentInParent2; _selectionExpires = Time.time + 30f; string hoverName = componentInParent2.Tame.GetHoverName(); val = Settings.HitchKey; Say(hoverName + " selected. Look at nearby cart shafts and press " + ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString() + "."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static void Warn(string text) { Log.LogWarning((object)text); } internal static void Say(string text) { if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false); } } } internal static class GameApi { internal static readonly FieldRef<BaseAI, Tameable> AiTame = AccessTools.FieldRefAccess<BaseAI, Tameable>("m_tamable"); internal static readonly FieldRef<Vagon, ConfigurableJoint> CartJoint = AccessTools.FieldRefAccess<Vagon, ConfigurableJoint>("m_attachJoin"); internal static readonly FieldRef<Vagon, GameObject> CartObject = AccessTools.FieldRefAccess<Vagon, GameObject>("m_attachedObject"); internal static readonly FieldRef<MonsterAI, Character> TargetCreature = AccessTools.FieldRefAccess<MonsterAI, Character>("m_targetCreature"); internal static readonly FieldRef<MonsterAI, StaticTarget> TargetStatic = AccessTools.FieldRefAccess<MonsterAI, StaticTarget>("m_targetStatic"); internal static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.DeclaredMethod(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true); internal static Player Requester(long sender, ZDOID playerId, Vector3 position, float range = 10f) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(playerId) : null); Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Player>() : null); if (!Object.op_Implicit((Object)(object)val2) || ((Character)val2).GetOwner() != sender || ((Character)val2).IsDead() || ((Character)val2).IsTeleporting() || !(Vector3.Distance(((Component)val2).transform.position, position) <= range)) { return null; } return val2; } } internal sealed class Settings { internal readonly HashSet<string> Extra; internal readonly HashSet<string> Riders; internal readonly HashSet<string> Pullers; internal readonly Dictionary<string, string[]> Foods; internal readonly Dictionary<string, float[]> Offsets; internal readonly bool Enabled; internal readonly bool Riding; internal readonly bool Carts; internal readonly bool Protection; internal readonly float TamingTime; internal readonly float FedTime; internal readonly float HitchRange; internal readonly float BreakDistance; internal readonly KeyboardShortcut RideKey; internal readonly KeyboardShortcut HitchKey; internal Settings(ConfigFile config) { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Expected O, but got Unknown //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) Enabled = Bind<bool>("General", "Enabled", value: true, "Enable the mod. All peers need the same mod and settings."); Extra = CreatureRules.Names(Bind<string>("Taming", "AdditionalCreatures", "Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring", "Comma-separated prefab names. Requires a ground Humanoid with MonsterAI; bosses, flying and aquatic-only creatures are excluded.")); Foods = CreatureRules.Foods(Bind<string>("Taming", "FoodRules", "Neck=RawMeat,FishRaw;Greyling=Raspberry,Blueberries,Mushroom;Greydwarf=Raspberry,Blueberries,Mushroom;Greydwarf_Elite=Raspberry,Blueberries,Mushroom;Troll=RawMeat,DeerMeat;Ulv=RawMeat,WolfMeat;Fenring=RawMeat,WolfMeat", "Prefab=foodPrefab,foodPrefab;Prefab=... . Used only for newly tameable creatures; existing taming and diets are preserved."), Plugin.Warn); Offsets = CreatureRules.Offsets(Bind<string>("Riding", "SeatOffsets", "Boar=0,0.85,-0.1;Wolf=0,1.05,-0.15;Neck=0,0.45,0;Greyling=0,1.2,-0.15;Greydwarf=0,1.5,-0.2;Greydwarf_Elite=0,2,-0.25;Troll=0,4.2,-0.3;Ulv=0,1,-0.15;Fenring=0,1.8,-0.2", "Local seat coordinates (x,y,z), using decimal dots. Unlisted creatures use their capsule collider. Existing saddles keep their own seats."), Plugin.Warn); TamingTime = config.Bind<float>("Taming", "TamingSeconds", 1800f, new ConfigDescription("Calm, fed time for added tameables. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 86400f), Array.Empty<object>())).Value; FedTime = config.Bind<float>("Taming", "FedSeconds", 600f, new ConfigDescription("How long food lasts for added tameables. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 86400f), Array.Empty<object>())).Value; Riding = Bind<bool>("Riding", "Enabled", value: true, "Enable bareback riding for configured creatures without existing saddles."); Riders = CreatureRules.Names(Bind<string>("Riding", "Creatures", "Boar,Wolf,Lox,Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring", "Rideable prefab names. Existing saddles still require their normal saddle item.")); Carts = Bind<bool>("Carts", "Enabled", value: true, "Enable attaching carts to tamed creatures."); Pullers = CreatureRules.Names(Bind<string>("Carts", "Creatures", "Boar,Wolf,Lox,Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring", "Prefab names allowed to pull carts.")); Protection = Bind<bool>("Carts", "PreventAttachedDamage", value: true, "Block cart damage only while its live animal hitch is intact. Does not protect the animal or prevent hammer removal."); HitchRange = config.Bind<float>("Carts", "HitchRange", 6f, new ConfigDescription("Maximum distance from cart shafts to animal hitch when connecting. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 10f), Array.Empty<object>())).Value; BreakDistance = config.Bind<float>("Carts", "BreakDistance", 10f, new ConfigDescription("Disconnect if the cart gets stuck this far behind its hitch. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 20f), Array.Empty<object>())).Value; RideKey = Bind<KeyboardShortcut>("Controls", "RideKey", new KeyboardShortcut((KeyCode)114, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Look at a tame to mount; press again to dismount. Native riding controls apply."); HitchKey = Bind<KeyboardShortcut>("Controls", "HitchKey", new KeyboardShortcut((KeyCode)104, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Look at a tame to select it, then at cart shafts to hitch. On a hitched cart, detach instead."); T Bind<T>(string section, string key, T value, string description) { return config.Bind<T>(section, key, value, description + " Restart Valheim after changing this setting.").Value; } } } }