using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("WaitForMEak")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+a91fcf35299222f7d23033aeba08d58084f81444")]
[assembly: AssemblyProduct("WaitForMEak")]
[assembly: AssemblyTitle("WaitForMEak")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace WaitForMEak
{
internal static class Arrival
{
private static readonly Dictionary<Character, CharacterSyncer> _syncers = new Dictionary<Character, CharacterSyncer>();
internal static bool IsStandable(Character c, out Vector3 groundPoint)
{
//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_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
groundPoint = Vector3.zero;
if ((Object)(object)c == (Object)null)
{
return false;
}
if (c.data.dead || c.data.fullyPassedOut || c.warping)
{
return false;
}
if (IsClimbing(c))
{
return false;
}
if (c.data.isCarried)
{
return false;
}
if (!c.data.isGrounded)
{
return false;
}
if (c.data.groundedFor < WaitConfig.GroundedSecondsRequired.Value)
{
return false;
}
Vector3 groundNormal = c.data.groundNormal;
if (((Vector3)(ref groundNormal)).sqrMagnitude < 0.01f)
{
return false;
}
if (Vector3.Angle(Vector3.up, groundNormal) > WaitConfig.MaxGroundSlope.Value)
{
return false;
}
if (((Vector3)(ref c.data.avarageVelocity)).magnitude > WaitConfig.MaxTargetSpeed.Value)
{
return false;
}
Vector3 val = c.data.groundPos;
if (val == Vector3.zero || Vector3.Distance(val, c.Center) > 4f)
{
val = c.Center;
}
groundPoint = val;
return true;
}
private static bool IsClimbing(Character c)
{
//IL_007b: 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_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
if (c.IsLocal)
{
if (!c.data.isClimbing && !c.data.isRopeClimbing)
{
return c.data.isVineClimbing;
}
return true;
}
CharacterSyncer syncer = GetSyncer(c);
if ((Object)(object)syncer == (Object)null || ((PhotonBinaryStreamSerializer<CharacterSyncData>)(object)syncer).RemoteValue.IsNone)
{
if (!c.data.isClimbing && !c.data.isRopeClimbing)
{
return c.data.isVineClimbing;
}
return true;
}
CharacterSyncData value = ((PhotonBinaryStreamSerializer<CharacterSyncData>)(object)syncer).RemoteValue.Value;
if (!value.isClimbing && !value.ropeClimbing)
{
return c.data.isVineClimbing;
}
return true;
}
private static CharacterSyncer GetSyncer(Character c)
{
if (_syncers.TryGetValue(c, out var value) && (Object)(object)value != (Object)null)
{
return value;
}
CharacterSyncer component = ((Component)c).GetComponent<CharacterSyncer>();
_syncers[c] = component;
return component;
}
internal static void ForgetDeadEntries()
{
if (_syncers.Count == 0)
{
return;
}
List<Character> list = null;
foreach (KeyValuePair<Character, CharacterSyncer> syncer in _syncers)
{
if (!((Object)(object)syncer.Key != (Object)null) || !((Object)(object)syncer.Value != (Object)null))
{
(list ?? (list = new List<Character>())).Add(syncer.Key);
}
}
if (list == null)
{
return;
}
foreach (Character item in list)
{
_syncers.Remove(item);
}
}
internal static Vector3 PickArrivalPosition(Vector3 groundPoint)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: 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_0047: 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_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: 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_0059: 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_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: 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_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: 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)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: 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)
Vector2 insideUnitCircle = Random.insideUnitCircle;
Vector2 val = ((Vector2)(ref insideUnitCircle)).normalized;
if (((Vector2)(ref val)).sqrMagnitude < 0.01f)
{
val = Vector2.right;
}
Vector3 val2 = new Vector3(val.x, 0f, val.y) * WaitConfig.ArrivalOffset.Value;
Vector3 val3 = groundPoint + val2 + Vector3.up * 2f;
RaycastHit val4 = HelperFunctions.LineCheck(val3, val3 + Vector3.down * 5f, (LayerType)1, 0f, (QueryTriggerInteraction)1);
return (((Object)(object)((RaycastHit)(ref val4)).transform != (Object)null) ? ((RaycastHit)(ref val4)).point : groundPoint) + Vector3.up * 1.1f;
}
}
internal static class HeldBelongings
{
internal static bool IsCarryingAnything(Character c)
{
Player val = (((Object)(object)c != (Object)null) ? c.player : null);
if ((Object)(object)val == (Object)null)
{
return false;
}
if (val.backpackSlot != null && !((ItemSlot)val.backpackSlot).IsEmpty())
{
return true;
}
if (val.tempFullSlot != null && !val.tempFullSlot.IsEmpty())
{
return true;
}
if (val.itemSlots != null)
{
ItemSlot[] itemSlots = val.itemSlots;
foreach (ItemSlot val2 in itemSlots)
{
if (val2 != null && !val2.IsEmpty())
{
return true;
}
}
}
return false;
}
internal static float[] SnapshotStatuses(Character c)
{
if ((Object)(object)c == (Object)null || c.refs == null || (Object)(object)c.refs.afflictions == (Object)null)
{
return null;
}
int num = Enum.GetNames(typeof(STATUSTYPE)).Length;
float[] array = new float[num];
for (int i = 0; i < num; i++)
{
array[i] = c.refs.afflictions.GetCurrentStatus((STATUSTYPE)i);
}
return array;
}
internal static void Revive(Character c, Vector3 position)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
((MonoBehaviourPun)c).photonView.RPC("ReviveCharacter", (RpcTarget)0, new object[1] { false });
((MonoBehaviourPun)c).photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { position, true });
}
internal static void RestoreStatuses(Character c, float[] snapshot)
{
if ((Object)(object)c == (Object)null || snapshot == null || c.refs == null || (Object)(object)c.refs.afflictions == (Object)null)
{
return;
}
float[] array = new float[snapshot.Length];
bool flag = false;
for (int i = 0; i < snapshot.Length; i++)
{
float num = snapshot[i] - c.refs.afflictions.GetCurrentStatus((STATUSTYPE)i);
if (!(Mathf.Abs(num) < 0.001f))
{
array[i] = num;
flag = true;
}
}
if (!flag)
{
Plugin.Log.LogInfo((object)(c.characterName + " came back with their statuses already intact."));
return;
}
((MonoBehaviourPun)c.refs.afflictions).photonView.RPC("RPC_ApplyStatusesFromFloatArray", (RpcTarget)0, new object[1] { array });
Plugin.Log.LogInfo((object)("Restored " + c.characterName + "'s statuses from before they were held."));
}
}
internal static class JoinCurse
{
internal static void Apply(Character c)
{
if (!((Object)(object)c == (Object)null) && c.refs != null && !((Object)(object)c.refs.afflictions == (Object)null))
{
float num = Ascents.startingCurse + (WaitConfig.CurseAsIfRevived.Value ? Ascents.revivalCurse : 0f);
float currentStatus = c.refs.afflictions.GetCurrentStatus((STATUSTYPE)5);
float num2 = num - currentStatus;
if (Mathf.Abs(num2) < 0.001f)
{
Plugin.Log.LogInfo((object)$"{c.characterName} already has the right Curse ({currentStatus:0.###}).");
return;
}
float[] array = new float[Enum.GetNames(typeof(STATUSTYPE)).Length];
array[5] = num2;
((MonoBehaviourPun)c.refs.afflictions).photonView.RPC("RPC_ApplyStatusesFromFloatArray", (RpcTarget)0, new object[1] { array });
Plugin.Log.LogInfo((object)($"Curse for {c.characterName}: {currentStatus:0.###} -> {num:0.###} " + $"(ascent start {Ascents.startingCurse:0.###}, revival " + $"{(WaitConfig.CurseAsIfRevived.Value ? Ascents.revivalCurse : 0f):0.###})."));
}
}
}
internal static class JoinPack
{
internal static void Grant(Character c)
{
if (!((Object)(object)c == (Object)null) && !((Object)(object)c.player == (Object)null))
{
switch (WaitConfig.PackForJoiners.Value)
{
case PackOnJoin.AlwaysFannypack:
GiveFresh(c, "Fannypack");
break;
case PackOnJoin.OnlyIfLeftBehind:
GiveLeftBehind(c);
break;
}
}
}
private static void GiveFresh(Character c, string prefabName)
{
Item val = default(Item);
ItemSlot val2 = default(ItemSlot);
if (!((ItemSlot)c.player.backpackSlot).IsEmpty())
{
Plugin.Log.LogInfo((object)(c.characterName + " already has a pack - not adding another."));
}
else if (!ItemDatabase.TryGetItem(prefabName, ref val) || (Object)(object)val == (Object)null)
{
Plugin.Log.LogWarning((object)("Couldn't find the '" + prefabName + "' item in the database."));
}
else if (c.player.AddItem(val.itemID, (ItemInstanceData)null, ref val2))
{
Plugin.Log.LogInfo((object)("Gave " + c.characterName + " a " + prefabName + "."));
}
else
{
Plugin.Log.LogWarning((object)("Failed to give " + c.characterName + " a " + prefabName + "."));
}
}
private static void GiveLeftBehind(Character c)
{
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f1: 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_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Invalid comparison between Unknown and I4
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Invalid comparison between Unknown and I4
if (!((ItemSlot)c.player.backpackSlot).IsEmpty())
{
Plugin.Log.LogInfo((object)(c.characterName + " already has a pack - not adding another."));
return;
}
Backpack val = null;
int num = 0;
Backpack[] array = Object.FindObjectsByType<Backpack>((FindObjectsSortMode)0);
foreach (Backpack val2 in array)
{
if (!((Object)(object)val2 == (Object)null) && (int)((Item)val2).itemState == 0 && !((Object)(object)((Item)val2).holderCharacter != (Object)null) && !(((Component)val2).transform.position.y < -100f))
{
int num2 = (((int)val2.backpackType == 1) ? 2 : (((int)val2.backpackType == 2) ? 1 : 0));
if (num2 != 0 && num2 > num)
{
num = num2;
val = val2;
}
}
}
if ((Object)(object)val == (Object)null)
{
Plugin.Log.LogInfo((object)("No pack was left behind in this run - " + c.characterName + " gets nothing."));
return;
}
Plugin.Log.LogInfo((object)($"Handing the abandoned {val.backpackType} at {((Component)val).transform.position} " + "to " + c.characterName + "."));
((Item)val).RequestPickup(((MonoBehaviourPun)c).photonView);
}
}
internal enum HoldDecision
{
Undecided,
LeaveToTheGame,
Hold
}
internal class PendingJoin
{
public int ActorNumber;
public string UserId;
public string Nickname;
public float JoinedAt;
public Character Character;
public float CharacterSeenAt;
public bool IsReconnect;
public bool Resumed;
public bool PreserveBelongings;
public HoldDecision Decision;
public bool GhostForced;
public bool Completing;
}
internal class LateJoinDirector : MonoBehaviour, IInRoomCallbacks
{
private readonly Dictionary<int, PendingJoin> _pending = new Dictionary<int, PendingJoin>();
private readonly List<PendingJoin> _scratch = new List<PendingJoin>();
private readonly Dictionary<string, bool> _interrupted = new Dictionary<string, bool>();
private float _nextPoll;
internal static LateJoinDirector Instance { get; private set; }
private void Awake()
{
Instance = this;
PhotonNetwork.AddCallbackTarget((object)this);
}
private void OnDestroy()
{
PhotonNetwork.RemoveCallbackTarget((object)this);
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
public void OnPlayerEnteredRoom(Player newPlayer)
{
if (newPlayer != null && !newPlayer.IsLocal && GameHandler.IsOnIsland && GameHandler.PlayersHaveLeftShore)
{
string userId = newPlayer.UserId;
bool value = false;
bool flag = userId != null && _interrupted.TryGetValue(userId, out value);
if (flag)
{
_interrupted.Remove(userId);
}
bool flag2 = HasReconnectData(newPlayer);
if (!flag && !WaitConfig.IncludeReconnectingPlayers.Value && flag2)
{
Plugin.Log.LogInfo((object)(newPlayer.NickName + " is reconnecting to this run - leaving them to the game."));
return;
}
_pending[newPlayer.ActorNumber] = new PendingJoin
{
ActorNumber = newPlayer.ActorNumber,
UserId = userId,
Nickname = newPlayer.NickName,
JoinedAt = Time.time,
CharacterSeenAt = -1f,
IsReconnect = (flag ? value : flag2),
Resumed = flag,
PreserveBelongings = (flag ? value : flag2),
Decision = HoldDecision.Undecided
};
Plugin.Log.LogInfo((object)(flag ? (newPlayer.NickName + " is back after dropping out mid-hold - picking up where we left off.") : (newPlayer.NickName + " joined a run in progress - working out what to do with them.")));
}
}
public void OnPlayerLeftRoom(Player otherPlayer)
{
if (otherPlayer == null)
{
return;
}
LowestScoutNotice.Forget(otherPlayer.ActorNumber);
if (_pending.TryGetValue(otherPlayer.ActorNumber, out var value))
{
_pending.Remove(otherPlayer.ActorNumber);
if (!value.Completing && value.Decision == HoldDecision.Hold && value.UserId != null)
{
_interrupted[value.UserId] = value.PreserveBelongings;
Plugin.Log.LogInfo((object)(otherPlayer.NickName + " left mid-hold - we'll pick it back up if they return."));
}
else
{
Plugin.Log.LogInfo((object)(otherPlayer.NickName + " left before we could place them."));
}
}
}
public void OnMasterClientSwitched(Player newMasterClient)
{
if (PhotonNetwork.IsMasterClient && _pending.Count != 0)
{
Plugin.Log.LogInfo((object)$"Inherited the room with {_pending.Count} joiner(s) still in hand. Carrying on.");
}
}
public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
}
public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
}
private static bool HasReconnectData(Player player)
{
ReconnectData val = default(ReconnectData);
SerializableRunBasedValues val2 = default(SerializableRunBasedValues);
return ReconnectHandler.TryGetReconnectData(player, ref val, ref val2);
}
private static bool CampfireGathersTheGroup()
{
if (!GameHandler.IsOnIsland)
{
return false;
}
return CharacterSpawner.ScoutsWereRevivedAtCurrentBaseCamp;
}
private void Update()
{
if (!PhotonNetwork.InRoom || !GameHandler.IsOnIsland)
{
if (_pending.Count > 0)
{
_pending.Clear();
}
if (_interrupted.Count > 0)
{
_interrupted.Clear();
}
}
else if (_pending.Count != 0 && !(Time.time < _nextPoll))
{
_nextPoll = Time.time + Mathf.Max(0.05f, WaitConfig.PollInterval.Value);
if (PhotonNetwork.IsMasterClient)
{
Arrival.ForgetDeadEntries();
Tick();
}
}
}
private void Tick()
{
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
Character val = FindLowest();
_scratch.Clear();
_scratch.AddRange(_pending.Values);
foreach (PendingJoin item in _scratch)
{
if (item.Completing || !ResolveCharacter(item))
{
continue;
}
if (item.Decision == HoldDecision.Undecided)
{
Decide(item);
if (item.Decision == HoldDecision.Undecided)
{
continue;
}
}
if (item.Decision == HoldDecision.LeaveToTheGame)
{
HandleGamesJoiner(item);
continue;
}
if (WaitConfig.GhostWhileWaiting.Value && !item.Character.data.dead)
{
MakeGhost(item);
}
if (!((Object)(object)val == (Object)null))
{
if (WaitConfig.TellJoinersWhoIsLowest.Value)
{
LowestScoutNotice.Push(item.ActorNumber, val);
}
if (Arrival.IsStandable(val, out var groundPoint))
{
item.Completing = true;
((MonoBehaviour)this).StartCoroutine(PlaceJoiner(item, val, groundPoint));
}
}
}
}
private bool ResolveCharacter(PendingJoin p)
{
if ((Object)(object)p.Character != (Object)null)
{
return true;
}
Character val = default(Character);
if (PlayerHandler.TryGetCharacter(p.ActorNumber, ref val) && (Object)(object)val != (Object)null)
{
p.Character = val;
p.CharacterSeenAt = Time.time;
return true;
}
if (Time.time - p.JoinedAt > WaitConfig.SpawnTimeoutSeconds.Value)
{
Plugin.Log.LogWarning((object)(p.Nickname + "'s character never showed up - giving up on them."));
_pending.Remove(p.ActorNumber);
}
return false;
}
private void Decide(PendingJoin p)
{
bool flag = Time.time - p.CharacterSeenAt >= WaitConfig.SettleSeconds.Value;
if (p.Resumed)
{
if (flag)
{
p.Decision = HoldDecision.Hold;
Plugin.Log.LogInfo((object)("Resuming the hold on " + p.Nickname + "."));
}
}
else if (p.IsReconnect)
{
if (flag)
{
bool flag2 = CampfireGathersTheGroup();
p.Decision = (flag2 ? HoldDecision.LeaveToTheGame : HoldDecision.Hold);
Plugin.Log.LogInfo((object)(flag2 ? (p.Nickname + " is reconnecting and the base camp campfire has gathered the group, so they come back to it with everyone else.") : (p.Nickname + " is reconnecting with no campfire to come back to - holding them for the lowest scout.")));
}
}
else if (!p.Character.data.dead && !p.Character.warping)
{
p.Decision = HoldDecision.LeaveToTheGame;
Plugin.Log.LogInfo((object)("The game brought " + p.Nickname + " into the run itself - leaving that alone."));
}
else if (flag)
{
p.Decision = HoldDecision.Hold;
Plugin.Log.LogInfo((object)(p.Nickname + " was left dead on arrival - holding them until the lowest scout is safe."));
}
}
private void HandleGamesJoiner(PendingJoin p)
{
if (p.Character.data.dead || p.Character.warping)
{
if (Time.time - p.JoinedAt > WaitConfig.SpawnTimeoutSeconds.Value)
{
Plugin.Log.LogInfo((object)(p.Nickname + " never came round - leaving them to the game."));
_pending.Remove(p.ActorNumber);
}
}
else
{
JoinPack.Grant(p.Character);
_pending.Remove(p.ActorNumber);
}
}
private Character FindLowest()
{
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
Character result = null;
float num = float.MaxValue;
foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters())
{
if ((Object)(object)allPlayerCharacter == (Object)null || allPlayerCharacter.isBot || allPlayerCharacter.isScoutmaster || allPlayerCharacter.data.dead || allPlayerCharacter.data.fullyPassedOut)
{
continue;
}
PhotonView photonView = ((MonoBehaviourPun)allPlayerCharacter).photonView;
if (!((Object)(object)photonView == (Object)null) && photonView.Owner != null && !IsBeingHeld(photonView.Owner.ActorNumber))
{
float y = allPlayerCharacter.Center.y;
if (y < num)
{
num = y;
result = allPlayerCharacter;
}
}
}
return result;
}
private bool IsBeingHeld(int actorNumber)
{
if (_pending.TryGetValue(actorNumber, out var value))
{
return value.Decision != HoldDecision.LeaveToTheGame;
}
return false;
}
private void MakeGhost(PendingJoin p)
{
((MonoBehaviourPun)p.Character).photonView.RPC("RPCA_SetDead", (RpcTarget)0, Array.Empty<object>());
if (!p.GhostForced)
{
p.GhostForced = true;
Plugin.Log.LogInfo((object)(p.Nickname + " is waiting as a ghost."));
}
}
private IEnumerator PlaceJoiner(PendingJoin p, Character target, Vector3 groundPoint)
{
//IL_001c: 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)
Character c = p.Character;
Vector3 val = Arrival.PickArrivalPosition(groundPoint);
Plugin.Log.LogInfo((object)$"Dropping {p.Nickname} next to {target.characterName} at {val}.");
bool num = p.PreserveBelongings || HeldBelongings.IsCarryingAnything(c);
float[] statuses = (p.PreserveBelongings ? HeldBelongings.SnapshotStatuses(c) : null);
if (num)
{
HeldBelongings.Revive(c, val);
}
else
{
((MonoBehaviourPun)c).photonView.RPC("RPCA_ReviveAtPosition", (RpcTarget)0, new object[3]
{
val,
false,
(int)MapHandler.CurrentSegmentNumber
});
}
yield return (object)new WaitForSeconds(Mathf.Max(0f, WaitConfig.PostReviveDelay.Value));
if ((Object)(object)c != (Object)null)
{
if (p.PreserveBelongings)
{
HeldBelongings.RestoreStatuses(c, statuses);
}
else
{
JoinCurse.Apply(c);
}
JoinPack.Grant(c);
}
LowestScoutNotice.Clear(p.ActorNumber);
_pending.Remove(p.ActorNumber);
}
}
internal static class LowestScoutNotice
{
private const float NoticeLifetime = 5f;
private static int _lowestViewId;
private static float _expires;
private static readonly Dictionary<int, float> _nextPush = new Dictionary<int, float>();
private static byte EventCode => (byte)Mathf.Clamp((WaitConfig.NoticeEventCode != null) ? WaitConfig.NoticeEventCode.Value : 79, 0, 199);
internal static Character Lowest
{
get
{
if (_lowestViewId == 0 || Time.time > _expires)
{
return null;
}
Character localCharacter = Character.localCharacter;
if ((Object)(object)localCharacter == (Object)null || !localCharacter.data.dead)
{
return null;
}
PhotonView val = PhotonView.Find(_lowestViewId);
if (!((Object)(object)val != (Object)null))
{
return null;
}
return ((Component)val).GetComponent<Character>();
}
}
internal static void Push(int actorNumber, Character lowest)
{
if (!((Object)(object)lowest == (Object)null) && !((Object)(object)((MonoBehaviourPun)lowest).photonView == (Object)null) && (!_nextPush.TryGetValue(actorNumber, out var value) || !(Time.time < value)))
{
_nextPush[actorNumber] = Time.time + 1f;
Raise(actorNumber, ((MonoBehaviourPun)lowest).photonView.ViewID);
}
}
internal static void Clear(int actorNumber)
{
_nextPush.Remove(actorNumber);
Raise(actorNumber, 0);
}
internal static void Forget(int actorNumber)
{
_nextPush.Remove(actorNumber);
}
private static void Raise(int actorNumber, int viewId)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Expected O, but got Unknown
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (PhotonNetwork.InRoom)
{
RaiseEventOptions val = new RaiseEventOptions();
val.TargetActors = new int[1] { actorNumber };
RaiseEventOptions val2 = val;
PhotonNetwork.RaiseEvent(EventCode, (object)viewId, val2, SendOptions.SendReliable);
}
}
internal static void Register()
{
PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
}
internal static void Unregister()
{
if (PhotonNetwork.NetworkingClient != null)
{
PhotonNetwork.NetworkingClient.EventReceived -= OnEvent;
}
}
private static void OnEvent(EventData e)
{
if (e.Code == EventCode && e.CustomData is int lowestViewId)
{
_lowestViewId = lowestViewId;
_expires = Time.time + 5f;
}
}
}
internal class LowestScoutNoticeUI : MonoBehaviour
{
private const float HintSizeRatio = 0.55f;
private TextMeshProUGUI _label;
private TextMeshProUGUI _clonedFrom;
private void LateUpdate()
{
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
Character lowest = LowestScoutNotice.Lowest;
if ((Object)(object)lowest == (Object)null)
{
Hide();
return;
}
GUIManager instance = GUIManager.instance;
if ((Object)(object)instance == (Object)null || (Object)(object)instance.spectatingNameText == (Object)null || (Object)(object)instance.spectatingObject == (Object)null)
{
return;
}
if (!instance.spectatingObject.activeInHierarchy)
{
Hide();
}
else if (EnsureLabel(instance.spectatingNameText))
{
MatchTypeface(instance.spectatingNameText);
((TMP_Text)_label).text = (((Object)(object)MainCameraMovement.specCharacter == (Object)(object)lowest) ? "LOWEST PLAYER" : ("LOWEST PLAYER: " + lowest.characterName));
((Graphic)_label).color = instance.spectatingNameColor;
if (!((Component)_label).gameObject.activeSelf)
{
((Component)_label).gameObject.SetActive(true);
}
}
}
private void MatchTypeface(TextMeshProUGUI src)
{
//IL_0058: 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_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: 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)
if ((Object)(object)((TMP_Text)_label).font != (Object)(object)((TMP_Text)src).font)
{
((TMP_Text)_label).font = ((TMP_Text)src).font;
}
if ((Object)(object)((TMP_Text)_label).fontSharedMaterial != (Object)(object)((TMP_Text)src).fontSharedMaterial)
{
((TMP_Text)_label).fontSharedMaterial = ((TMP_Text)src).fontSharedMaterial;
}
if (((TMP_Text)_label).fontStyle != ((TMP_Text)src).fontStyle)
{
((TMP_Text)_label).fontStyle = ((TMP_Text)src).fontStyle;
}
if (((TMP_Text)_label).fontWeight != ((TMP_Text)src).fontWeight)
{
((TMP_Text)_label).fontWeight = ((TMP_Text)src).fontWeight;
}
float num = Mathf.Max(10f, ((TMP_Text)src).fontSize * 0.55f);
if (!Mathf.Approximately(((TMP_Text)_label).fontSize, num))
{
((TMP_Text)_label).fontSize = num;
}
}
private void Hide()
{
if ((Object)(object)_label != (Object)null && ((Component)_label).gameObject.activeSelf)
{
((Component)_label).gameObject.SetActive(false);
}
}
private bool EnsureLabel(TextMeshProUGUI nameText)
{
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_0141: 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_0161: Unknown result type (might be due to invalid IL or missing references)
//IL_0166: Unknown result type (might be due to invalid IL or missing references)
//IL_0179: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_label != (Object)null && (Object)(object)_clonedFrom == (Object)(object)nameText)
{
return true;
}
if ((Object)(object)_label != (Object)null)
{
Object.Destroy((Object)(object)((Component)_label).gameObject);
}
GameObject val = Object.Instantiate<GameObject>(((Component)nameText).gameObject);
((Object)val).name = "WaitForMEak_LowestScoutNotice";
Component[] components = val.GetComponents<Component>();
foreach (Component val2 in components)
{
if (!(val2 is RectTransform) && !(val2 is TextMeshProUGUI) && !(val2 is CanvasRenderer))
{
Object.Destroy((Object)(object)val2);
}
}
for (int num = val.transform.childCount - 1; num >= 0; num--)
{
Object.Destroy((Object)(object)((Component)val.transform.GetChild(num)).gameObject);
}
_label = val.GetComponent<TextMeshProUGUI>();
if ((Object)(object)_label == (Object)null)
{
Object.Destroy((Object)(object)val);
return false;
}
RectTransform rectTransform = ((TMP_Text)_label).rectTransform;
((Transform)rectTransform).SetParent(((TMP_Text)nameText).transform, false);
rectTransform.anchorMin = new Vector2(0.5f, 0f);
rectTransform.anchorMax = new Vector2(0.5f, 0f);
rectTransform.pivot = new Vector2(0.5f, 1f);
rectTransform.anchoredPosition = new Vector2(0f, -4f);
((Transform)rectTransform).localScale = Vector3.one;
Rect rect = ((TMP_Text)nameText).rectTransform.rect;
rectTransform.sizeDelta = new Vector2(Mathf.Max(320f, ((Rect)(ref rect)).width), 28f);
((TMP_Text)_label).enableAutoSizing = false;
((TMP_Text)_label).alignment = (TextAlignmentOptions)258;
((Graphic)_label).raycastTarget = false;
((TMP_Text)_label).overflowMode = (TextOverflowModes)0;
((TMP_Text)_label).richText = false;
((Component)_label).gameObject.SetActive(false);
_clonedFrom = nameText;
return true;
}
private void OnDestroy()
{
if ((Object)(object)_label != (Object)null)
{
Object.Destroy((Object)(object)((Component)_label).gameObject);
}
}
}
[BepInPlugin("com.iatespaghetti.waitformeak", "WAITFORMEAK", "0.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "com.iatespaghetti.waitformeak";
public const string PluginName = "WAITFORMEAK";
public const string PluginVersion = "0.1.0";
private const string ModConfigGuid = "com.github.PEAKModding.PEAKLib.ModConfig";
private GameObject _director;
internal static Plugin Instance { get; private set; }
internal static ManualLogSource Log { get; private set; }
private void Awake()
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Expected O, but got Unknown
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
WaitConfig.Bind(((BaseUnityPlugin)this).Config);
LowestScoutNotice.Register();
_director = new GameObject("WAITFORMEAKDirector");
((Object)_director).hideFlags = (HideFlags)61;
Object.DontDestroyOnLoad((Object)(object)_director);
_director.AddComponent<LateJoinDirector>();
_director.AddComponent<LowestScoutNoticeUI>();
Log.LogInfo((object)"WAITFORMEAK 0.1.0 loaded. Only the host needs this.");
}
private void OnDestroy()
{
LowestScoutNotice.Unregister();
if ((Object)(object)_director != (Object)null)
{
Object.Destroy((Object)(object)_director);
}
}
}
public enum PackOnJoin
{
Off,
AlwaysFannypack,
OnlyIfLeftBehind
}
internal static class WaitConfig
{
public static ConfigEntry<bool> CurseAsIfRevived;
public static ConfigEntry<PackOnJoin> PackForJoiners;
public static ConfigEntry<float> GroundedSecondsRequired;
public static ConfigEntry<bool> IncludeReconnectingPlayers;
public static ConfigEntry<bool> GhostWhileWaiting;
public static ConfigEntry<bool> TellJoinersWhoIsLowest;
public static ConfigEntry<float> MaxGroundSlope;
public static ConfigEntry<float> MaxTargetSpeed;
public static ConfigEntry<float> ArrivalOffset;
public static ConfigEntry<int> NoticeEventCode;
public static ConfigEntry<float> SettleSeconds;
public static ConfigEntry<float> SpawnTimeoutSeconds;
public static ConfigEntry<float> PostReviveDelay;
public static ConfigEntry<float> PollInterval;
private static ConfigDescription Hidden(string description)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Expected O, but got Unknown
return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { "Hidden" });
}
public static void Bind(ConfigFile cfg)
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Expected O, but got Unknown
CurseAsIfRevived = cfg.Bind<bool>("General", "Curse as if revived", false, "Late joiners start with the same amount of Curse a revive would have given them (0.05, or 0.15 on Ascent 7+). Off by default, so joining a run in progress costs nothing. Turn it on if you'd rather it cost what a revive costs, since being put down next to the group is a revive in all but name and the group would have paid that Curse to bring anyone else back.\nThe Ascent 7/8 starting Curse is applied either way, so this is the revival Curse on top of it. Neither applies to joiners the base camp campfire spawns in by itself, or to reconnecting players; the game handles the first and the mod leaves the second's own Curse alone.");
PackForJoiners = cfg.Bind<PackOnJoin>("General", "Pack for late joiners", PackOnJoin.Off, "What a late joiner gets once they arrive, campfire spawn or not.\nOff: nothing.\nAlwaysFannypack: a fresh fanny pack every time.\nOnlyIfLeftBehind: only if a backpack or fanny pack is lying abandoned on the ground somewhere in the run. That pack, and everything in it, is handed to them. Backpacks are preferred over fanny packs.");
GroundedSecondsRequired = cfg.Bind<float>("General", "Seconds the scout must be standing", 1f, new ConfigDescription("How long the lowest scout has to have been standing on the ground before a waiting joiner is dropped next to them. Stops arrivals mid-hop. Raise it if joiners keep landing on someone who was only briefly on solid ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
IncludeReconnectingPlayers = cfg.Bind<bool>("General", "Also move reconnecting players", false, "Also hold and move players who are RE-connecting to a run they were already part of. Off by default: the game restores those players to where they left off, and hauling them down to the lowest scout would undo that. When it is on they keep their items, backpack and statuses through the hold.");
GhostWhileWaiting = cfg.Bind<bool>("General", "GhostWhileWaiting", true, Hidden("Keep joiners dead (ghost/spectator) until the lowest scout is somewhere safe to drop them. Off: they are left wherever the game spawned them and are just teleported when the time comes."));
TellJoinersWhoIsLowest = cfg.Bind<bool>("General", "TellJoinersWhoIsLowest", true, Hidden("While a joiner waits, show them who the lowest scout is on the spectate panel, under whoever they're currently watching. Their camera is left alone; they can switch to that scout themselves if they want. Only does anything if that player also has this mod installed."));
MaxGroundSlope = cfg.Bind<float>("Arrival", "MaxGroundSlope", 45f, Hidden("Steepest ground (degrees) that still counts as a standable place."));
MaxTargetSpeed = cfg.Bind<float>("Arrival", "MaxTargetSpeed", 6f, Hidden("If the target is moving faster than this (m/s) they don't count as settled. Catches sliding, being launched, and rocket rides."));
ArrivalOffset = cfg.Bind<float>("Arrival", "ArrivalOffset", 1.5f, Hidden("How far to the side of the target the joiner lands, in metres."));
NoticeEventCode = cfg.Bind<int>("General", "NoticeEventCode", 79, Hidden("Photon event code used to tell a waiting joiner who the lowest scout is. Only worth changing if another mod happens to use the same one. The host and the joiner both have to be set to the same value, and if they aren't the only thing lost is that hint. Valid range is 0 to 199."));
SettleSeconds = cfg.Bind<float>("Timing", "SettleSeconds", 4f, Hidden("How long to watch a joiner's character after it appears before deciding what to do with them. The game's own late-join spawn takes a moment to finish, and whether it leaves them dead is the signal this mod reads, so don't set this shorter than that spawn takes."));
SpawnTimeoutSeconds = cfg.Bind<float>("Timing", "SpawnTimeoutSeconds", 90f, Hidden("Give up on a joiner whose character never shows up within this many seconds."));
PostReviveDelay = cfg.Bind<float>("Timing", "PostReviveDelay", 1f, Hidden("Seconds to wait after the revive before applying Curse and handing over a pack."));
PollInterval = cfg.Bind<float>("Timing", "PollInterval", 0.25f, Hidden("Seconds between checks while joiners are waiting."));
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}