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 ValBallCompatibility v0.1.2
plugins/ValBallCompatibility/ValBallCompatibility.dll
Decompiled 5 days agousing System; using System.Collections; 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.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("ValBallCompatibility")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("ValBallCompatibility")] [assembly: AssemblyTitle("ValBallCompatibility")] [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 ValBallCompatibility { internal static class Cargo { private sealed class Cached { internal string Data; internal Inventory Inventory; internal bool Failed; } [ThreadStatic] private static int weightDepth; private static readonly ConditionalWeakTable<ItemData, Cached> cache = new ConditionalWeakTable<ItemData, Cached>(); private static Type portalType; private static PropertyInfo portalSynced; private static FieldInfo portalAllowed; private static bool portalVerified; private static Inventory Get(ItemData item, string data) { Cached orCreateValue = cache.GetOrCreateValue(item); if (orCreateValue.Data != data) { orCreateValue.Data = data; orCreateValue.Inventory = null; orCreateValue.Failed = true; orCreateValue.Inventory = Load(data); orCreateValue.Failed = false; } if (orCreateValue.Failed || orCreateValue.Inventory == null) { throw new InvalidOperationException("Invalid cached horse cargo."); } return orCreateValue.Inventory; } internal static Inventory Load(string text) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown if (text.Length > 2097152) { throw new InvalidOperationException("Cargo size exceeds limit."); } byte[] array = Convert.FromBase64String(text); if (array.Length > 1048576) { throw new InvalidOperationException("Cargo size exceeds limit."); } int num; using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(array))) { num = ((binaryReader.ReadInt32() >= 108) ? binaryReader.ReadUInt16() : binaryReader.ReadInt32()); } if (num < 0 || num > 256) { throw new InvalidOperationException("Cargo item count exceeds limit."); } Inventory val = new Inventory("Transport saddlebags", (Sprite)null, 16, 16); val.Load(new ZPackage(array)); if (val.GetAllItems().Count != num) { throw new InvalidOperationException("Cargo contains missing or unreadable items."); } return val; } internal static bool Has(ItemData item, out string data) { data = null; if (item?.m_customData != null && item.m_customData.ContainsKey("valball.prefab") && item.m_customData.TryGetValue("vercadi.valball.cargo", out data)) { return !string.IsNullOrEmpty(data); } return false; } internal static void Weight(ItemData __instance, ref float __result) { if (!Has(__instance, out var data)) { return; } if (++weightDepth > 4) { weightDepth--; __result = 100000f; return; } try { __result += Get(__instance, data).GetTotalWeight(); } catch { __result = 100000f; } finally { weightDepth--; } } internal static void Teleportable(Inventory __instance, bool __0, ref bool __result) { if (!__result) { return; } bool all = __0 || (Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.GetGlobalKey((GlobalKeys)35)); try { foreach (ItemData allItem in __instance.GetAllItems()) { if (Has(allItem, out var data) && !ContentsPortable(Get(allItem, data), all, 0)) { __result = false; break; } } } catch { __result = false; } } private static bool ContentsPortable(Inventory inv, bool all, int depth) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown if (depth > 4) { return false; } foreach (ItemData allItem in inv.GetAllItems()) { if (allItem?.m_shared == null || allItem.m_shared.m_toolTier >= 1000) { return false; } if (!all && !allItem.m_shared.m_teleportable && !ServerAllows(allItem)) { return false; } if (Has(allItem, out var data) && !ContentsPortable(Get(allItem, data), all, depth + 1)) { return false; } if (!Chainloader.PluginInfos.TryGetValue("vapok.mods.adventurebackpacks", out var value)) { continue; } Type type = ((object)value.Instance).GetType().Assembly.GetType("AdventureBackpacks.API.ABAPI", throwOnError: true); MethodInfo methodInfo = AccessTools.Method(type, "IsBackpack", new Type[1] { typeof(ItemData) }, (Type[])null); if (methodInfo != null && (bool)methodInfo.Invoke(null, new object[1] { allItem })) { Inventory val = (Inventory)AccessTools.Method(type, "GetBackpackInventory", new Type[1] { typeof(ItemData) }, (Type[])null).Invoke(null, new object[1] { allItem }); if (val == null || !ContentsPortable(val, all, depth + 1)) { return false; } } } return true; } private static bool ServerAllows(ItemData item) { if (!Object.op_Implicit((Object)(object)item.m_dropPrefab) || !Chainloader.PluginInfos.TryGetValue("vercadi.AdventureBackpackCompatibility", out var value)) { return false; } Type type = ((object)value.Instance).GetType(); if (portalType != type) { portalType = type; portalVerified = false; Native.Verify(type.Assembly, "87929803694672352059BCE6595CEE4CF7DE424FB5776F067F23529C1896AF4A"); portalSynced = AccessTools.Property(type, "Synced"); portalAllowed = AccessTools.Field(type, "allowed"); portalVerified = portalSynced != null && portalAllowed != null; } if (!portalVerified || !(bool)portalSynced.GetValue(value.Instance, null)) { return false; } return ((HashSet<string>)portalAllowed.GetValue(value.Instance)).Contains(Rules.Name(((Object)item.m_dropPrefab).name)); } } internal sealed class Journal { internal readonly string PathName; private readonly string snapshot; internal Journal(string operation, ItemData ball, string animalId) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown string text = Path.Combine(Paths.BepInExRootPath, "ValBallRecovery"); Directory.CreateDirectory(text); Inventory val = new Inventory("Recovery", (Sprite)null, 1, 1); if (!val.AddItem(ball.Clone())) { throw new InvalidOperationException("Cannot stage recovery item."); } ZPackage val2 = new ZPackage(); val.Save(val2); snapshot = "ValBallCompatibility 0.1.0\nOperation=" + operation + "\nAnimal=" + animalId + "\nInventory=" + val2.GetBase64() + "\n"; PathName = Path.Combine(text, DateTime.UtcNow.ToString("yyyyMMddTHHmmssfff") + "-" + Guid.NewGuid().ToString("N") + ".txt"); Phase("prepared"); } internal void Phase(string phase) { byte[] bytes = Encoding.UTF8.GetBytes(snapshot + "Phase=" + phase + "\n"); using FileStream fileStream = new FileStream(PathName, FileMode.Create, FileAccess.Write, FileShare.Read); fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(flushToDisk: true); } internal void Complete() { File.Delete(PathName); } } internal sealed class Native { internal const string Hash = "0D81F59CD0B57FD038460F679DEA164B9C077A0B6E6111A641AE9D57222B3AF0"; internal readonly Type Projectile; internal readonly Type Capture; internal readonly Type Captured; internal readonly Type Plugin; internal readonly BaseUnityPlugin Mod; internal readonly SkillType Skill; internal Native() { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Mod = Chainloader.PluginInfos["gokbe.valball"].Instance; Verify(((object)Mod).GetType().Assembly, "0D81F59CD0B57FD038460F679DEA164B9C077A0B6E6111A641AE9D57222B3AF0"); Assembly assembly = ((object)Mod).GetType().Assembly; Projectile = assembly.GetType("ValBall.ValBallProjectile", throwOnError: true); Capture = assembly.GetType("ValBall.CaptureRules", throwOnError: true); Captured = assembly.GetType("ValBall.CapturedCreature", throwOnError: true); Plugin = assembly.GetType("ValBall.ValBallPlugin", throwOnError: true); Skill = (SkillType)AccessTools.Field(Plugin, "ValMasterSkill").GetValue(null); string[] array = new string[3] { "Capture", "Release", "OnHit" }; foreach (string text in array) { if (AccessTools.Method(Projectile, text, (Type[])null, (Type[])null) == null) { throw new MissingMethodException(text); } } } internal static void Verify(Assembly a, string expected) { using FileStream inputStream = File.OpenRead(a.Location); using SHA256 sHA = SHA256.Create(); if (BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "") != expected) { throw new NotSupportedException("Unreviewed " + a.GetName().Name + " binary."); } } internal static object Field(object o, string name) { return AccessTools.Field(o.GetType(), name).GetValue(o); } internal static T Read<T>(object o, string name) { return (T)Field(o, name); } internal static object Call(Type t, string name, object o, params object[] args) { return AccessTools.Method(t, name, (Type[])null, (Type[])null).Invoke(o, args); } internal ItemData Item(string prefab) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown return (ItemData)Call(Plugin, "NewItem", null, prefab); } internal object Snapshot(Character c) { return Call(Captured, "FromCharacter", null, c); } internal void Write(object snapshot, ItemData item) { Call(Captured, "Write", snapshot, item); } internal void Timers(object snapshot, ZDO zdo) { Call(Captured, "ApplyTimers", snapshot, zdo); } internal void Xp(Player p, float amount) { Call(Capture, "AwardXp", null, p, amount); } internal T Config<T>(string section, string key) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return ((ConfigEntry<T>)(object)Mod.Config[new ConfigDefinition(section, key)]).Value; } internal object Tier(object projectile) { return Field(projectile, "m_tier"); } internal ItemData Empty(object projectile) { return Item(Read<string>(Tier(projectile), "EmptyPrefab")); } internal ItemData Full(object projectile) { return Item(Read<string>(Tier(projectile), "FullPrefab")); } internal float Multiplier(object projectile) { return Read<ConfigEntry<float>>(Tier(projectile), "Multiplier").Value; } } internal static class ExtraState { private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; internal const string HorseId = "rae_OdinHorse"; internal static readonly string[] AiBools = new string[3] { "huntplayer", "spawnday", "spawnnight" }; internal static readonly FieldInfo HorseCache = FindHorseCache(); private static FieldInfo FindHorseCache() { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (!((Object)(object)value.Instance == (Object)null)) { Type type = ((object)value.Instance).GetType().Assembly.GetType("OdinHorse.HorseSaddlebags"); if (type != null) { Native.Verify(type.Assembly, "424B2406AC4C4F024E57C349277E3A4DA03AB95802C23A955247167B3751A99D"); return AccessTools.Field(type, "s_containerCache") ?? throw new MissingFieldException("Horse cache"); } } } return null; } internal static Container CachedHorseBag(Character c) { //IL_0039: 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) ZNetView component = ((Component)c).GetComponent<ZNetView>(); if (HorseCache?.GetValue(null) is IDictionary dictionary && Object.op_Implicit((Object)(object)component) && component.IsValid() && dictionary.Contains(component.GetZDO().m_uid)) { object? obj = dictionary[component.GetZDO().m_uid]; return (Container)((obj is Container) ? obj : null); } return null; } internal static string Guard(Character c) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown Tameable component = ((Component)c).GetComponent<Tameable>(); if (Object.op_Implicit((Object)(object)component) && (component.m_unsummonDistance > 0f || component.m_unsummonOnOwnerLogoutSeconds > 0f)) { return "Summoned creatures cannot be transported."; } if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.m_saddle) && component.m_saddle.HaveValidUser()) { return "Dismount before capturing this animal."; } foreach (Vagon item in (IEnumerable)AccessTools.Field(typeof(Vagon), "m_instances").GetValue(null)) { Vagon val = item; if (Object.op_Implicit((Object)(object)val) && val.IsAttached(c)) { return "Detach the cart before capturing this animal."; } } Container[] componentsInChildren = ((Component)c).GetComponentsInChildren<Container>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (componentsInChildren[i].IsInUse()) { return "Close the animal's storage first."; } } Container val2 = CachedHorseBag(c); if (Object.op_Implicit((Object)(object)val2) && val2.IsInUse()) { return "Close the horse's saddlebags first."; } return null; } internal static void Save(Character c, ItemData item) { //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Expected O, but got Unknown ZDO zDO = ((Component)c).GetComponent<ZNetView>().GetZDO(); Dictionary<string, string> customData = item.m_customData; customData["vercadi.valball.version"] = "1"; customData["vercadi.valball.maxHealth"] = c.GetMaxHealth().ToString("R", Invariant); customData["vercadi.valball.bred"] = (zDO.GetBool("vercadi.valball.bred", false) ? "1" : "0"); customData["vercadi.valball.oceanDuration"] = zDO.GetFloat("vercadi.valball.oceanDuration", 0f).ToString("R", Invariant); Tameable component = ((Component)c).GetComponent<Tameable>(); if (Object.op_Implicit((Object)(object)component)) { customData["vercadi.valball.tameRemaining"] = zDO.GetFloat(ZDOVars.s_tameTimeLeft, component.m_tamingTime).ToString("R", Invariant); } BaseAI component2 = ((Component)c).GetComponent<BaseAI>(); if (Object.op_Implicit((Object)(object)component2)) { string[] array = new string[2] { "m_despawnInDay", "m_eventCreature" }; foreach (string text in array) { FieldInfo fieldInfo = AccessTools.Field(((object)component2).GetType(), text); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { customData["vercadi.valball." + text] = (((bool)fieldInfo.GetValue(component2)) ? "1" : "0"); } } } string text2 = Rules.Name(((Object)((Component)c).gameObject).name); if (!(text2 == "rae_OdinHorse") && !(text2 == "rae_Offspring_Normal")) { return; } if (HorseCache == null) { throw new NotSupportedException("Reviewed OdinHorse adapter is unavailable."); } customData["vercadi.valball.coat"] = zDO.GetInt("HorseMaterial", -1).ToString(Invariant); customData["vercadi.valball.armor"] = (zDO.GetBool("HaveArmor", false) ? "1" : "0"); customData["vercadi.valball.waiting"] = (zDO.GetBool("IsWaiting", false) ? "1" : "0"); Container val = CachedHorseBag(c); if (Object.op_Implicit((Object)(object)val)) { ZPackage val2 = new ZPackage(); val.GetInventory().Save(val2); if (val2.GetBase64() != zDO.GetString("rae_saddlebags", "") && val.GetInventory().NrOfItems() > 0) { throw new InvalidOperationException("Saddlebag cache differs from network state. Rejoin before capturing this horse."); } } string text3 = zDO.GetString("rae_saddlebags", ""); if (text3.Length > 2097152) { throw new InvalidDataException("Horse cargo exceeds transport bound."); } if (text3.Length > 0) { Cargo.Load(text3); } customData["vercadi.valball.cargo"] = text3; } internal static void Restore(Character c, ItemData item) { Dictionary<string, string> customData = item.m_customData; ZNetView component = ((Component)c).GetComponent<ZNetView>(); ZDO zDO = component.GetZDO(); if (!customData.ContainsKey("vercadi.valball.version")) { return; } zDO.Set("vercadi.valball.bred", customData.TryGetValue("vercadi.valball.bred", out var value) && value == "1"); zDO.Set("vercadi.valball.oceanDuration", Rules.Number(customData, "vercadi.valball.oceanDuration")); if (customData.ContainsKey("vercadi.valball.tameRemaining")) { zDO.Set(ZDOVars.s_tameTimeLeft, Math.Max(0f, Rules.Number(customData, "vercadi.valball.tameRemaining"))); } BaseAI component2 = ((Component)c).GetComponent<BaseAI>(); if (Object.op_Implicit((Object)(object)component2)) { string[] array = new string[2] { "m_despawnInDay", "m_eventCreature" }; foreach (string text in array) { FieldInfo fieldInfo = AccessTools.Field(((object)component2).GetType(), text); if (!(fieldInfo != null) || !customData.TryGetValue("vercadi.valball." + text, out var value2)) { continue; } MonsterAI val = (MonsterAI)(object)((component2 is MonsterAI) ? component2 : null); if (val != null) { if (text == "m_eventCreature") { val.SetEventCreature(value2 == "1"); } else { val.SetDespawnInDay(value2 == "1"); } } else { fieldInfo.SetValue(component2, value2 == "1"); } } } if (customData.TryGetValue("vercadi.valball.coat", out var value3)) { zDO.Set("HorseMaterial", int.Parse(value3, Invariant)); zDO.Set("rae_saddlebags", customData.TryGetValue("vercadi.valball.cargo", out var value4) ? value4 : ""); string value5; bool flag = customData.TryGetValue("vercadi.valball.armor", out value5) && value5 == "1"; string value6; bool flag2 = customData.TryGetValue("vercadi.valball.waiting", out value6) && value6 == "1"; zDO.Set("HaveArmor", flag); zDO.Set("IsWaiting", flag2); if (Rules.Name(((Object)((Component)c).gameObject).name) == "rae_OdinHorse") { component.InvokeRPC(ZNetView.Everybody, "SetArmor", new object[1] { flag }); component.InvokeRPC(ZNetView.Everybody, "SetWait", new object[1] { flag2 }); } } float num = Rules.Number(customData, "vercadi.valball.maxHealth"); if (num > 0f && num < 10000000f) { c.SetMaxHealth(num); } } } [BepInPlugin("vercadi.ValBallCompatibility", "ValBall Compatibility", "0.1.0")] [BepInDependency("gokbe.valball", "1.6.0")] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.9")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "vercadi.ValBallCompatibility"; public const string Version = "0.1.0"; private static Plugin instance; private Native native; private Harmony harmony; private ConfigSync sync; private ConfigEntry<string> progression; private Dictionary<string, Species> species = new Dictionary<string, Species>(); private bool ready; private bool faulted; [ThreadStatic] private static int breeding; private bool Synced { get { if (Object.op_Implicit((Object)(object)ZNet.instance)) { if (!ZNet.instance.IsServer()) { if (((ConditionalConfigSync)sync).InitialSyncDone) { return !((ConditionalConfigSync)sync).IsSourceOfTruth; } return false; } return true; } return false; } } private void Awake() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Expected O, but got Unknown //IL_0321: Expected O, but got Unknown //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Expected O, but got Unknown //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Expected O, but got Unknown instance = this; harmony = new Harmony("vercadi.ValBallCompatibility"); progression = ((BaseUnityPlugin)this).Config.Bind<string>("Capture", "Species progression", Rules.DefaultProgression, "Exact reviewed prefab:ValMasterLevel:worldBossKey entries separated by semicolons. Omit a species to disable new capture. Already-filled balls remain releasable. Already-tamed supported pets bypass skill and boss gates."); sync = new ConfigSync("vercadi.ValBallCompatibility") { DisplayName = "ValBall Compatibility", CurrentVersion = "0.1.0", MinimumRequiredVersion = "0.1.0", ModRequired = true }; ((ConditionalConfigSync)sync).AddLockingConfigEntry<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock server settings", true, "Enforce this helper and ValBall gameplay settings from the server. Map/UI preferences remain local.")); ((ConditionalConfigSync)sync).AddConfigEntry<string>(progression, (ConfigSyncMode)0); progression.SettingChanged += delegate { ReadRules(); }; ReadRules(); try { Type type = ((object)Chainloader.PluginInfos["gokbe.valball"].Instance).GetType().Assembly.GetType("ValBall.ValBallProjectile", throwOnError: true); Patch(type, "OnHit", "OnHitGuard"); native = new Native(); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in native.Mod.Config) { if (!(item.Key.Section == "Map") && !(item.Key.Section == "Debug") && !(item.Key.Key == "ShowChanceLabel")) { typeof(ConfigSync).GetMethods().Single((MethodInfo m) => m.Name == "AddConfigEntry" && m.IsGenericMethodDefinition && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType == typeof(ConfigSyncMode)).MakeGenericMethod(item.Value.SettingType) .Invoke(sync, new object[2] { item.Value, (object)(ConfigSyncMode)0 }); } } Patch(native.Projectile, "Capture", "Capture"); Patch(native.Projectile, "Release", "Release"); Patch(native.Capture, "GetLabel", "Label"); Patch(typeof(Tameable), "TamingUpdate", "Taming"); Patch(typeof(Character), "Awake", null, "Spawned"); Tuple<Type, string>[] array = new Tuple<Type, string>[2] { Tuple.Create(typeof(Procreation), "Procreate"), Tuple.Create(typeof(Growup), "GrowUpdate") }; foreach (Tuple<Type, string> tuple in array) { harmony.Patch((MethodBase)AccessTools.Method(tuple.Item1, tuple.Item2, (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BreedingStart", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "BreedingEnd", (Type[])null), (HarmonyMethod)null); } harmony.Patch((MethodBase)AccessTools.Method(typeof(ItemData), "GetWeight", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Cargo), "Weight", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Inventory), "IsTeleportable", new Type[1] { typeof(bool) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Cargo), "Teleportable", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"ACTIVE: reviewed ValBall 1.6.0 capture rules, animal state, horse cargo and aquatic release guards. Multiplayer gameplay validation pending."); } catch (Exception ex) { ready = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Capture helper initialization failed. Do not throw ValBalls until this is resolved. " + ex)); } } private void Patch(Type type, string method, string prefix = null, string postfix = null) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) harmony.Patch((MethodBase)(AccessTools.Method(type, method, (Type[])null, (Type[])null) ?? throw new MissingMethodException(type.FullName, method)), (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private void ReadRules() { try { species = Rules.Parse(progression.Value); } catch (Exception ex) { species.Clear(); ((BaseUnityPlugin)this).Logger.LogError((object)("Capture rules rejected; new captures disabled. " + ex.Message)); } } private Species SpeciesFor(Character target) { string text = Rules.Name(((Object)((Component)target).gameObject).name); if (species.TryGetValue(text, out var value)) { return value; } Growup component = ((Component)target).GetComponent<Growup>(); if (Object.op_Implicit((Object)(object)component)) { object? value2 = AccessTools.Field(typeof(Growup), "m_grownPrefab").GetValue(component); GameObject val = (GameObject)((value2 is GameObject) ? value2 : null); if (Object.op_Implicit((Object)(object)val) && species.TryGetValue(Rules.Name(((Object)val).name), out var value3) && !value3.Hostile && value3.WaterDepth == 0f) { return new Species(text, value3.Level, value3.Boss, hostile: false, value3.Xp, 0f, 0.6f, 0f, young: true); } } return null; } private Decision Evaluate(Character target, Player player, float multiplier) { //IL_0107: Unknown result type (might be due to invalid IL or missing references) if (!ready || faulted || !Synced) { return new Decision { Refusal = "Capture is waiting for matching server settings or requires log review." }; } if (!Object.op_Implicit((Object)(object)target) || target.IsPlayer() || target.IsDead() || target.IsBoss()) { return new Decision { Refusal = "This target cannot be captured." }; } Species species = SpeciesFor(target); if (species == null) { return new Decision { Refusal = "This creature is not supported for capture." }; } ZNetView component = ((Component)target).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { return new Decision { Refusal = "Creature network state is unavailable." }; } string text = ExtraState.Guard(target); if (text != null) { return new Decision { Refusal = text }; } ZDO zDO = component.GetZDO(); return Rules.Evaluate(species, target.IsTamed(), zDO.GetBool("valball_bonded", false) || zDO.GetBool("vercadi.valball.seen", false), zDO.GetBool("vercadi.valball.bred", false), Object.op_Implicit((Object)(object)player) ? ((Character)player).GetSkills().GetSkillLevel(native.Skill) : 0f, species.Boss.Length == 0 || (Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.GetGlobalKey(species.Boss)), target.GetHealthPercentage(), target.GetLevel() - 1, multiplier, native.Config<float>("Val Master", "XpMultiplier"), native.Config<int>("Val Master", "TameableXpFactor")); } private static bool Label(Character __0, Player __1, float __2, ref string __result) { try { Decision decision = instance.Evaluate(__0, __1, __2); __result = (decision.Allowed ? ("Capture: " + Mathf.RoundToInt(decision.Chance * 100f) + "%") : decision.Refusal); } catch { __result = "Capture unavailable; check the compatibility log."; } return false; } private static bool OnHitGuard(object __instance, Collider __0, Vector3 __1, bool __2) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) if (instance.ready && !instance.faulted && instance.Synced) { return true; } if (Native.Read<bool>(__instance, "m_resolved")) { return false; } Component val = (Component)__instance; ZNetView component = val.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner()) { return false; } AccessTools.Field(__instance.GetType(), "m_resolved").SetValue(__instance, true); Projectile component2 = val.GetComponent<Projectile>(); ItemData val2 = (ItemData)AccessTools.Field(typeof(Projectile), "m_weapon").GetValue(component2); object? value = AccessTools.Field(typeof(Projectile), "m_owner").GetValue(component2); Player player = (Player)((value is Player) ? value : null); try { Deliver(val2.Clone(), __1, player); Tell(player, "ValBall transport is unavailable. Check server sync and the compatibility log."); } catch (Exception ex) { ((BaseUnityPlugin)instance).Logger.LogError((object)("Could not refund blocked throw: " + ex)); } return false; } private static void BreedingStart() { breeding++; } private static Exception BreedingEnd(Exception __exception) { breeding = Math.Max(0, breeding - 1); return __exception; } private static void Spawned(Character __instance) { if (breeding > 0) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { component.GetZDO().Set("vercadi.valball.bred", true); } } } private static bool Taming(Tameable __instance) { if (!Object.op_Implicit((Object)(object)instance) || !instance.ready) { return true; } string key = Rules.Name(((Object)((Component)__instance).gameObject).name); if (!Rules.Catalog.TryGetValue(key, out var value) || value.WaterDepth <= 0f) { return true; } if (instance.species.TryGetValue(key, out var value2)) { value = value2; } if (!instance.Synced) { return false; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); Character component2 = ((Component)__instance).GetComponent<Character>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner() || !Object.op_Implicit((Object)(object)component2) || component2.IsTamed()) { return true; } ZDO zDO = component.GetZDO(); float num = zDO.GetFloat("vercadi.valball.oceanDuration", 0f); if (num != value.TamingSeconds) { float num2 = zDO.GetFloat(ZDOVars.s_tameTimeLeft, __instance.m_tamingTime); float num3 = ((num > 0f) ? num : Math.Max(1f, __instance.m_tamingTime)); zDO.Set(ZDOVars.s_tameTimeLeft, value.TamingSeconds * Mathf.Clamp01(num2 / num3)); zDO.Set("vercadi.valball.oceanDuration", value.TamingSeconds); } __instance.m_tamingTime = value.TamingSeconds; if (value.Boss.Length != 0) { if (Object.op_Implicit((Object)(object)ZoneSystem.instance)) { return ZoneSystem.instance.GetGlobalKey(value.Boss); } return false; } return true; } private static void Tell(Player player, string text) { try { if (Object.op_Implicit((Object)(object)player)) { ((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false); } } catch { } } private void OnDestroy() { ready = false; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } private static bool Capture(object __instance, Collider __0, Vector3 __1, Player __2) { //IL_0054: 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_0336: 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_01ef: 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) Plugin plugin = instance; Journal journal = null; bool flag = false; bool flag2 = false; ItemData val = null; try { val = plugin.native.Empty(__instance); Character val2 = (Object.op_Implicit((Object)(object)__0) ? ((Component)__0).GetComponentInParent<Character>() : null); Decision decision = plugin.Evaluate(val2, __2, plugin.native.Multiplier(__instance)); if (!decision.Allowed) { flag2 = true; Deliver(val, __1, __2); Tell(__2, decision.Refusal); return false; } ZNetView component = ((Component)val2).GetComponent<ZNetView>(); component.ClaimOwnership(); if (!component.IsOwner()) { flag2 = true; Deliver(val, __1, __2); Tell(__2, "Creature ownership is busy. Try again when it has settled."); return false; } string text = ExtraState.Guard(val2); if (text != null) { flag2 = true; Deliver(val, __1, __2); Tell(__2, text); return false; } if (Random.value > decision.Chance) { if (!plugin.native.Config<bool>("Capture", "BreakOnFail")) { flag2 = true; Deliver(val, __1, __2); } Tell(__2, "The creature broke free."); return false; } object snapshot = plugin.native.Snapshot(val2); ItemData val3 = plugin.native.Full(__instance); val3.m_stack = 1; plugin.native.Write(snapshot, val3); ExtraState.Save(val2, val3); val3.m_customData["vercadi.valball.transaction"] = Guid.NewGuid().ToString("N"); if (!Object.op_Implicit((Object)(object)ZNetScene.instance.GetPrefab(val3.m_customData["valball.prefab"]))) { throw new InvalidOperationException("Creature prefab cannot be released."); } journal = new Journal("capture", val3, ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString()); journal.Phase("before-destroy"); flag = true; ZNetScene.instance.Destroy(((Component)val2).gameObject); journal.Phase("creature-destroyed"); Deliver(val3, __1, __2); journal.Phase("filled-ball-delivered"); journal.Complete(); journal = null; try { plugin.native.Xp(__2, decision.Xp); } catch (Exception ex) { ((BaseUnityPlugin)plugin).Logger.LogWarning((object)("Captured successfully; XP callback failed: " + ex.Message)); } ((BaseUnityPlugin)plugin).Logger.LogInfo((object)("CAPTURE COMMITTED: " + val3.m_customData["valball.prefab"] + "; cargo/state saved; xp=" + decision.Xp)); Tell(__2, "Captured " + Localization.instance.Localize(Object.op_Implicit((Object)(object)val2) ? val2.m_name : val3.m_customData["valball.display"]) + "."); } catch (Exception ex2) { if (flag || flag2) { plugin.faulted = true; ((BaseUnityPlugin)plugin).Logger.LogError((object)("Capture stopped after an uncertain world/inventory operation. Do not retry or restore automatically. Recovery record: " + journal?.PathName + "\n" + ex2)); Tell(__2, "Capture stopped. Keep the session unchanged and review the recovery log."); } else { ((BaseUnityPlugin)plugin).Logger.LogError((object)("Capture refused before creature removal: " + ex2)); try { if (val != null) { flag2 = true; Deliver(val, __1, __2); } journal?.Complete(); } catch (Exception ex3) { plugin.faulted = true; ((BaseUnityPlugin)plugin).Logger.LogError((object)("Refund uncertain; no retry: " + ex3)); } Tell(__2, "Capture refused; animal was not removed. Check the log."); } } return false; } private unsafe static bool Release(object __instance, object __0, ItemData __1, Vector3 __2, Player __3) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) Plugin plugin = instance; GameObject val = null; Journal journal = null; bool flag = false; bool flag2 = false; try { string text = Native.Read<string>(__0, "Prefab"); GameObject prefab = ZNetScene.instance.GetPrefab(text); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<Character>())) { flag2 = true; Deliver(Single(__1), __2, __3); Tell(__3, "Creature mod is missing. Filled ball returned."); return false; } Rules.Catalog.TryGetValue(text, out var value); Vector3 position = __2 + Vector3.up * 0.2f; if (value != null && value.WaterDepth > 0f && !WaterPosition(__2, value, out position, out var why)) { flag2 = true; Deliver(Single(__1), __2, __3); Tell(__3, why); return false; } if (__1.m_customData.TryGetValue("vercadi.valball.cargo", out var value2) && value2.Length > 0) { Cargo.Load(value2); } if (__1.m_customData.ContainsKey("vercadi.valball.coat") && ExtraState.HorseCache == null) { throw new InvalidOperationException("OdinHorse state adapter missing."); } journal = new Journal("release", Single(__1), "not-spawned"); Vector3 val2 = (Object.op_Implicit((Object)(object)__3) ? ((Component)__3).transform.forward : Vector3.forward); val2.y = 0f; val = Object.Instantiate<GameObject>(prefab, position, (((Vector3)(ref val2)).sqrMagnitude > 0.01f) ? Quaternion.LookRotation(val2) : Quaternion.identity); Character component = val.GetComponent<Character>(); ZNetView component2 = val.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !component2.IsValid() || !component2.IsOwner()) { throw new InvalidOperationException("Released creature has no owned network state."); } ZDO zDO = component2.GetZDO(); Journal journal2 = journal; ZDOID uid = zDO.m_uid; journal2.Phase("spawned-" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString()); component.SetLevel(Native.Read<int>(__0, "Level")); zDO.Set("valball_bonded", true); zDO.Set("vercadi.valball.seen", true); if (Object.op_Implicit((Object)(object)__3)) { zDO.Set("valball_owner", __3.GetPlayerID()); } bool flag3 = Native.Read<bool>(__0, "Tamed"); if (flag3) { MonsterAI component3 = val.GetComponent<MonsterAI>(); if (Object.op_Implicit((Object)(object)component3)) { component3.MakeTame(); } else { component.SetTamed(true); } } else { component.SetTamed(false); } plugin.native.Timers(__0, zDO); ExtraState.Restore(component, __1); float num = Native.Read<float>(__0, "HealthPct"); component.SetHealth(Mathf.Max(1f, component.GetMaxHealth() * Mathf.Clamp(num, 0.01f, 1f))); zDO.Set(ZDOVars.s_tamedName, Native.Read<string>(__0, "TamedName") ?? ""); zDO.Set(ZDOVars.s_tamedNameAuthor, Native.Read<string>(__0, "TamedNameAuthor") ?? ""); Type type = plugin.native.Plugin.Assembly.GetType("ValBall.Growth", throwOnError: true); Type type2 = plugin.native.Plugin.Assembly.GetType("ValBall.Breeding", throwOnError: true); zDO.Set((int)AccessTools.Field(type, "GrowthHash").GetValue(null), Native.Read<float>(__0, "GrowthPoints")); zDO.Set((int)AccessTools.Field(type, "KillsHash").GetValue(null), Native.Read<int>(__0, "Kills"), false); zDO.Set((int)AccessTools.Field(type2, "BirthsHash").GetValue(null), Native.Read<int>(__0, "Births"), false); Tameable component4 = val.GetComponent<Tameable>(); if (flag3 && Native.Read<bool>(__0, "Saddle") && Object.op_Implicit((Object)(object)component4) && Object.op_Implicit((Object)(object)component4.m_saddle)) { component2.InvokeRPC("AddSaddle", Array.Empty<object>()); } journal.Phase("creature-state-restored"); flag = true; Deliver(plugin.native.Empty(__instance), __2, __3); journal.Phase("empty-ball-delivered"); journal.Complete(); journal = null; ((BaseUnityPlugin)plugin).Logger.LogInfo((object)("RELEASE COMMITTED: " + text + "; tame=" + flag3 + "; one creature restored.")); Tell(__3, "Released " + Localization.instance.Localize(component.m_name) + "."); } catch (Exception ex) { if (!flag && !flag2) { try { if (Object.op_Implicit((Object)(object)val)) { ZNetView component5 = val.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component5) || !component5.IsValid() || !component5.IsOwner()) { throw new InvalidOperationException("Cannot confirm rollback ownership."); } ZNetScene.instance.Destroy(val); if (component5.IsValid()) { throw new InvalidOperationException("Rollback did not remove creature network state."); } } flag2 = true; Deliver(Single(__1), __2, __3); journal?.Complete(); ((BaseUnityPlugin)plugin).Logger.LogError((object)("Release rolled back; filled ball returned. " + ex)); Tell(__3, "Release failed; filled ball returned. Check the log before retrying."); return false; } catch (Exception ex2) { ((BaseUnityPlugin)plugin).Logger.LogError((object)("Release rollback uncertain: " + ex2)); } } plugin.faulted = true; ((BaseUnityPlugin)plugin).Logger.LogError((object)("Release stopped after an uncertain operation; no automatic replay/refund. Recovery record: " + journal?.PathName + "\n" + ex)); Tell(__3, "Release stopped. Review the recovery log before continuing."); } return false; } private static ItemData Single(ItemData item) { ItemData obj = item.Clone(); obj.m_stack = 1; return obj; } private static void Deliver(ItemData ball, Vector3 hitPoint, Player player) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (ball == null) { throw new InvalidOperationException("No return item."); } ball.m_stack = 1; if (Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead() && ((Character)player).IsOwner() && (instance.native == null || instance.native.Config<bool>("General", "ReturnToInventory"))) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory.CanAddItem(ball, 1) && inventory.AddItem(ball)) { return; } } ItemDrop val = ItemDrop.DropItem(ball, 1, hitPoint + Vector3.up * 0.3f, Quaternion.identity); if (!Object.op_Implicit((Object)(object)val) || val.m_itemData == null) { throw new InvalidOperationException("Item delivery did not produce an item drop."); } } private static bool WaterPosition(Vector3 hit, Species s, out Vector3 position, out string why) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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) position = hit; why = "Release this animal in deeper, unobstructed water."; float liquidLevel = Floating.GetLiquidLevel(hit, 1f, (LiquidType)0); if (float.IsNaN(liquidLevel) || float.IsInfinity(liquidLevel)) { return false; } int mask = LayerMask.GetMask(new string[6] { "terrain", "Default", "static_solid", "piece", "Default_small", "vehicle" }); Vector3[] array = (Vector3[])(object)new Vector3[5] { Vector3.zero, Vector3.right * s.Radius, Vector3.left * s.Radius, Vector3.forward * s.Radius, Vector3.back * s.Radius }; RaycastHit val2 = default(RaycastHit); foreach (Vector3 val in array) { if (!Physics.Raycast(new Vector3(hit.x + val.x, liquidLevel + 1f, hit.z + val.z), Vector3.down, ref val2, 1000f, mask, (QueryTriggerInteraction)1) || liquidLevel - ((RaycastHit)(ref val2)).point.y < s.WaterDepth) { return false; } } position.y = liquidLevel - Math.Min(s.WaterDepth * 0.5f, s.Radius); if (Physics.OverlapSphere(position, s.Radius, mask, (QueryTriggerInteraction)1).Any((Collider c) => Object.op_Implicit((Object)(object)c) && !c.isTrigger)) { return false; } return true; } } internal sealed class Species { internal string Name; internal string Boss; internal int Level; internal int Xp; internal bool Hostile; internal bool Young; internal float WaterDepth; internal float Radius; internal float TamingSeconds; internal Species(string name, int level, string boss, bool hostile, int xp = 20, float waterDepth = 0f, float radius = 0.6f, float taming = 0f, bool young = false) { Name = name; Level = level; Boss = boss; Hostile = hostile; Xp = xp; WaterDepth = waterDepth; Radius = radius; TamingSeconds = taming; Young = young; } } internal sealed class Decision { internal string Refusal; internal float Chance; internal float Xp; internal bool Allowed => Refusal == null; } internal static class Rules { internal const string Prefix = "vercadi.valball."; internal const int MaxInventoryBytes = 1048576; internal static readonly Dictionary<string, Species> Catalog = Build(); internal static string DefaultProgression => string.Join(";", Catalog.Values.Select((Species s) => s.Name + ":" + s.Level + ":" + s.Boss)); private static Dictionary<string, Species> Build() { Dictionary<string, Species> d = new Dictionary<string, Species>(StringComparer.Ordinal); string[] array = new string[5] { "Boar", "Deer", "Sheep_TW", "rae_OdinHorse", "Hen" }; for (int i = 0; i < array.Length; i++) { Add(new Species(array[i], 0, "", hostile: false)); } array = new string[3] { "Piggy", "rae_Offspring_Normal", "Chicken" }; for (int i = 0; i < array.Length; i++) { Add(new Species(array[i], 0, "", hostile: false, 20, 0f, 0.6f, 0f, young: true)); } array = new string[5] { "Brown", "DarkGrey", "Exotic", "Native", "White" }; foreach (string text in array) { Add(new Species("BOH_Hare_" + text, 0, "", hostile: false)); Add(new Species("BOH_Hare_" + text + "Kitten", 0, "", hostile: false, 20, 0f, 0.6f, 0f, young: true)); } Add(new Species("Hare", 0, "defeated_goblinking", hostile: false, 96)); Add(new Species("BOH_Hare_Kitten", 0, "defeated_goblinking", hostile: false, 96, 0f, 0.6f, 0f, young: true)); Add(new Species("Greyling", 10, "", hostile: true)); Add(new Species("Neck", 10, "", hostile: true)); Add(new Species("Greydwarf", 25, "defeated_eikthyr", hostile: true, 36)); Add(new Species("Skeleton", 25, "defeated_eikthyr", hostile: true, 36)); Add(new Species("Greydwarf_Elite", 35, "defeated_gdking", hostile: true, 36)); Add(new Species("Greydwarf_Shaman", 35, "defeated_gdking", hostile: true, 36)); Add(new Species("Bjorn", 40, "defeated_gdking", hostile: true, 50)); Add(new Species("Wolf", 40, "defeated_bonemass", hostile: true, 64)); Add(new Species("Draugr", 45, "defeated_gdking", hostile: true, 50)); Add(new Species("Lox", 50, "defeated_dragon", hostile: true, 80, 0f, 2f)); Add(new Species("Troll", 55, "defeated_gdking", hostile: true, 50, 0f, 1.8f)); Add(new Species("Fenring", 60, "defeated_bonemass", hostile: true, 64)); Add(new Species("Fenring_Cultist", 60, "defeated_bonemass", hostile: true, 64)); Add(new Species("StoneGolem", 65, "defeated_dragon", hostile: true, 80, 0f, 1.5f)); Add(new Species("Unbjorn", 70, "defeated_goblinking", hostile: true, 96)); Add(new Species("Animal_Dolphin_RtD", 25, "defeated_gdking", hostile: false, 36, 2f, 1.2f, 300f)); Add(new Species("Monster_Orca_RtD", 45, "defeated_bonemass", hostile: false, 64, 5f, 3f, 900f)); Add(new Species("Mirmaid_RtD", 65, "defeated_goblinking", hostile: false, 96, 2f, 1f, 1200f)); return d; void Add(Species s) { d.Add(s.Name, s); } } internal static Dictionary<string, Species> Parse(string text) { Dictionary<string, Species> dictionary = new Dictionary<string, Species>(StringComparer.Ordinal); string[] array = (text ?? "").Split(new char[1] { ';' }); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2)) { string[] array2 = text2.Trim().Split(new char[1] { ':' }); if (array2.Length != 3 || !Catalog.TryGetValue(array2[0], out var value) || !int.TryParse(array2[1], out var result) || result < 0 || result > 100 || array2[2].Any((char c) => !char.IsLetterOrDigit(c) && c != '_') || dictionary.ContainsKey(array2[0])) { throw new FormatException("Invalid or unreviewed capture species rule: " + text2); } dictionary.Add(value.Name, new Species(value.Name, result, array2[2], value.Hostile, value.Xp, value.WaterDepth, value.Radius, value.TamingSeconds, value.Young)); } } return dictionary; } internal static Decision Evaluate(Species s, bool tamed, bool seenBefore, bool bred, float skill, bool bossUnlocked, float health, int stars, float ball, float xpMultiplier, float tameXpPercent) { if (s == null) { return new Decision { Refusal = "This creature is not supported for capture." }; } if (tamed) { return new Decision { Chance = 1f, Xp = 0f }; } if (!bossUnlocked) { return new Decision { Refusal = "World boss requirement: " + s.Boss }; } if (skill < (float)s.Level) { return new Decision { Refusal = "Requires Val Master " + s.Level + "." }; } if (s.Hostile && health > 0.50001f) { return new Decision { Refusal = "Weaken this wild creature to 50% health first." }; } double num = (s.Hostile ? Math.Min(0.9, 0.6 * Math.Pow(0.75, Math.Max(0, stars)) * (1.0 + (double)Math.Max(0f, skill - (float)s.Level) * 0.01) * (double)Math.Max(0f, ball)) : 1.0); return new Decision { Chance = (float)num, Xp = ((seenBefore || bred || s.Young) ? 0f : ((float)(s.Xp * (1 + Math.Max(0, stars))) * Math.Max(0f, xpMultiplier) * (s.Hostile ? 1f : (Math.Max(0f, tameXpPercent) / 100f)))) }; } internal static string Name(string name) { string text; if (name == null || !name.EndsWith("(Clone)", StringComparison.Ordinal)) { text = name; if (text == null) { return ""; } } else { text = name.Substring(0, name.Length - 7); } return text; } internal static float Number(IDictionary<string, string> data, string key, float fallback = 0f) { if (!data.TryGetValue(key, out var value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result)) { return fallback; } return result; } } }