using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SeaLegs")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Azumatt")]
[assembly: AssemblyProduct("SeaLegs")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B24BE9E0-1351-4951-AE19-B645B64CA6C5")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class ExtensionMarkerAttribute : Attribute
{
public ExtensionMarkerAttribute(string name)
{
}
}
}
namespace SeaLegs
{
internal sealed class LocalBodyDriver : MonoBehaviour
{
private Player _player;
private OtherPlayer _other;
private bool _wasOnBoat;
internal void Init(Player player)
{
_player = player;
_other = player.Other;
_wasOnBoat = player.Movement.OnBoat;
Sync();
}
private void Update()
{
if (Object.op_Implicit((Object)(object)_player) && Object.op_Implicit((Object)(object)_other) && SeaLegsPlugin.IsEnabled)
{
Sync();
}
}
private void Sync()
{
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: 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_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
if (Object.op_Implicit((Object)(object)_player.CamObject) && Object.op_Implicit((Object)(object)_player.Transform) && Object.op_Implicit((Object)(object)_other._transform))
{
bool onBoat = _player.Movement.OnBoat;
if (onBoat != _wasOnBoat)
{
_wasOnBoat = onBoat;
ClearVelSamples();
}
_other.OnBoat = onBoat;
Vector3 localEulerAngles = _player.CamObject.localEulerAngles;
Quaternion val = Quaternion.Euler(0f, localEulerAngles.y, 0f);
_other._transform.position = _player.Transform.position + val * SeaLegsPlugin.BodyOffset;
_other._transform.rotation = val;
_other._camProxy.localRotation = Quaternion.Euler(localEulerAngles.x, 0f, 0f);
}
}
private void ClearVelSamples()
{
_other._velSamples.Clear();
}
}
internal sealed class ShadowHeadFollow : MonoBehaviour
{
private Transform _head;
internal void Follow(Transform head)
{
_head = head;
}
private void LateUpdate()
{
//IL_001a: 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)
if (Object.op_Implicit((Object)(object)_head))
{
((Component)this).transform.localPosition = _head.localPosition;
((Component)this).transform.localRotation = _head.localRotation;
}
}
}
[BepInPlugin("Azumatt.SeaLegs", "SeaLegs", "1.0.0")]
public class SeaLegsPlugin : BaseUnityPlugin
{
public enum Toggle
{
On = 1,
Off = 0
}
internal const string ModName = "SeaLegs";
internal const string ModVersion = "1.0.0";
internal const string Author = "Azumatt";
private const string ModGUID = "Azumatt.SeaLegs";
private static string ConfigFileName = "Azumatt.SeaLegs.cfg";
private static string ConfigFileFullPath;
private readonly Harmony _harmony = new Harmony("Azumatt.SeaLegs");
public static readonly ManualLogSource SeaLegsLogger;
internal static ConfigEntry<Toggle> ModEnabled;
internal static ConfigEntry<string> BodyOffsetText;
internal static ConfigEntry<string> ComponentAllowList;
private static string _offsetRaw;
private static Vector3 _offset;
internal static bool IsEnabled
{
get
{
ConfigEntry<Toggle> modEnabled = ModEnabled;
if (modEnabled != null)
{
return modEnabled.Value == Toggle.On;
}
return false;
}
}
internal static Vector3 BodyOffset
{
get
{
//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_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
string value = BodyOffsetText.Value;
if (value == _offsetRaw)
{
return _offset;
}
_offsetRaw = value;
_offset = ParseVector(value);
return _offset;
}
}
public void Awake()
{
bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
ModEnabled = config("1 - General", "Enabled", Toggle.On, "Turn the whole mod off without unloading it. Takes effect next time you load into a world.");
BodyOffsetText = config("2 - Body", "Body Offset", "0, 0, -0.2", "x, y, z nudge for the whole body, in body space, metres. Positive z is the way you are facing. The default sits it back far enough that you are not standing in your own collar. Big values not recommended.");
ComponentAllowList = config("3 - Advanced", "Components To Enable", "OtherPlayer,PlayerBody,PlayerLegs,PlayerArms,IK", "Comma separated. These are the components the game switches off on your own player. Anything not in this list stays off.");
Assembly executingAssembly = Assembly.GetExecutingAssembly();
_harmony.PatchAll(executingAssembly);
SetupWatcher();
if (saveOnConfigSet)
{
((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
((BaseUnityPlugin)this).Config.Save();
}
}
private void OnDestroy()
{
((BaseUnityPlugin)this).Config.Save();
}
private void SetupWatcher()
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
fileSystemWatcher.Changed += ReadConfigValues;
fileSystemWatcher.Created += ReadConfigValues;
fileSystemWatcher.Renamed += ReadConfigValues;
fileSystemWatcher.IncludeSubdirectories = true;
fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
fileSystemWatcher.EnableRaisingEvents = true;
}
private void ReadConfigValues(object sender, FileSystemEventArgs e)
{
if (!File.Exists(ConfigFileFullPath))
{
return;
}
try
{
((BaseUnityPlugin)this).Config.Reload();
}
catch
{
SeaLegsLogger.LogError((object)("There was an issue loading your " + ConfigFileName));
SeaLegsLogger.LogError((object)"Please check your config entries for spelling and format!");
}
}
internal static bool IsLocal(Player? player)
{
if ((Object)(object)player != (Object)null && (Object)(object)Player.LocalPlayer != (Object)null)
{
return (Object)(object)player == (Object)(object)Player.LocalPlayer;
}
return false;
}
private static Vector3 ParseVector(string raw)
{
//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_004e: Unknown result type (might be due to invalid IL or missing references)
string[] array = raw.Split(new char[1] { ',' });
Vector3 zero = Vector3.zero;
for (int i = 0; i < 3 && i < array.Length; i++)
{
if (float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
((Vector3)(ref zero))[i] = result;
}
}
return zero;
}
private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description)
{
return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description);
}
private ConfigEntry<T> config<T>(string group, string name, T value, string description)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Expected O, but got Unknown
return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()));
}
static SeaLegsPlugin()
{
string configPath = Paths.ConfigPath;
char directorySeparatorChar = Path.DirectorySeparatorChar;
ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
SeaLegsLogger = Logger.CreateLogSource("SeaLegs");
ModEnabled = null;
BodyOffsetText = null;
ComponentAllowList = null;
_offsetRaw = null;
}
}
public static class ToggleExtensions
{
[SpecialName]
public sealed class <G>$3B6D679A65CD5647F9CC4965B58E8041
{
[SpecialName]
public static class <M>$48F04745A371B4424895B07F25A43FD8
{
}
[ExtensionMarker("<M>$48F04745A371B4424895B07F25A43FD8")]
public bool IsOn()
{
throw null;
}
[ExtensionMarker("<M>$48F04745A371B4424895B07F25A43FD8")]
public bool IsOff()
{
throw null;
}
}
public static bool IsOn(this SeaLegsPlugin.Toggle toggle)
{
return toggle == SeaLegsPlugin.Toggle.On;
}
public static bool IsOff(this SeaLegsPlugin.Toggle toggle)
{
return toggle == SeaLegsPlugin.Toggle.Off;
}
}
internal static class LocalBody
{
internal static bool Active { get; private set; }
internal static void Build(Player player, List<GameObject> bodyObjects)
{
try
{
foreach (GameObject bodyObject in bodyObjects)
{
if (Object.op_Implicit((Object)(object)bodyObject))
{
bodyObject.SetActive(true);
}
}
EnableAllowedComponents(player);
player.Skin.InitializeOther();
player.Body.ToggleOldModel(player._isBean.Value);
OtherPlayer other = player.Other;
if (Object.op_Implicit((Object)(object)other._col))
{
((Collider)other._col).enabled = false;
}
ApplyHead(player);
if (Object.op_Implicit((Object)(object)player.Body._nameTextCanvasHolder))
{
((Component)player.Body._nameTextCanvasHolder).gameObject.SetActive(false);
}
if (Object.op_Implicit((Object)(object)other._otherPlayerCanvasHolder))
{
((Component)other._otherPlayerCanvasHolder).gameObject.SetActive(false);
}
LocalBodyDriver localBodyDriver = ((Component)player).gameObject.GetComponent<LocalBodyDriver>();
if (!Object.op_Implicit((Object)(object)localBodyDriver))
{
localBodyDriver = ((Component)player).gameObject.AddComponent<LocalBodyDriver>();
}
localBodyDriver.Init(player);
Active = true;
SeaLegsPlugin.SeaLegsLogger.LogInfo((object)$"Body restored, {bodyObjects.Count} object(s) kept alive");
}
catch (Exception arg)
{
Active = false;
SeaLegsPlugin.SeaLegsLogger.LogError((object)$"Failed to restore the local body: {arg}");
}
}
internal static void Clear()
{
Active = false;
}
private static void ApplyHead(Player player)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
Transform head = player.Body.Head;
if (Object.op_Implicit((Object)(object)head))
{
BuildShadowHead(player, head);
head.localScale = Vector3.zero;
}
}
private static void BuildShadowHead(Player player, Transform head)
{
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
Transform val = (Object.op_Implicit((Object)(object)player.Other._transform) ? player.Other._transform : head.root);
Transform[] componentsInChildren = ((Component)head).GetComponentsInChildren<Transform>(true);
Transform transform = Object.Instantiate<GameObject>(((Component)head).gameObject, head.parent).transform;
((Object)transform).name = "SeaLegsShadowHead";
transform.localPosition = head.localPosition;
transform.localRotation = head.localRotation;
transform.localScale = Vector3.one;
Transform[] componentsInChildren2 = ((Component)transform).GetComponentsInChildren<Transform>(true);
if (componentsInChildren2.Length != componentsInChildren.Length)
{
Object.Destroy((Object)(object)((Component)transform).gameObject);
SeaLegsPlugin.SeaLegsLogger.LogWarning((object)$"Shadow head came out with {componentsInChildren2.Length} bones against the real {componentsInChildren.Length}. Skipping it.");
return;
}
Dictionary<Transform, Transform> dictionary = new Dictionary<Transform, Transform>();
for (int i = 0; i < componentsInChildren.Length; i++)
{
dictionary[componentsInChildren[i]] = componentsInChildren2[i];
}
Renderer[] componentsInChildren3 = ((Component)transform).GetComponentsInChildren<Renderer>(true);
for (int j = 0; j < componentsInChildren3.Length; j++)
{
componentsInChildren3[j].shadowCastingMode = (ShadowCastingMode)3;
}
int num = 0;
SkinnedMeshRenderer[] componentsInChildren4 = ((Component)val).GetComponentsInChildren<SkinnedMeshRenderer>(true);
foreach (SkinnedMeshRenderer val2 in componentsInChildren4)
{
if (!Object.op_Implicit((Object)(object)val2.sharedMesh) || val2.bones == null || !RidesHead(val2.bones, dictionary))
{
continue;
}
SkinnedMeshRenderer component = Object.Instantiate<GameObject>(((Component)val2).gameObject, ((Component)val2).transform.parent).GetComponent<SkinnedMeshRenderer>();
if (!Object.op_Implicit((Object)(object)component))
{
continue;
}
((Object)component).name = ((Object)val2).name + " (SeaLegs shadow)";
Transform[] bones = component.bones;
for (int k = 0; k < bones.Length; k++)
{
if (Object.op_Implicit((Object)(object)bones[k]) && dictionary.TryGetValue(bones[k], out var value))
{
bones[k] = value;
}
}
component.bones = bones;
if (Object.op_Implicit((Object)(object)component.rootBone) && dictionary.TryGetValue(component.rootBone, out var value2))
{
component.rootBone = value2;
}
((Renderer)component).shadowCastingMode = (ShadowCastingMode)3;
num++;
}
((Component)transform).gameObject.AddComponent<ShadowHeadFollow>().Follow(head);
SeaLegsPlugin.SeaLegsLogger.LogInfo((object)$"Shadow head built from {num} mesh(es)");
}
private static bool RidesHead(Transform[] bones, Dictionary<Transform, Transform> map)
{
foreach (Transform val in bones)
{
if (Object.op_Implicit((Object)(object)val) && map.ContainsKey(val))
{
return true;
}
}
return false;
}
private static void EnableAllowedComponents(Player player)
{
string[] array = SeaLegsPlugin.ComponentAllowList.Value.Split(new char[1] { ',' });
for (int i = 0; i < array.Length; i++)
{
array[i] = array[i].Trim();
}
MonoBehaviour[] disableWhenLocal = player._disableWhenLocal;
foreach (MonoBehaviour val in disableWhenLocal)
{
if (!Object.op_Implicit((Object)(object)val))
{
continue;
}
string name = ((object)val).GetType().Name;
string[] array2 = array;
foreach (string text in array2)
{
if (text.Length != 0 && string.Equals(text, name, StringComparison.OrdinalIgnoreCase))
{
((Behaviour)val).enabled = true;
break;
}
}
}
}
}
[HarmonyPatch(typeof(Player), "InitializePlayer")]
internal static class SaveBodyFromTheDestroyLoop
{
internal static List<GameObject>? Stashed { get; private set; }
private static void Prefix(Player __instance)
{
Stashed = null;
if (SeaLegsPlugin.IsEnabled && ((NetworkBehaviour)__instance).Owner.IsLocalClient)
{
Stashed = __instance._otherObjects;
__instance._otherObjects = new List<GameObject>();
}
}
private static void Postfix(Player __instance)
{
if (Stashed != null)
{
List<GameObject> stashed = Stashed;
Stashed = null;
__instance._otherObjects = stashed;
LocalBody.Build(__instance, stashed);
}
}
}
[HarmonyPatch(typeof(Player), "OnStopClient")]
internal static class ForgetBodyOnDisconnect
{
private static void Postfix(Player __instance)
{
if (SeaLegsPlugin.IsLocal(__instance))
{
LocalBody.Clear();
}
}
}
[HarmonyPatch(typeof(PlayerLegs), "AnimateStep")]
internal static class FlagTheBodysFootstep
{
internal static bool Stepping;
private static void Prefix(PlayerLegs __instance)
{
Stepping = SeaLegsPlugin.IsLocal(__instance._player);
}
private static void Postfix()
{
Stepping = false;
}
}
[HarmonyPatch(typeof(AudioManager), "PlayRandomPlayerClip")]
internal static class DropTheDoubledFootstep
{
private static bool Prefix(Player player)
{
if (FlagTheBodysFootstep.Stepping)
{
return !SeaLegsPlugin.IsLocal(player);
}
return true;
}
}
[HarmonyPatch(typeof(OtherPlayer), "WaterCheck")]
internal static class DropTheDoubledSplash
{
private static bool Prefix(OtherPlayer __instance)
{
return !SeaLegsPlugin.IsLocal(__instance._player);
}
}
}