using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Joive.BurglinGnomes.SwimmingRing.Config; using Joive.BurglinGnomes.SwimmingRing.Game; using Joive.BurglinGnomes.SwimmingRing.Gameplay; using Joive.BurglinGnomes.SwimmingRing.Items; using Lightbug.CharacterControllerPro.Core; using Lightbug.CharacterControllerPro.Implementation; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using UnityEngine.Localization.Tables; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Joive")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Adds a craftable swimming ring equipment item.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+e93357b17a5bd3c061e91c11c78c776a117b3496")] [assembly: AssemblyProduct("BG Swimming Ring")] [assembly: AssemblyTitle("BG-SwimmingRing-Joive")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Joive.BurglinGnomes.SwimmingRing { [BepInPlugin("joive.bg.swimmingring", "BG Swimming Ring", "1.0.0")] public sealed class SwimmingRingPlugin : BaseUnityPlugin { public const string PluginId = "joive.bg.swimmingring"; public const string PluginName = "BG Swimming Ring"; public const string Version = "1.0.0"; private Harmony harmony; private void Awake() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown PluginConfig.Bind(((BaseUnityPlugin)this).Config); ModLog.Bind(((BaseUnityPlugin)this).Logger); SwimmingRingDefinition.Configure(PluginConfig.PlasticCost.Value); GameText.RegisterSwimmingRingText(); if (PluginConfig.DebugLogs.Value) { ModLog.Info("BG-SwimmingRing-Joive.dll loaded. Version: 1.0.0"); } harmony = new Harmony("joive.bg.swimmingring"); harmony.PatchAll(typeof(SwimmingRingPlugin).Assembly); ModLog.Info("BG Swimming Ring 1.0.0 loaded."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } } namespace Joive.BurglinGnomes.SwimmingRing.Patches { [HarmonyPatch(typeof(EquipmentHandler), "SetWearedEquipment")] internal static class EquipmentHandlerSetWearedEquipmentPatch { private static bool Prefix(string name) { return !GameApi.IsSwimmingRingName(name); } } [HarmonyPatch(typeof(EquipmentHandler), "SetOtherWearedEquipment")] internal static class EquipmentHandlerSetOtherWearedEquipmentPatch { private static bool Prefix(string name) { return !GameApi.IsSwimmingRingName(name); } } [HarmonyPatch(typeof(GnomiumDeposit), "OnNetworkSpawn")] internal static class GnomiumDepositOnNetworkSpawnPatch { private static void Postfix(GnomiumDeposit __instance) { CraftingRegistry.EnsureRegistered(__instance); } } [HarmonyPatch(typeof(InventoryBase), "OnNetworkSpawn")] internal static class InventoryOnNetworkSpawnPatch { private static void Postfix(InventoryBase __instance) { ItemRegistry.EnsureRegistered(__instance.BoundItems); } } [HarmonyPatch(typeof(ItemInstance), "OnNetworkSpawn")] internal static class ItemInstanceOnNetworkSpawnPatch { private static void Postfix(ItemInstance __instance) { SwimmingRingVisualApplier.Apply(__instance); } } [HarmonyPatch(typeof(ItemInstance), "OnInstanceDataChange")] internal static class ItemInstanceOnInstanceDataChangePatch { private static void Postfix(ItemInstance __instance) { SwimmingRingVisualApplier.Apply(__instance); } } [HarmonyPatch(typeof(NormalMovement), "HandleVelocity")] internal static class NormalMovementHandleVelocityPatch { private static void Postfix(NormalMovement __instance, float dt) { SwimmingRingMovement.Apply(__instance, dt); } } [HarmonyPatch(typeof(Unpinned), "CheckEnterTransition")] internal static class UnpinnedCheckEnterTransitionPatch { private static void Postfix(Unpinned __instance, object fromState, ref bool __result) { if (__result && fromState is NormalMovement && GameApi.TryGetPlayer((Component)(object)__instance, out var player) && GameApi.CanStandFromWater(player) && GameApi.IsPlayerInWater(__instance)) { __result = false; } } } [HarmonyPatch(typeof(Unpinned), "CheckExitTransition")] internal static class UnpinnedCheckExitTransitionPatch { private static void Prefix(Unpinned __instance) { if (GameApi.TryGetPlayer((Component)(object)__instance, out var player) && GameApi.CanStandFromWater(player) && GameApi.IsPlayerInWater(__instance)) { player.StateController.EnqueueTransition(); } } } } namespace Joive.BurglinGnomes.SwimmingRing.Items { internal static class SwimmingRingDefinition { internal const string ItemName = "Inflatable Swimming Ring"; internal const string LegacyItemName = "inflatable_swimming_ring"; internal const string DisplayName = "Inflatable Swimming Ring"; internal const string Description = "Keeps you steady in water."; internal const float MaxDurability = 100f; internal static int PlasticCost { get; private set; } = 4; internal static void Configure(int plasticCost) { PlasticCost = plasticCost; } internal static LocalizedString NameText() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001a: Expected O, but got Unknown return new LocalizedString(TableReference.op_Implicit("Items"), TableEntryReference.op_Implicit("Inflatable Swimming Ring")); } internal static LocalizedString DescriptionText() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001a: Expected O, but got Unknown return new LocalizedString(TableReference.op_Implicit("Items Descriptions"), TableEntryReference.op_Implicit("Inflatable Swimming Ring")); } } } namespace Joive.BurglinGnomes.SwimmingRing.Game { internal static class CraftingRegistry { private static bool missingPlasticLogged; internal static void EnsureRegistered(GnomiumDeposit deposit) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown if (GameApi.IsEnabled && !((Object)(object)deposit == (Object)null) && !((Object)(object)deposit.Recipes == (Object)null) && !((Object)(object)deposit.ResourceInventory == (Object)null)) { ItemData val = ItemRegistry.EnsureRegistered(deposit.ResourceInventory.BoundItems); ItemData val2 = FindPlasticResource(deposit.ResourceInventory.BoundItems); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !HasRecipe(deposit.Recipes, val)) { Recipe val3 = new Recipe(); val3.craftResultType = (CraftResultType)0; val3.cost = (ItemCost[])(object)new ItemCost[1] { new ItemCost { item = val2, amount = SwimmingRingDefinition.PlasticCost } }; Recipe val4 = val3; GameApi.SetRecipeItem(val4, val); Array.Resize(ref deposit.Recipes.recipes, deposit.Recipes.recipes.Length + 1); deposit.Recipes.recipes[deposit.Recipes.recipes.Length - 1] = val4; GameApi.Debug("Registered recipe: Inflatable Swimming Ring"); } } } private static ItemData FindPlasticResource(AllItems allItems) { string resourceName = ResourceStorage.GetResourceName(3); int num = default(int); ItemData item = allItems.GetItem(resourceName, ref num); if ((Object)(object)item == (Object)null && !missingPlasticLogged) { missingPlasticLogged = true; ModLog.Warning("Plastic resource item was not found."); } return item; } private static bool HasRecipe(CraftingRecipes recipes, ItemData result) { Recipe[] recipes2 = recipes.recipes; foreach (Recipe val in recipes2) { if (val != null && (Object)(object)val.CraftResult == (Object)(object)result) { return true; } } return false; } } internal static class GameApi { private static readonly FieldInfo RecipeItemField = AccessTools.Field(typeof(Recipe), "item"); private static readonly MethodInfo CheckIsPlayerInWaterMethod = AccessTools.Method(typeof(Unpinned), "CheckIsPlayerInWater", (Type[])null, (Type[])null); private static readonly MethodInfo LoadImageMethod = FindLoadImageMethod(); internal static bool Ready { get; } = Validate(); internal static bool IsEnabled { get { if (PluginConfig.EnableMod != null && PluginConfig.EnableMod.Value) { return Ready; } return false; } } internal static void SetRecipeItem(Recipe recipe, ItemData item) { RecipeItemField.SetValue(recipe, item); } internal static bool LoadImage(Texture2D texture, byte[] bytes) { if (LoadImageMethod == null) { return false; } object[] parameters = ((LoadImageMethod.GetParameters().Length != 2) ? new object[3] { texture, bytes, false } : new object[2] { texture, bytes }); return (bool)LoadImageMethod.Invoke(null, parameters); } internal static bool IsPlayerInWater(Unpinned state) { return (bool)CheckIsPlayerInWaterMethod.Invoke(state, null); } internal static bool HasSwimmingRing(PlayerNetworking player) { return HasSwimmingRing(player, equippedOnly: false); } internal static bool HasEquippedSwimmingRing(PlayerNetworking player) { return HasSwimmingRing(player, equippedOnly: true); } private static bool HasSwimmingRing(PlayerNetworking player, bool equippedOnly) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player.Inventory == (Object)null) { return false; } foreach (InventoryItem content in ((InventoryBase)player.Inventory).Contents) { InventoryItem current = content; if (((InventoryItem)(ref current)).IsValidItem && (!equippedOnly || current.slotIndex <= 1)) { ItemData itemData = ((InventoryBase)player.Inventory).GetItemData((int)((InventoryItem)(ref current)).ItemIndex); if ((Object)(object)itemData != (Object)null && IsSwimmingRing(itemData)) { return true; } } } return false; } internal static bool IsSwimmingRing(ItemData itemData) { if ((Object)(object)itemData != (Object)null) { return IsSwimmingRingName(((CraftableItemBase)itemData).Name); } return false; } internal static bool IsSwimmingRingName(string itemName) { if (!(itemName == "Inflatable Swimming Ring")) { return itemName == "inflatable_swimming_ring"; } return true; } internal static bool CanStandFromWater(PlayerNetworking player) { if (HasEquippedSwimmingRing(player) && !((HealthBase)player.Health).Dead && !player.Tied && player.StatusEffects.StunnedDuration <= 0f) { return !player.Dismemberment.IsMissingLeg; } return false; } internal static bool TryGetPlayer(Component component, out PlayerNetworking player) { player = component.GetComponentInParent(); return (Object)(object)player != (Object)null; } internal static void Debug(string message) { if (PluginConfig.DebugLogs != null && PluginConfig.DebugLogs.Value) { ModLog.Info(message); } } private static bool Validate() { return (byte)(1u & (Require(RecipeItemField, "CraftingRecipes.Recipe.item") ? 1u : 0u) & (Require(CheckIsPlayerInWaterMethod, "Unpinned.CheckIsPlayerInWater") ? 1u : 0u) & (Require(LoadImageMethod, "UnityEngine.ImageConversion.LoadImage") ? 1u : 0u)) != 0; } private static MethodInfo FindLoadImageMethod() { Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule"); if (type == null) { return null; } return type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Texture2D), typeof(byte[]) }, null) ?? type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, null); } private static bool Require(MemberInfo member, string name) { if (member != null) { return true; } ModLog.Warning("Missing game member: " + name); return false; } } internal static class GameText { private readonly struct TextPair { internal readonly string Name; internal readonly string Description; internal TextPair(string name, string description) { Name = name; Description = description; } } private static readonly Dictionary TextByLanguage = new Dictionary { { "en", new TextPair("Inflatable Swimming Ring", "Keeps you steady in water.") }, { "ru", new TextPair("Надувной круг", "Помогает держаться на воде.") }, { "uk", new TextPair("Надувний круг", "Допомагає триматися на воді.") }, { "de", new TextPair("Aufblasbarer Schwimmring", "Hilft dir, im Wasser stabil zu bleiben.") }, { "fr", new TextPair("Bouée gonflable", "Vous aide à rester stable dans l'eau.") }, { "es", new TextPair("Flotador inflable", "Te ayuda a mantenerte estable en el agua.") }, { "pt", new TextPair("Boia inflável", "Ajuda você a se manter estável na água.") }, { "it", new TextPair("Ciambella gonfiabile", "Ti aiuta a restare stabile in acqua.") }, { "pl", new TextPair("Nadmuchiwane koło", "Pomaga utrzymać się stabilnie w wodzie.") }, { "tr", new TextPair("Şişme yüzme simidi", "Suda dengede kalmana yardımcı olur.") }, { "zh", new TextPair("充气游泳圈", "帮助你在水中保持稳定。") }, { "ja", new TextPair("空気入り浮き輪", "水中で体勢を保ちやすくする。") }, { "ko", new TextPair("공기 주입식 튜브", "물속에서 안정적으로 움직일 수 있게 해 줍니다.") } }; internal static void RegisterSwimmingRingText() { IReadOnlyList locales = LocalizationSettings.AvailableLocales.Locales; if (locales == null || locales.Count == 0) { AddText("Items", "Inflatable Swimming Ring", "Inflatable Swimming Ring"); AddText("Items Descriptions", "Inflatable Swimming Ring", "Keeps you steady in water."); return; } foreach (Locale item in locales) { TextPair text = GetText(item); AddText("Items", "Inflatable Swimming Ring", text.Name, item); AddText("Items Descriptions", "Inflatable Swimming Ring", text.Description, item); } } private static void AddText(string tableName, string key, string value) { AddText(tableName, key, value, null); } private static void AddText(string tableName, string key, string value, Locale locale) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) StringTable table = ((LocalizedDatabase)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(tableName), (Locale)null); if ((Object)(object)locale != (Object)null) { table = ((LocalizedDatabase)(object)LocalizationSettings.StringDatabase).GetTable(TableReference.op_Implicit(tableName), locale); } if (!((Object)(object)table == (Object)null) && ((DetailedLocalizationTable)(object)table).GetEntry(key) == null) { ((DetailedLocalizationTable)(object)table).AddEntry(key, value); } } private static TextPair GetText(Locale locale) { //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) LocaleIdentifier identifier = locale.Identifier; string text = ((LocaleIdentifier)(ref identifier)).Code.ToLowerInvariant(); if (TextByLanguage.TryGetValue(text, out var value)) { return value; } int num = text.IndexOf('-'); string key = ((num >= 0) ? text.Substring(0, num) : text); if (TextByLanguage.TryGetValue(key, out var value2)) { return value2; } return TextByLanguage["en"]; } } internal static class ItemRegistry { private static ItemData swimmingRing; private static int swimmingRingIndex = -1; internal static bool IsSwimmingRingIndex(byte itemIndex) { if (swimmingRingIndex >= 0) { return itemIndex == swimmingRingIndex; } return false; } internal static ItemData EnsureRegistered(AllItems allItems) { if (!GameApi.IsEnabled || (Object)(object)allItems == (Object)null) { return null; } GameText.RegisterSwimmingRingText(); if ((Object)(object)swimmingRing != (Object)null && allItems.IndexOf(swimmingRing) >= 0) { swimmingRingIndex = allItems.IndexOf(swimmingRing); return swimmingRing; } ItemData val = FindExisting(allItems); if ((Object)(object)val != (Object)null) { NormalizeItem(val); swimmingRing = val; swimmingRingIndex = allItems.IndexOf(swimmingRing); return swimmingRing; } swimmingRing = CreateItem(allItems); Array.Resize(ref allItems.items, allItems.items.Length + 1); allItems.items[allItems.items.Length - 1] = swimmingRing; swimmingRingIndex = allItems.items.Length - 1; GameApi.Debug("Registered item: Inflatable Swimming Ring at index " + swimmingRingIndex + "."); return swimmingRing; } private static ItemData CreateItem(AllItems allItems) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) ItemData obj = ScriptableObject.CreateInstance(); ((Object)obj).name = "Inflatable Swimming Ring"; ((CraftableItemBase)obj).localizedName = SwimmingRingDefinition.NameText(); ((CraftableItemBase)obj).localizedDesc = SwimmingRingDefinition.DescriptionText(); obj.description = "Keeps you steady in water."; ((CraftableItemBase)obj).icon = SwimmingRingIcon.GetIcon(); obj.equipmentType = (EquipmentType)2; obj.maxStackSize = 1; obj.durabilityLoss = (EquipmentDurabilityLossType)2; obj.maxDurability = 100f; obj.dontAllowEquipTogetherWith = (ItemData[])(object)new ItemData[0]; obj.prefab = allItems.defaultPrefab; ((Object)obj).hideFlags = (HideFlags)32; return obj; } private static ItemData FindExisting(AllItems allItems) { ItemData[] items = allItems.items; foreach (ItemData val in items) { if ((Object)(object)val != (Object)null && GameApi.IsSwimmingRing(val)) { return val; } } return null; } private static void NormalizeItem(ItemData item) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) ((Object)item).name = "Inflatable Swimming Ring"; ((CraftableItemBase)item).localizedName = SwimmingRingDefinition.NameText(); ((CraftableItemBase)item).localizedDesc = SwimmingRingDefinition.DescriptionText(); item.description = "Keeps you steady in water."; ((CraftableItemBase)item).icon = SwimmingRingIcon.GetIcon(); item.equipmentType = (EquipmentType)2; item.dontAllowEquipTogetherWith = (ItemData[])(object)new ItemData[0]; } } internal static class ModLog { private static ManualLogSource logger; internal static void Bind(ManualLogSource source) { logger = source; } internal static void Info(string message) { ManualLogSource obj = logger; if (obj != null) { obj.LogInfo((object)message); } } internal static void Warning(string message) { ManualLogSource obj = logger; if (obj != null) { obj.LogWarning((object)message); } } internal static void Error(string message) { ManualLogSource obj = logger; if (obj != null) { obj.LogError((object)message); } } } internal static class SwimmingRingAssets { private const string BundleFileName = "swimmingring.bundle"; private const string VisualPrefabPath = "assets/prefabs/swimmingringvisual.prefab"; private static bool loadAttempted; private static AssetBundle bundle; private static GameObject visualPrefab; internal static GameObject GetVisualPrefab() { return LoadVisualPrefab(); } private static GameObject LoadVisualPrefab() { if (loadAttempted) { return visualPrefab; } loadAttempted = true; try { byte[] array = ReadBundleBytes(); GameApi.Debug("Loading embedded AssetBundle bytes: " + array.Length + "."); bundle = AssetBundle.LoadFromMemory(array); if ((Object)(object)bundle == (Object)null) { ModLog.Warning("Embedded swimming ring bundle could not be loaded."); return null; } GameApi.Debug("AssetBundle.LoadFromMemory succeeded for swimmingring.bundle."); visualPrefab = bundle.LoadAsset("assets/prefabs/swimmingringvisual.prefab"); if ((Object)(object)visualPrefab == (Object)null) { ModLog.Warning("Swimming ring visual prefab was not found at assets/prefabs/swimmingringvisual.prefab."); return null; } ((Object)visualPrefab).name = "Inflatable Swimming Ring"; ((Object)visualPrefab).hideFlags = (HideFlags)32; GameApi.Debug("Loaded prefab SwimmingRingVisual from embedded swimmingring.bundle: " + ((Object)visualPrefab).name + "."); return visualPrefab; } catch (Exception ex) { ModLog.Warning("Swimming ring bundle failed to load: " + ex.Message); return null; } } private static byte[] ReadBundleBytes() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = FindBundleResourceName(executingAssembly); if (text == null) { throw new InvalidOperationException("Missing embedded resource ending with swimmingring.bundle."); } GameApi.Debug("Using embedded bundle resource: " + text); using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Could not open embedded resource: " + text); } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return memoryStream.ToArray(); } private static string FindBundleResourceName(Assembly assembly) { string[] manifestResourceNames = assembly.GetManifestResourceNames(); foreach (string text in manifestResourceNames) { if (text.EndsWith("swimmingring.bundle", StringComparison.OrdinalIgnoreCase)) { return text; } } return null; } } internal static class SwimmingRingIcon { private const string IconResourceFileName = "SwimmingRingIcon.png"; private const int Size = 128; private static Sprite icon; internal static Sprite GetIcon() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: 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_00f8: 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_0157: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_014c: 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) if ((Object)(object)icon != (Object)null) { return icon; } icon = LoadEmbeddedIcon(); if ((Object)(object)icon != (Object)null) { return icon; } Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false); Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.08f, 0.34f, 0.95f, 1f); Color val4 = default(Color); ((Color)(ref val4))..ctor(0.78f, 0.92f, 1f, 1f); Color val5 = default(Color); ((Color)(ref val5))..ctor(0.02f, 0.08f, 0.28f, 1f); Vector2 val6 = default(Vector2); ((Vector2)(ref val6))..ctor(63.5f, 63.5f); float num = 53.76f; float num2 = 28.16f; float num3 = 57.6f; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float num4 = Vector2.Distance(new Vector2((float)j, (float)i), val6); Color val7 = val2; if (num4 <= num3 && num4 >= num2 * 0.92f) { val7 = val5; } if (num4 <= num && num4 >= num2) { val7 = ((Mathf.Sin(Mathf.Atan2((float)i - val6.y, (float)j - val6.x) * 6f) > 0.35f) ? val4 : val3); } val.SetPixel(j, i, val7); } } val.Apply(); ((Object)val).name = "SwimmingRingIcon"; ((Object)val).hideFlags = (HideFlags)32; icon = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 128f); ((Object)icon).name = "SwimmingRingIcon"; ((Object)icon).hideFlags = (HideFlags)32; return icon; } private static Sprite LoadEmbeddedIcon() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_00a9: 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) try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = FindIconResourceName(executingAssembly); if (text == null) { ModLog.Warning("Swimming ring icon resource was not found."); return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { ModLog.Warning("Swimming ring icon resource could not be opened."); return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!GameApi.LoadImage(val, memoryStream.ToArray())) { ModLog.Warning("Swimming ring icon PNG could not be loaded."); return null; } ((Object)val).name = "SwimmingRingIcon"; ((Object)val).hideFlags = (HideFlags)32; Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), (float)Mathf.Max(((Texture)val).width, ((Texture)val).height)); ((Object)obj).name = "SwimmingRingIcon"; ((Object)obj).hideFlags = (HideFlags)32; return obj; } catch (Exception ex) { ModLog.Warning("Swimming ring icon failed to load: " + ex.Message); return null; } } private static string FindIconResourceName(Assembly assembly) { string[] manifestResourceNames = assembly.GetManifestResourceNames(); foreach (string text in manifestResourceNames) { if (text.EndsWith("SwimmingRingIcon.png", StringComparison.OrdinalIgnoreCase)) { return text; } } return null; } } internal static class SwimmingRingPlayerVisual { private const string PlayerVisualRootName = "SwimmingRingPlayerVisual"; private static readonly Dictionary playerVisuals = new Dictionary(); internal static void Apply(PlayerNetworking player) { if (!GameApi.IsEnabled || (Object)(object)player == (Object)null) { return; } Transform val = GetCachedPlayerVisual(player); if (!GameApi.HasEquippedSwimmingRing(player)) { if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } return; } bool flag = (Object)(object)val == (Object)null; if (flag) { GameObject visualPrefab = SwimmingRingAssets.GetVisualPrefab(); if ((Object)(object)visualPrefab == (Object)null) { return; } GameObject obj = Object.Instantiate(visualPrefab, ((Component)player).transform, false); ((Object)obj).name = "SwimmingRingPlayerVisual"; val = obj.transform; playerVisuals[((Object)player).GetInstanceID()] = val; SwimmingRingVisualUtility.StripGameplayComponents(val); GameApi.Debug("Instantiated SwimmingRingPlayerVisual for player: " + ((Object)player).name + "."); } ((Component)val).gameObject.SetActive(true); ApplyPlayerVisualTransform(val); if (flag) { SwimmingRingVisualUtility.PrepareVisual(val, "SwimmingRingPlayerVisual"); } } private static Transform GetCachedPlayerVisual(PlayerNetworking player) { int instanceID = ((Object)player).GetInstanceID(); if (!playerVisuals.TryGetValue(instanceID, out var value)) { return null; } if ((Object)(object)value != (Object)null) { return value; } playerVisuals.Remove(instanceID); return null; } private static void ApplyPlayerVisualTransform(Transform visual) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) visual.localPosition = new Vector3(0f, PluginConfig.PlayerVisualHeightOffset.Value, PluginConfig.PlayerVisualForwardOffset.Value); visual.localRotation = Quaternion.identity; visual.localScale = Vector3.one * PluginConfig.PlayerVisualScale.Value; } } internal static class SwimmingRingVisualApplier { internal static void Apply(ItemInstance itemInstance) { SwimmingRingWorldVisual.Apply(itemInstance); } internal static void ApplyToPlayer(PlayerNetworking player) { SwimmingRingPlayerVisual.Apply(player); } } internal static class SwimmingRingVisualMaterial { private const float RuntimeSmoothness = 0.45f; private const string RuntimeShaderName = "Standard"; private static readonly Color FallbackOrange = new Color(1f, 0.58f, 0.22f, 1f); private static Material runtimeMaterial; private static bool missingShaderLogged; internal static Material GetRuntimeMaterial(Material sourceMaterial) { //IL_0066: 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) if ((Object)(object)runtimeMaterial == (Object)null) { runtimeMaterial = CreateRuntimeMaterial(); if ((Object)(object)runtimeMaterial == (Object)null) { return sourceMaterial; } } Texture mainTexture = GetMainTexture(sourceMaterial); if ((Object)(object)mainTexture != (Object)null) { SetMaterialTexture(runtimeMaterial, mainTexture); SetMaterialColor(runtimeMaterial, Color.white); return runtimeMaterial; } SetMaterialTexture(runtimeMaterial, null); SetMaterialColor(runtimeMaterial, FallbackOrange); return runtimeMaterial; } internal static string GetMaterialName(Renderer renderer) { Material sharedMaterial = renderer.sharedMaterial; if (!((Object)(object)sharedMaterial != (Object)null)) { return "null"; } return ((Object)sharedMaterial).name; } internal static string GetShaderName(Renderer renderer) { Material sharedMaterial = renderer.sharedMaterial; if (!((Object)(object)sharedMaterial != (Object)null) || !((Object)(object)sharedMaterial.shader != (Object)null)) { return "null"; } return ((Object)sharedMaterial.shader).name; } internal static string GetTextureName(Renderer renderer) { Texture mainTexture = GetMainTexture(renderer.sharedMaterial); if (!((Object)(object)mainTexture != (Object)null)) { return "null"; } return ((Object)mainTexture).name; } private static Material CreateRuntimeMaterial() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Standard"); if ((Object)(object)val == (Object)null) { if (!missingShaderLogged) { missingShaderLogged = true; ModLog.Warning("Shader not found: Standard."); } return null; } Material val2 = new Material(val); ((Object)val2).name = "SwimmingRingRuntimeOrangeMaterial"; SetMaterialColor(val2, FallbackOrange); if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", 0f); } if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", 0.45f); } GameApi.Debug("Created runtime visual material with shader: " + ((Object)val).name + "."); return val2; } private static void SetMaterialColor(Material material, Color color) { //IL_0013: 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) if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", color); } if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } } private static void SetMaterialTexture(Material material, Texture texture) { material.mainTexture = texture; if (material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", texture); } if (material.HasProperty("_BaseMap")) { material.SetTexture("_BaseMap", texture); } } private static Texture GetMainTexture(Material material) { if ((Object)(object)material == (Object)null) { return null; } if ((Object)(object)material.mainTexture != (Object)null) { return material.mainTexture; } if (material.HasProperty("_BaseMap")) { return material.GetTexture("_BaseMap"); } if (material.HasProperty("_MainTex")) { return material.GetTexture("_MainTex"); } return null; } } internal static class SwimmingRingVisualUtility { internal static void PrepareVisual(Transform visual, string visualName) { SetActiveRecursive(visual); Renderer[] componentsInChildren = ((Component)visual).GetComponentsInChildren(true); GameApi.Debug(visualName + " renderer count: " + componentsInChildren.Length + "."); if (componentsInChildren.Length == 0) { ModLog.Warning(visualName + " has no renderers."); return; } Renderer[] array = componentsInChildren; foreach (Renderer val in array) { if (!((Object)(object)val == (Object)null)) { val.enabled = true; if (PluginConfig.ForceRuntimeOrangeMaterial.Value) { val.sharedMaterial = SwimmingRingVisualMaterial.GetRuntimeMaterial(val.sharedMaterial); } LogRenderer(val); } } } internal static void StripGameplayComponents(Transform visual) { Collider[] componentsInChildren = ((Component)visual).GetComponentsInChildren(true); Collider[] array = componentsInChildren; for (int i = 0; i < array.Length; i++) { array[i].enabled = false; } Rigidbody[] componentsInChildren2 = ((Component)visual).GetComponentsInChildren(true); Rigidbody[] array2 = componentsInChildren2; for (int i = 0; i < array2.Length; i++) { Object.Destroy((Object)(object)array2[i]); } Camera[] componentsInChildren3 = ((Component)visual).GetComponentsInChildren(true); Camera[] array3 = componentsInChildren3; for (int i = 0; i < array3.Length; i++) { ((Behaviour)array3[i]).enabled = false; } Light[] componentsInChildren4 = ((Component)visual).GetComponentsInChildren(true); Light[] array4 = componentsInChildren4; for (int i = 0; i < array4.Length; i++) { ((Behaviour)array4[i]).enabled = false; } GameApi.Debug("Prepared player visual components: disabled colliders=" + componentsInChildren.Length + ", removed rigidbodies=" + componentsInChildren2.Length + ", disabled cameras=" + componentsInChildren3.Length + ", disabled lights=" + componentsInChildren4.Length + "."); } private static void SetActiveRecursive(Transform root) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown ((Component)root).gameObject.SetActive(true); foreach (Transform item in root) { SetActiveRecursive(item); } } private static void LogRenderer(Renderer renderer) { //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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) Bounds bounds = renderer.bounds; GameApi.Debug("Prepared visual renderer: " + ((Object)renderer).name + ", enabled=" + renderer.enabled + ", activeInHierarchy=" + ((Component)renderer).gameObject.activeInHierarchy + ", material=" + SwimmingRingVisualMaterial.GetMaterialName(renderer) + ", shader=" + SwimmingRingVisualMaterial.GetShaderName(renderer) + ", texture=" + SwimmingRingVisualMaterial.GetTextureName(renderer) + ", boundsCenter=" + ((object)((Bounds)(ref bounds)).center/*cast due to .constrained prefix*/).ToString() + ", boundsSize=" + ((object)((Bounds)(ref bounds)).size/*cast due to .constrained prefix*/).ToString() + "."); } } internal static class SwimmingRingWorldVisual { private const string VisualRootName = "SwimmingRingVisual"; internal static void Apply(ItemInstance itemInstance) { if (!GameApi.IsEnabled || (Object)(object)itemInstance == (Object)null || !IsSwimmingRingInstance(itemInstance)) { return; } GameApi.Debug("Found ItemInstance for Inflatable Swimming Ring: " + ((Object)itemInstance).name + "."); Transform transform = ((Component)itemInstance).transform; Transform val = FindExistingVisual(transform); HideDefaultRenderers(transform, val); if ((Object)(object)val != (Object)null) { ApplyWorldVisualTransform(val); SwimmingRingVisualUtility.PrepareVisual(val, "SwimmingRingVisual"); return; } GameObject visualPrefab = SwimmingRingAssets.GetVisualPrefab(); if (!((Object)(object)visualPrefab == (Object)null)) { GameObject obj = Object.Instantiate(visualPrefab, transform, false); ((Object)obj).name = "SwimmingRingVisual"; ApplyWorldVisualTransform(obj.transform); SwimmingRingVisualUtility.PrepareVisual(obj.transform, "SwimmingRingVisual"); GameApi.Debug("Instantiated SwimmingRingVisual for Inflatable Swimming Ring: " + ((Object)itemInstance).name + "."); } } private static bool IsSwimmingRingInstance(ItemInstance itemInstance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return ItemRegistry.IsSwimmingRingIndex(itemInstance.InstanceData.itemIndex); } private static void HideDefaultRenderers(Transform root, Transform existingVisual) { Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !IsInsideVisual(((Component)val).transform, existingVisual)) { val.enabled = false; } } } private static bool IsInsideVisual(Transform candidate, Transform existingVisual) { if ((Object)(object)existingVisual == (Object)null) { return false; } if (!((Object)(object)candidate == (Object)(object)existingVisual)) { return candidate.IsChildOf(existingVisual); } return true; } private static Transform FindExistingVisual(Transform root) { Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)(object)root && ((Object)val).name == "SwimmingRingVisual") { return val; } } return null; } private static void ApplyWorldVisualTransform(Transform visual) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0026: 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) visual.localPosition = Vector3.up * PluginConfig.VisualHeightOffset.Value; visual.localRotation = Quaternion.identity; visual.localScale = Vector3.one * PluginConfig.VisualScale.Value; } } } namespace Joive.BurglinGnomes.SwimmingRing.Gameplay { internal static class SwimmingRingMovement { private const int WaterSurfaceDetectionMask = 1; private const int WaterMask = 16; private const float InputRiseThreshold = 0.35f; private const float IdleInputSqrThreshold = 0.01f; private const float SwimAnimationMinSpeed = 0.1f; private static readonly int SwimSpeedParameter = Animator.StringToHash("SwimSpeed"); internal static void Apply(NormalMovement movement, float dt) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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) //IL_007d: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (!GameApi.IsEnabled || !GameApi.TryGetPlayer((Component)(object)movement, out var player)) { return; } SwimmingRingVisualApplier.ApplyToPlayer(player); if (GameApi.HasEquippedSwimmingRing(player)) { if (!Utils.CheckIsInWater(player.Actor.Center, ((Component)player).transform.position, LayerMask.op_Implicit(1), LayerMask.op_Implicit(16), 0.2f)) { ResetSwimAnimation(player, dt); return; } player.Actor.ForceNotGrounded(3); ApplyHorizontalSwim(player, dt); ApplySwimAnimation(player, dt); Vector3 verticalVelocity = ((PhysicsActor)player.Actor).VerticalVelocity; verticalVelocity.y = Mathf.Max(verticalVelocity.y, 0f - PluginConfig.MaxSinkSpeed.Value); verticalVelocity.y = Mathf.Min(PluginConfig.MaxRiseSpeed.Value, verticalVelocity.y + GetRiseForce(player) * dt); ((PhysicsActor)player.Actor).VerticalVelocity = verticalVelocity; } } private static void ApplyHorizontalSwim(PlayerNetworking player, float dt) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0044: 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) Vector3 inputMovementReference = player.StateController.InputMovementReference; Vector3 planarVelocity = ((PhysicsActor)player.Actor).PlanarVelocity; if (((Vector3)(ref inputMovementReference)).sqrMagnitude > 0.01f) { Vector3 val = ((Vector3)(ref inputMovementReference)).normalized * PluginConfig.SwimSpeed.Value; ((PhysicsActor)player.Actor).PlanarVelocity = Vector3.MoveTowards(planarVelocity, val, PluginConfig.SwimAcceleration.Value * dt); } else { ((PhysicsActor)player.Actor).PlanarVelocity = planarVelocity * PluginConfig.SwimSpeedMultiplier.Value; } } private static void ApplySwimAnimation(PlayerNetworking player, float dt) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Animator animator = ((PhysicsActor)player.Actor).Animator; if (!((Object)(object)animator == (Object)null)) { CharacterActions characterActions = player.Controller.Brain.CharacterActions; float sqrMagnitude = ((Vector2)(ref characterActions.movement.value)).sqrMagnitude; float value = PluginConfig.SwimSpeed.Value; Vector3 planarVelocity = ((PhysicsActor)player.Actor).PlanarVelocity; float num = Mathf.InverseLerp(0.1f, value, ((Vector3)(ref planarVelocity)).magnitude); float num2 = ((Mathf.Max(sqrMagnitude, num) > 0.01f) ? PluginConfig.SwimAnimationSpeed.Value : 0f); float num3 = animator.GetFloat(SwimSpeedParameter); animator.SetFloat(SwimSpeedParameter, Mathf.Lerp(num3, num2, PluginConfig.SwimAnimationLerp.Value * dt)); } } private static void ResetSwimAnimation(PlayerNetworking player, float dt) { Animator animator = ((PhysicsActor)player.Actor).Animator; if (!((Object)(object)animator == (Object)null)) { float num = animator.GetFloat(SwimSpeedParameter); animator.SetFloat(SwimSpeedParameter, Mathf.Lerp(num, 0f, PluginConfig.SwimAnimationLerp.Value * dt)); } } private static float GetRiseForce(PlayerNetworking player) { //IL_0018: 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_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_0034: Unknown result type (might be due to invalid IL or missing references) float num = PluginConfig.BuoyancyForce.Value; CharacterBrain brain = player.Controller.Brain; if (brain.CharacterActions.jump.value || brain.CharacterActions.movement.value.y > 0.35f) { num += PluginConfig.ActiveRiseForce.Value; } return num; } } } namespace Joive.BurglinGnomes.SwimmingRing.Config { internal static class PluginConfig { private const int DefaultPlasticCost = 4; private const float DefaultSwimSpeedMultiplier = 0.85f; private const float DefaultSwimSpeed = 4.8f; private const float DefaultSwimAcceleration = 18f; private const float DefaultSwimAnimationSpeed = 1.15f; private const float DefaultSwimAnimationLerp = 8f; private const float DefaultBuoyancyForce = 18f; private const float DefaultActiveRiseForce = 16f; private const float DefaultMaxRiseSpeed = 5.5f; private const float DefaultMaxSinkSpeed = 0.4f; private const float DefaultWorldVisualScale = 6f; private const float DefaultWorldVisualHeightOffset = 0.25f; private const float DefaultPlayerVisualScale = 9f; private const float DefaultPlayerVisualHeightOffset = 0.35f; private const float DefaultPlayerVisualForwardOffset = -0.08f; internal static ConfigEntry EnableMod { get; private set; } internal static ConfigEntry PlasticCost { get; private set; } internal static ConfigEntry SwimSpeedMultiplier { get; private set; } internal static ConfigEntry SwimSpeed { get; private set; } internal static ConfigEntry SwimAcceleration { get; private set; } internal static ConfigEntry SwimAnimationSpeed { get; private set; } internal static ConfigEntry SwimAnimationLerp { get; private set; } internal static ConfigEntry BuoyancyForce { get; private set; } internal static ConfigEntry ActiveRiseForce { get; private set; } internal static ConfigEntry MaxRiseSpeed { get; private set; } internal static ConfigEntry MaxSinkSpeed { get; private set; } internal static ConfigEntry VisualScale { get; private set; } internal static ConfigEntry VisualHeightOffset { get; private set; } internal static ConfigEntry PlayerVisualScale { get; private set; } internal static ConfigEntry PlayerVisualHeightOffset { get; private set; } internal static ConfigEntry PlayerVisualForwardOffset { get; private set; } internal static ConfigEntry ForceRuntimeOrangeMaterial { get; private set; } internal static ConfigEntry DebugLogs { get; private set; } internal static void Bind(ConfigFile config) { EnableMod = config.Bind("General", "EnableMod", true, "Turns the mod on."); PlasticCost = config.Bind("Crafting", "PlasticCost", 4, "Plastic cost for the recipe."); SwimSpeedMultiplier = config.Bind("Swimming", "SwimSpeedMultiplier", 0.85f, "Speed kept when there is no swim input."); SwimSpeed = config.Bind("Swimming", "SwimSpeed", 4.8f, "Swim speed with the ring equipped."); SwimAcceleration = config.Bind("Swimming", "SwimAcceleration", 18f, "How fast swim movement catches up."); SwimAnimationSpeed = config.Bind("Swimming", "SwimAnimationSpeed", 1.15f, "Swim animation value while moving."); SwimAnimationLerp = config.Bind("Swimming", "SwimAnimationLerp", 8f, "Swim animation blend speed."); BuoyancyForce = config.Bind("Swimming", "BuoyancyForce", 18f, "Upward force in water."); ActiveRiseForce = config.Bind("Swimming", "ActiveRiseForce", 16f, "Extra lift from jump or forward input."); MaxRiseSpeed = config.Bind("Swimming", "MaxRiseSpeed", 5.5f, "Top upward speed from the ring."); MaxSinkSpeed = config.Bind("Swimming", "MaxSinkSpeed", 0.4f, "Sink speed limit while floating."); VisualScale = config.Bind("Visual", "VisualScale", 6f, "World item visual size."); VisualHeightOffset = config.Bind("Visual", "VisualHeightOffset", 0.25f, "World item visual height."); PlayerVisualScale = config.Bind("Visual", "PlayerVisualScale", 9f, "Player visual size."); PlayerVisualHeightOffset = config.Bind("Visual", "PlayerVisualHeightOffset", 0.35f, "Player visual height."); PlayerVisualForwardOffset = config.Bind("Visual", "PlayerVisualForwardOffset", -0.08f, "Player visual forward offset."); ForceRuntimeOrangeMaterial = config.Bind("Visual", "ForceRuntimeOrangeMaterial", true, "Use the runtime orange material."); DebugLogs = config.Bind("General", "DebugLogs", false, "Write extra setup logs."); if (PlayerVisualScale.Value < 9f) { PlayerVisualScale.Value = 9f; } } } }