using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.Thanks.We're best friends")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("com.github.Thanks.We're best friends")]
[assembly: AssemblyTitle("com.github.Thanks.We're best friends")]
[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 PeakAutoFartLocal
{
[BepInPlugin("com.github.Thanks.PeakAutoFartLocal", "We're Best Friends", "0.0.1")]
public sealed class AutoFartPlugin : BaseUnityPlugin
{
public const string PluginGuid = "com.github.Thanks.PeakAutoFartLocal";
public const string PluginName = "We're Best Friends";
public const string PluginVersion = "0.0.1";
private const string CurrentModeDescription = "April Fools' Day mode: improve player experience by turning nearby teammates into surprise local fart triggers.";
private const float ActivationDelaySeconds = 30f;
private const float TriggerRadius = 2.5f;
private const float ExitRadius = 4f;
private const float BlastRadius = 4f;
private const float TriggerCooldownSeconds = 0.75f;
private const float HostTriggerCooldownSeconds = 1.25f;
private const float ScanIntervalSeconds = 0.15f;
private const float SafetyCheckIntervalSeconds = 0.2f;
private const float OriginalFartForce = 350f;
private const float ClientTriggerChance = 1f;
private const float HostTriggerChance = 1f;
private const float NarrowSpaceCheckRadius = 1f;
private const int NarrowSpaceColliderThreshold = 18;
private const string ExplosionVfxPath = "VFX_SporeExploExploEdibleSpawn";
private const string ExplosionNoKnockbackVfxPath = "VFX_SporeExploExploEdibleSpawn_NoKnockback";
private readonly HashSet<int> _nearbyActivePlayers = new HashSet<int>();
private readonly Dictionary<int, HashSet<int>> _hostNearbyPlayersBySource = new Dictionary<int, HashSet<int>>();
private readonly List<Character> _scanBuffer = new List<Character>();
private readonly List<Character> _roomCharacterBuffer = new List<Character>();
private readonly List<int> _actorIdBuffer = new List<int>();
private readonly Collider[] _narrowSpaceBuffer = (Collider[])(object)new Collider[24];
private float _nextAllowedTriggerAt;
private float _nextScanAt;
private float _nextSafetyCheckAt;
private float _gameplayEnteredAt = -1f;
private bool _cachedSafetySuppressed;
private bool _loggedActivationDelay;
private string? _lastSafetyReason;
private bool _loggedSafetySuppression;
private bool _loggedRescueSuppression;
private void Awake()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"We're Best Friends loaded. April Fools' Day mode: improve player experience by turning nearby teammates into surprise local fart triggers. Host-authoritative multiplayer sync is active for nearby players, and clients can still fall back to local behavior when joining unmodded hosts.");
ResetState();
}
private void Update()
{
if ((Object)(object)GameHandler.Instance == (Object)null || !GameHandler.IsInGameplayScene)
{
ResetState();
return;
}
if (!IsGameplayActivationReady())
{
_nearbyActivePlayers.Clear();
_scanBuffer.Clear();
return;
}
if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
{
HandleHostAuthoritativeRoom();
return;
}
if (!TryGetLocalCharacter(out Character character))
{
_nearbyActivePlayers.Clear();
_scanBuffer.Clear();
return;
}
if (!IsCompatibleMultiplayerSession())
{
_nearbyActivePlayers.Clear();
_scanBuffer.Clear();
return;
}
if (IsSafetySuppressed(character))
{
if (!_loggedSafetySuppression)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("PeakAutoFartLocal paused because a safety condition is active: " + (_lastSafetyReason ?? "unknown") + "."));
_loggedSafetySuppression = true;
}
_nearbyActivePlayers.Clear();
_scanBuffer.Clear();
return;
}
_loggedSafetySuppression = false;
if (Time.time < _nextScanAt)
{
return;
}
_nextScanAt = Time.time + 0.15f;
if (ScanNearbyPlayers(character))
{
if (!_loggedRescueSuppression)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"PeakAutoFartLocal paused because a downed teammate is nearby.");
_loggedRescueSuppression = true;
}
_nearbyActivePlayers.Clear();
return;
}
_loggedRescueSuppression = false;
Character val = null;
bool flag = false;
foreach (Character item in _scanBuffer)
{
int characterIdentity = GetCharacterIdentity(item);
if (_nearbyActivePlayers.Add(characterIdentity))
{
flag = true;
if (val == null)
{
val = item;
}
}
}
SyncTrackedActorIds(_nearbyActivePlayers, _scanBuffer);
if (flag && !((Object)(object)val == (Object)null) && !(Time.time < _nextAllowedTriggerAt))
{
if (!ShouldTriggerForThisEntry())
{
_nextAllowedTriggerAt = Time.time + GetTriggerCooldownSeconds();
}
else if (TryTriggerEffect(character, val))
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"PeakAutoFartLocal original fart effect triggered successfully.");
_nextAllowedTriggerAt = Time.time + GetTriggerCooldownSeconds();
}
}
}
private void HandleHostAuthoritativeRoom()
{
if (Time.time < _nextScanAt)
{
return;
}
_nextScanAt = Time.time + 0.15f;
_roomCharacterBuffer.Clear();
List<Character> allPlayerCharacters = PlayerHandler.GetAllPlayerCharacters();
if (allPlayerCharacters == null || allPlayerCharacters.Count <= 1)
{
_hostNearbyPlayersBySource.Clear();
return;
}
foreach (Character item in allPlayerCharacters)
{
if (!((Object)(object)item == (Object)null) && !((Object)(object)item.data == (Object)null) && ((Component)item).gameObject.activeInHierarchy)
{
_roomCharacterBuffer.Add(item);
}
}
if (HasNearbyDownedPair(_roomCharacterBuffer))
{
if (!_loggedRescueSuppression)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"PeakAutoFartLocal host scan paused because a downed teammate is nearby.");
_loggedRescueSuppression = true;
}
_hostNearbyPlayersBySource.Clear();
return;
}
_loggedRescueSuppression = false;
Character val = null;
foreach (Character item2 in _roomCharacterBuffer)
{
bool num = ScanNearbyPlayersForSource(item2, _roomCharacterBuffer);
int characterIdentity = GetCharacterIdentity(item2);
if (num)
{
_hostNearbyPlayersBySource.Remove(characterIdentity);
continue;
}
if (!_hostNearbyPlayersBySource.TryGetValue(characterIdentity, out HashSet<int> value))
{
value = new HashSet<int>();
_hostNearbyPlayersBySource[characterIdentity] = value;
}
bool flag = false;
foreach (Character item3 in _scanBuffer)
{
int characterIdentity2 = GetCharacterIdentity(item3);
if (value.Add(characterIdentity2))
{
flag = true;
}
}
SyncTrackedActorIds(value, _scanBuffer);
if (flag && (Object)(object)val == (Object)null)
{
val = item2;
}
}
if ((Object)(object)val == (Object)null || Time.time < _nextAllowedTriggerAt || !ShouldTriggerForThisEntry())
{
if ((Object)(object)val != (Object)null && Time.time >= _nextAllowedTriggerAt)
{
_nextAllowedTriggerAt = Time.time + GetTriggerCooldownSeconds();
}
}
else if (TryTriggerBlast(val, _roomCharacterBuffer))
{
((BaseUnityPlugin)this).Logger.LogInfo((object)$"PeakAutoFartLocal host blast triggered for actor {GetCharacterIdentity(val)}.");
_nextAllowedTriggerAt = Time.time + GetTriggerCooldownSeconds();
}
}
private bool ScanNearbyPlayers(Character localCharacter)
{
//IL_0020: 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_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_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
_scanBuffer.Clear();
List<Character> allPlayerCharacters = PlayerHandler.GetAllPlayerCharacters();
if (allPlayerCharacters == null || allPlayerCharacters.Count <= 1)
{
return false;
}
Vector3 center = localCharacter.Center;
foreach (Character item in allPlayerCharacters)
{
if (!IsRelevantOtherCharacter(localCharacter, item))
{
continue;
}
int characterIdentity = GetCharacterIdentity(item);
Vector3 val = item.Center - center;
float num = (_nearbyActivePlayers.Contains(characterIdentity) ? 4f : 2.5f);
if (!(((Vector3)(ref val)).sqrMagnitude > num * num))
{
if (IsDowned(item))
{
return true;
}
_scanBuffer.Add(item);
}
}
return false;
}
private bool HasNearbyDownedPair(List<Character> characters)
{
//IL_000d: 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_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: 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)
for (int i = 0; i < characters.Count; i++)
{
Character val = characters[i];
Vector3 center = val.Center;
for (int j = 0; j < characters.Count; j++)
{
if (i == j)
{
continue;
}
Character val2 = characters[j];
if (IsRelevantOtherCharacter(val, val2) && IsDowned(val2))
{
Vector3 val3 = val2.Center - center;
if (((Vector3)(ref val3)).sqrMagnitude <= 16f)
{
return true;
}
}
}
}
return false;
}
private bool ScanNearbyPlayersForSource(Character sourceCharacter, List<Character> characters)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
_scanBuffer.Clear();
Vector3 center = sourceCharacter.Center;
foreach (Character character in characters)
{
if (!IsRelevantOtherCharacter(sourceCharacter, character))
{
continue;
}
int characterIdentity = GetCharacterIdentity(sourceCharacter);
int characterIdentity2 = GetCharacterIdentity(character);
HashSet<int> value = null;
_hostNearbyPlayersBySource.TryGetValue(characterIdentity, out value);
float num = ((value != null && value.Contains(characterIdentity2)) ? 4f : 2.5f);
Vector3 val = character.Center - center;
if (!(((Vector3)(ref val)).sqrMagnitude > num * num))
{
if (IsDowned(character))
{
return true;
}
_scanBuffer.Add(character);
}
}
return false;
}
private void SyncTrackedActorIds(HashSet<int> trackedActorIds, List<Character> characters)
{
_actorIdBuffer.Clear();
foreach (Character character in characters)
{
_actorIdBuffer.Add(GetCharacterIdentity(character));
}
trackedActorIds.IntersectWith(_actorIdBuffer);
}
private static bool TryGetLocalCharacter(out Character? character)
{
character = null;
if ((Object)(object)Player.localPlayer == (Object)null)
{
return false;
}
character = Player.localPlayer.character;
if ((Object)(object)character == (Object)null || !character.IsLocal || !((Component)character).gameObject.activeInHierarchy)
{
character = null;
return false;
}
return true;
}
private bool IsGameplayActivationReady()
{
if (!GameHandler.IsInGameplayScene)
{
_gameplayEnteredAt = -1f;
_loggedActivationDelay = false;
return false;
}
if (_gameplayEnteredAt < 0f)
{
_gameplayEnteredAt = Time.time;
}
if (Time.time - _gameplayEnteredAt >= 30f)
{
_loggedActivationDelay = false;
return true;
}
if (!_loggedActivationDelay)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)$"PeakAutoFartLocal waiting {30f:0} seconds after gameplay starts before enabling effects.");
_loggedActivationDelay = true;
}
return false;
}
private bool ShouldSuppressForSafety(Character localCharacter, out string? reason)
{
if (IsLocalRescuing(localCharacter))
{
reason = "reviving";
return true;
}
if (IsInNarrowSpace(localCharacter))
{
reason = "narrow_space";
return true;
}
reason = null;
return false;
}
private bool IsSafetySuppressed(Character localCharacter)
{
if (Time.time >= _nextSafetyCheckAt)
{
_nextSafetyCheckAt = Time.time + 0.2f;
_cachedSafetySuppressed = ShouldSuppressForSafety(localCharacter, out _lastSafetyReason);
}
return _cachedSafetySuppressed;
}
private static bool IsCompatibleMultiplayerSession()
{
if (PhotonNetwork.InRoom)
{
return true;
}
List<Character> allPlayerCharacters = PlayerHandler.GetAllPlayerCharacters();
if (allPlayerCharacters != null)
{
return allPlayerCharacters.Count > 1;
}
return false;
}
private static bool IsLocalRescuing(Character localCharacter)
{
if (HasTruthyMember(localCharacter, "revive", "reviving", "isreviving"))
{
return true;
}
return HasTruthyMember(Player.localPlayer, "revive", "reviving", "isreviving");
}
private bool IsInNarrowSpace(Character localCharacter)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
int num = Physics.OverlapSphereNonAlloc(localCharacter.Center, 1f, _narrowSpaceBuffer, -1, (QueryTriggerInteraction)1);
int num2 = 0;
for (int i = 0; i < num; i++)
{
Collider val = _narrowSpaceBuffer[i];
if (!((Object)(object)val == (Object)null))
{
Rigidbody attachedRigidbody = val.attachedRigidbody;
if (!((Object)(object)attachedRigidbody != (Object)null) || !((Component)attachedRigidbody).transform.IsChildOf(((Component)localCharacter).transform))
{
num2++;
}
}
}
Array.Clear(_narrowSpaceBuffer, 0, _narrowSpaceBuffer.Length);
return num2 >= 18;
}
private static bool IsRelevantOtherCharacter(Character localCharacter, Character otherCharacter)
{
if ((Object)(object)otherCharacter != (Object)null && (Object)(object)otherCharacter != (Object)(object)localCharacter && (Object)(object)otherCharacter.data != (Object)null)
{
return ((Component)otherCharacter).gameObject.activeInHierarchy;
}
return false;
}
private static bool IsDowned(Character character)
{
if (!character.data.dead && !character.data.passedOut)
{
return character.data.fullyPassedOut;
}
return true;
}
private static int GetCharacterIdentity(Character character)
{
PhotonView component = ((Component)character).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null && component.OwnerActorNr > 0)
{
return component.OwnerActorNr;
}
return ((Object)character).GetInstanceID();
}
private static bool HasTruthyMember(object? target, params string[] tokens)
{
if (target == null)
{
return false;
}
Type type = target.GetType();
BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo[] properties = type.GetProperties(bindingAttr);
foreach (PropertyInfo propertyInfo in properties)
{
if (NameMatches(propertyInfo.Name, tokens) && propertyInfo.GetIndexParameters().Length == 0)
{
object value;
try
{
value = propertyInfo.GetValue(target);
}
catch
{
continue;
}
if (IsTruthy(value))
{
return true;
}
}
}
FieldInfo[] fields = type.GetFields(bindingAttr);
foreach (FieldInfo fieldInfo in fields)
{
if (NameMatches(fieldInfo.Name, tokens))
{
object value2;
try
{
value2 = fieldInfo.GetValue(target);
}
catch
{
continue;
}
if (IsTruthy(value2))
{
return true;
}
}
}
return false;
}
private static bool NameMatches(string name, string[] tokens)
{
string text = name.Replace("_", string.Empty).ToLowerInvariant();
foreach (string value in tokens)
{
if (text.Contains(value))
{
return true;
}
}
return false;
}
private static bool IsTruthy(object? value)
{
if (value != null)
{
if (!(value is bool result))
{
if (!(value is int num))
{
if (!(value is long num2))
{
if (!(value is float value2))
{
if (value is double value3)
{
return Math.Abs(value3) > 0.001;
}
return false;
}
return Math.Abs(value2) > 0.001f;
}
return num2 != 0;
}
return num != 0;
}
return result;
}
return false;
}
private static bool ShouldTriggerForThisEntry()
{
return Random.value <= GetTriggerChance();
}
private static float GetTriggerChance()
{
if (PhotonNetwork.IsConnected)
{
_ = PhotonNetwork.IsMasterClient;
return 1f;
}
return 1f;
}
private static float GetTriggerCooldownSeconds()
{
if (PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient)
{
return 1.25f;
}
return 0.75f;
}
private bool TryTriggerEffect(Character localCharacter, Character triggerCharacter)
{
try
{
return (PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient) ? TryTriggerBlast(triggerCharacter, _roomCharacterBuffer) : ApplyOriginalFartBlast(localCharacter, _scanBuffer);
}
catch (Exception arg)
{
((BaseUnityPlugin)this).Logger.LogError((object)$"PeakAutoFartLocal failed to trigger the original mushroom fart effect: {arg}");
return false;
}
}
private bool TryTriggerBlast(Character sourceCharacter, List<Character> characters)
{
return ApplyOriginalFartBlast(sourceCharacter, characters);
}
private bool ApplyOriginalFartBlast(Character sourceCharacter, IEnumerable<Character> potentialTargets)
{
//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_001e: 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_004c: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
Vector3 center = sourceCharacter.Center;
if ((Object)(object)GameUtils.instance != (Object)null)
{
GameUtils.instance.SpawnResourceAtPositionNetworked("VFX_SporeExploExploEdibleSpawn", center, (RpcTarget)5);
GameUtils.instance.RPC_SpawnResourceAtPosition("VFX_SporeExploExploEdibleSpawn_NoKnockback", center);
}
bool result = false;
foreach (Character potentialTarget in potentialTargets)
{
if (IsValidBlastTarget(sourceCharacter, potentialTarget, center))
{
PhotonView val = potentialTarget.refs?.view ?? ((Component)potentialTarget).GetComponent<PhotonView>();
if ((Object)(object)val == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"PeakAutoFartLocal could not find a PhotonView on a fart blast target.");
continue;
}
val.RPC("RPCA_AddForceToBodyPart", (RpcTarget)5, new object[3]
{
(object)(BodypartType)0,
Vector3.zero,
Vector3.up * 350f
});
result = true;
}
}
return result;
}
private static bool IsValidBlastTarget(Character sourceCharacter, Character targetCharacter, Vector3 blastCenter)
{
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: 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)
if ((Object)(object)targetCharacter == (Object)null || (Object)(object)targetCharacter == (Object)(object)sourceCharacter || (Object)(object)targetCharacter.data == (Object)null || !((Component)targetCharacter).gameObject.activeInHierarchy || IsDowned(targetCharacter))
{
return false;
}
Vector3 val = targetCharacter.Center - blastCenter;
return ((Vector3)(ref val)).sqrMagnitude <= 16f;
}
private void ResetState()
{
_nearbyActivePlayers.Clear();
_scanBuffer.Clear();
_nextAllowedTriggerAt = Time.time;
_nextScanAt = Time.time;
_nextSafetyCheckAt = Time.time;
_gameplayEnteredAt = -1f;
_cachedSafetySuppressed = false;
_lastSafetyReason = null;
_loggedActivationDelay = false;
_loggedSafetySuppression = false;
_loggedRescueSuppression = false;
}
}
}