using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
[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("Piggyback")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Piggyback")]
[assembly: AssemblyTitle("Piggyback")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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 Piggyback
{
[BepInPlugin("com.shuhia.peak.piggyback", "Piggyback", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "com.shuhia.peak.piggyback";
public const string PluginName = "Piggyback";
public const string PluginVersion = "1.0.0";
private const byte PiggybackWeightEventCode = 87;
private static readonly Harmony Harmony = new Harmony("com.shuhia.peak.piggyback");
private static readonly MethodInfo CanDoInputMethod = AccessTools.Method(typeof(Character), "CanDoInput", (Type[])null, (Type[])null);
private static readonly MethodInfo ToggleCarryPhysicsMethod = AccessTools.Method(typeof(CharacterCarrying), "ToggleCarryPhysics", (Type[])null, (Type[])null);
private static readonly MethodInfo DropMethod = AccessTools.Method(typeof(CharacterCarrying), "Drop", new Type[1] { typeof(Character) }, (Type[])null);
private static readonly MethodInfo UpdateWeightMethod = AccessTools.Method(typeof(CharacterAfflictions), "UpdateWeight", (Type[])null, (Type[])null);
private static readonly FieldInfo GrabbedPlayerField = AccessTools.Field(typeof(CharacterData), "grabbedPlayer");
private static readonly FieldInfo GrabbingPlayerField = AccessTools.Field(typeof(CharacterData), "grabbingPlayer");
private static readonly FieldInfo AfflictionsCharacterField = AccessTools.Field(typeof(CharacterAfflictions), "character");
private static readonly MethodInfo SetStatusMethod = AccessTools.Method(typeof(CharacterAfflictions), "SetStatus", new Type[3]
{
typeof(STATUSTYPE),
typeof(float),
typeof(bool)
}, (Type[])null);
internal static ConfigEntry<bool> Enabled = null;
internal static ConfigEntry<Key> AttachDetachHotkey = null;
internal static ConfigEntry<float> MaxAttachDistance = null;
internal static ConfigEntry<float> MaxAttachAngle = null;
internal static ConfigEntry<float> BreakDistance = null;
internal static ConfigEntry<bool> RequireLineOfSight = null;
internal static ConfigEntry<bool> DebugLogging = null;
private Character? _currentCarrier;
private bool _isAttached;
private float _lastToggleTime;
private readonly Dictionary<int, int> _mountedRidersByCarrier = new Dictionary<int, int>();
private readonly List<int> _staleMountedRiders = new List<int>();
internal static Plugin? Instance { get; private set; }
internal static bool IsMounted => Instance?._isAttached ?? false;
private void Awake()
{
Instance = this;
Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable local-only piggyback riding.");
AttachDetachHotkey = ((BaseUnityPlugin)this).Config.Bind<Key>("General", "AttachDetachHotkey", (Key)21, "Press to attach to a nearby teammate or detach if already mounted.");
MaxAttachDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxAttachDistance", 4f, "Maximum distance to search for a teammate to mount.");
MaxAttachAngle = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxAttachAngle", 60f, "Maximum angle in front of the rider when searching for a teammate.");
BreakDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BreakDistance", 6f, "Automatically detach if the carrier gets farther than this.");
RequireLineOfSight = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireLineOfSight", true, "Require clear line of sight to the carrier's carry point.");
DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Enable verbose logging for piggyback events.");
PhotonNetwork.NetworkingClient.EventReceived += OnNetworkEvent;
Harmony.PatchAll(typeof(Plugin));
((BaseUnityPlugin)this).Logger.LogInfo((object)"Piggyback loaded.");
}
private void OnDestroy()
{
TryDetach("plugin destroyed");
PhotonNetwork.NetworkingClient.EventReceived -= OnNetworkEvent;
Harmony.UnpatchSelf();
if (Instance == this)
{
Instance = null;
}
}
private void Update()
{
if (!Enabled.Value)
{
if (_isAttached)
{
TryDetach("disabled");
}
return;
}
if (_isAttached)
{
ValidateMountedState();
}
if (HotkeyPressed())
{
TogglePiggyback();
}
}
private bool HotkeyPressed()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
Keyboard current = Keyboard.current;
if (current == null)
{
return false;
}
Key value = AttachDetachHotkey.Value;
if ((int)value == 0)
{
return false;
}
KeyControl val = current[value];
if (val != null)
{
return ((ButtonControl)val).wasPressedThisFrame;
}
return false;
}
private void TogglePiggyback()
{
if (Time.unscaledTime - _lastToggleTime < 0.2f)
{
return;
}
Character local = Character.localCharacter;
if (local == null || !InvokeCanDoInput(local))
{
return;
}
_lastToggleTime = Time.unscaledTime;
if (_isAttached)
{
TryDetach("hotkey");
}
else if (TryGetLocalCharacter(out local))
{
Character val = FindBestCarrier(local);
if (val == null)
{
LogDebug("No valid piggyback target found.");
}
else
{
AttachToCarrier(local, val);
}
}
}
private Character? FindBestCarrier(Character local)
{
Character result = null;
float num = float.MaxValue;
foreach (Character allCharacter in Character.AllCharacters)
{
if (IsValidCarrier(local, allCharacter, out var distance) && distance < num)
{
result = allCharacter;
num = distance;
}
}
return result;
}
private bool IsValidCarrier(Character local, Character? candidate, out float distance)
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: 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_0068: 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)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
distance = float.MaxValue;
if (candidate == null || (Object)(object)candidate == (Object)(object)local)
{
return false;
}
if (candidate.isBot || candidate.data.dead || candidate.data.fullyPassedOut)
{
return false;
}
if (candidate.refs == null || candidate.refs.carryPosRef == null)
{
return false;
}
Vector3 center = local.Center;
Vector3 position = candidate.refs.carryPosRef.position;
Vector3 val = position - center;
distance = ((Vector3)(ref val)).magnitude;
if (distance <= 0.001f || distance > MaxAttachDistance.Value)
{
return false;
}
if (Vector3.Angle(local.data.lookDirection, val) > MaxAttachAngle.Value)
{
return false;
}
if (RequireLineOfSight.Value)
{
return HasLineOfSight(local, candidate, center, position);
}
return true;
}
private static bool HasLineOfSight(Character local, Character target, Vector3 riderCenter, Vector3 targetPoint)
{
//IL_0029: 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_002e: 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_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: 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_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: 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_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = ((((Vector3)(ref local.data.lookDirection)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref local.data.lookDirection)).normalized : Vector3.forward);
Vector3 val2 = riderCenter + val * 0.25f;
Vector3 val3 = targetPoint - val2;
float magnitude = ((Vector3)(ref val3)).magnitude;
if (magnitude <= 0.001f)
{
return true;
}
RaycastHit[] array = Physics.RaycastAll(val2, ((Vector3)(ref val3)).normalized, magnitude, -1, (QueryTriggerInteraction)1);
Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
RaycastHit[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
RaycastHit val4 = array2[i];
if (((RaycastHit)(ref val4)).collider == null)
{
continue;
}
Character componentInParent = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<Character>();
if (!((Object)(object)componentInParent == (Object)(object)local) && !((Object)(object)componentInParent == (Object)(object)target))
{
Transform transform = ((Component)((RaycastHit)(ref val4)).collider).transform;
if (!transform.IsChildOf(((Component)local).transform) && !transform.IsChildOf(((Component)target).transform))
{
return false;
}
}
}
return true;
}
private void AttachToCarrier(Character local, Character carrier)
{
BreakConflictingStates(local);
local.data.carrier = carrier;
local.data.isCarried = true;
InvokeToggleCarryPhysics(local.refs.carriying, carried: true);
BroadcastMountedWeightLink(local, carrier, attached: true);
_currentCarrier = carrier;
_isAttached = true;
LogDebug("Attached to " + carrier.characterName + ".");
}
private void BreakConflictingStates(Character local)
{
if (local.data.carriedPlayer != null)
{
InvokeDrop(local.refs.carriying, local.data.carriedPlayer);
}
if (local.data.currentClimbHandle != null)
{
local.refs.climbing.CancelHandle(false);
}
if (local.data.isVineClimbing || local.data.isRopeClimbing || local.data.isClimbing)
{
local.refs.climbing.StopAnyClimbing();
}
if (GetGrabbedPlayer(local.data) != null)
{
BroadcastGrabUnattach(local);
}
Character grabbingPlayer = GetGrabbingPlayer(local.data);
if (grabbingPlayer != null)
{
BroadcastGrabUnattach(grabbingPlayer);
}
}
private void ValidateMountedState()
{
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
Character localCharacter = Character.localCharacter;
if (localCharacter == null)
{
ClearMountedState();
}
else if (_currentCarrier == null)
{
TryDetach("carrier missing");
}
else if (localCharacter.data.dead || localCharacter.data.fullyPassedOut)
{
TryDetach("rider incapacitated");
}
else if (_currentCarrier.data.dead || _currentCarrier.data.fullyPassedOut)
{
TryDetach("carrier incapacitated");
}
else if (_currentCarrier.refs == null || _currentCarrier.refs.carryPosRef == null)
{
TryDetach("carrier carry point missing");
}
else if (!Character.AllCharacters.Contains(_currentCarrier))
{
TryDetach("carrier despawned");
}
else if (!localCharacter.data.isCarried || (Object)(object)localCharacter.data.carrier != (Object)(object)_currentCarrier)
{
TryDetach("mounted state changed externally");
}
else if (Vector3.Distance(localCharacter.Center, _currentCarrier.refs.carryPosRef.position) > BreakDistance.Value)
{
TryDetach("carrier out of range");
}
}
private void TryDetach(string reason)
{
Character localCharacter = Character.localCharacter;
if (localCharacter != null)
{
if (_currentCarrier != null)
{
BroadcastMountedWeightLink(localCharacter, _currentCarrier, attached: false);
}
InvokeToggleCarryPhysics(localCharacter.refs.carriying, carried: false);
localCharacter.data.carrier = null;
localCharacter.data.isCarried = false;
}
ClearMountedState();
LogDebug("Detached: " + reason);
}
private bool IsCustomMounted(Character rider)
{
if (_isAttached && (Object)(object)rider == (Object)(object)Character.localCharacter)
{
return (Object)(object)rider.data.carrier == (Object)(object)_currentCarrier;
}
return false;
}
private void OnNetworkEvent(EventData eventData)
{
if (eventData.Code == 87 && eventData.CustomData is object[] array && array.Length == 3)
{
int key = Convert.ToInt32(array[0]);
int num = Convert.ToInt32(array[1]);
if (Convert.ToBoolean(array[2]))
{
_mountedRidersByCarrier[key] = num;
}
else
{
_mountedRidersByCarrier.Remove(key);
}
PhotonView val = PhotonView.Find(num);
Character val2 = ((val == null) ? null : ((Component)val).GetComponent<Character>());
if (val2 != null && val2.refs.view.IsMine)
{
InvokeUpdateWeight(val2.refs.afflictions);
}
}
}
private void ClearMountedState()
{
_currentCarrier = null;
_isAttached = false;
}
private static bool TryGetLocalCharacter(out Character local)
{
local = Character.localCharacter;
if (local != null && !local.data.dead)
{
return !local.data.fullyPassedOut;
}
return false;
}
private static Character? GetGrabbedPlayer(CharacterData data)
{
object? value = GrabbedPlayerField.GetValue(data);
return (Character?)((value is Character) ? value : null);
}
private static Character? GetGrabbingPlayer(CharacterData data)
{
object? value = GrabbingPlayerField.GetValue(data);
return (Character?)((value is Character) ? value : null);
}
private static void InvokeToggleCarryPhysics(CharacterCarrying carrying, bool carried)
{
ToggleCarryPhysicsMethod.Invoke(carrying, new object[1] { carried });
}
private static void InvokeDrop(CharacterCarrying carrying, Character target)
{
DropMethod.Invoke(carrying, new object[1] { target });
}
private static void InvokeUpdateWeight(CharacterAfflictions afflictions)
{
UpdateWeightMethod.Invoke(afflictions, Array.Empty<object>());
}
private static void InvokeSetStatus(CharacterAfflictions afflictions, STATUSTYPE statusType, float amount, bool pushStatus)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
SetStatusMethod.Invoke(afflictions, new object[3] { statusType, amount, pushStatus });
}
private static bool InvokeCanDoInput(Character character)
{
return (bool)CanDoInputMethod.Invoke(character, Array.Empty<object>());
}
private void BroadcastMountedWeightLink(Character rider, Character carrier, bool attached)
{
//IL_0049: 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_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Expected O, but got Unknown
if (PhotonNetwork.InRoom)
{
PhotonNetwork.RaiseEvent((byte)87, (object)new object[3]
{
rider.refs.view.ViewID,
carrier.refs.view.ViewID,
attached
}, new RaiseEventOptions
{
Receivers = (ReceiverGroup)1
}, SendOptions.SendReliable);
}
}
private float GetAdditionalMountedWeightStatus(Character carrier)
{
if (!carrier.refs.view.IsMine || _mountedRidersByCarrier.Count == 0)
{
return 0f;
}
int viewID = carrier.refs.view.ViewID;
float num = 0f;
_staleMountedRiders.Clear();
foreach (KeyValuePair<int, int> item in _mountedRidersByCarrier)
{
if (item.Value == viewID)
{
PhotonView val = PhotonView.Find(item.Key);
Character val2 = ((val == null) ? null : ((Component)val).GetComponent<Character>());
if (val2 == null || (Object)(object)val2 == (Object)(object)carrier || val2.data.dead || val2.data.fullyPassedOut)
{
_staleMountedRiders.Add(item.Key);
}
else
{
num += val2.refs.afflictions.GetCurrentStatus((STATUSTYPE)7);
}
}
}
foreach (int staleMountedRider in _staleMountedRiders)
{
_mountedRidersByCarrier.Remove(staleMountedRider);
}
return num;
}
private static void BroadcastGrabUnattach(Character grabOwner)
{
grabOwner.refs.view.RPC("RPCA_GrabUnattach", (RpcTarget)0, Array.Empty<object>());
}
internal static void ApplyMountedInput(CharacterInput input)
{
//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)
Plugin instance = Instance;
if (instance != null && instance._isAttached && Enabled.Value)
{
Character localCharacter = Character.localCharacter;
if (localCharacter != null && !((Object)(object)input != (Object)(object)localCharacter.input))
{
input.movementInput = Vector2.zero;
input.jumpWasPressed = false;
input.jumpIsPressed = false;
input.crouchIsPressed = false;
input.crouchWasPressed = false;
input.crouchToggleWasPressed = false;
input.sprintIsPressed = false;
input.sprintToggleIsPressed = false;
input.sprintWasPressed = false;
input.sprintToggleWasPressed = false;
input.interactWasPressed = false;
input.interactIsPressed = false;
input.interactWasReleased = false;
input.dropWasPressed = false;
input.dropIsPressed = false;
input.dropWasReleased = false;
}
}
}
private void LogDebug(string message)
{
if (DebugLogging.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)message);
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(CharacterInput), "Sample")]
private static void CharacterInputSamplePostfix(CharacterInput __instance)
{
ApplyMountedInput(__instance);
}
[HarmonyPostfix]
[HarmonyPatch(typeof(CharacterAfflictions), "UpdateWeight")]
private static void CharacterAfflictionsUpdateWeightPostfix(CharacterAfflictions __instance)
{
Plugin instance = Instance;
if (instance == null)
{
return;
}
object? value = AfflictionsCharacterField.GetValue(__instance);
Character val = (Character)((value is Character) ? value : null);
if (val != null)
{
if (instance.IsCustomMounted(val) && instance._currentCarrier != null)
{
instance.BroadcastMountedWeightLink(val, instance._currentCarrier, attached: true);
}
float additionalMountedWeightStatus = instance.GetAdditionalMountedWeightStatus(val);
if (!(additionalMountedWeightStatus <= 0f))
{
float amount = __instance.GetCurrentStatus((STATUSTYPE)7) + additionalMountedWeightStatus;
InvokeSetStatus(__instance, (STATUSTYPE)7, amount, pushStatus: true);
}
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(Character), "BreakCharacterCarrying")]
private static bool CharacterBreakCharacterCarryingPrefix(Character __instance)
{
Plugin instance = Instance;
if (instance == null || !instance.IsCustomMounted(__instance))
{
return true;
}
instance.TryDetach("break carry");
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(CharacterAfflictions), "AddAffliction")]
private static void CharacterAfflictionsAddAfflictionPrefix(CharacterAfflictions __instance, ref bool __state)
{
__state = false;
Plugin instance = Instance;
if (instance != null && instance._isAttached)
{
object? value = AfflictionsCharacterField.GetValue(__instance);
Character val = (Character)((value is Character) ? value : null);
if (val != null && instance.IsCustomMounted(val) && val.data.isCarried)
{
val.data.isCarried = false;
__state = true;
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(CharacterAfflictions), "AddAffliction")]
private static void CharacterAfflictionsAddAfflictionPostfix(CharacterAfflictions __instance, bool __state)
{
if (!__state)
{
return;
}
Plugin instance = Instance;
if (instance != null)
{
object? value = AfflictionsCharacterField.GetValue(__instance);
Character val = (Character)((value is Character) ? value : null);
if (val != null && instance.IsCustomMounted(val))
{
val.data.isCarried = true;
}
}
}
}
}