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 BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.Rendering; [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("ForcedFriendship")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.1.0")] [assembly: AssemblyInformationalVersion("0.3.1+ceac12ea65e5ec71605892d0a9ca796bc6a5a2fe")] [assembly: AssemblyProduct("ForcedFriendship")] [assembly: AssemblyTitle("ForcedFriendship")] [assembly: AssemblyVersion("0.3.1.0")] [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; } } } namespace ForcedFriendship { internal static class BeamColors { internal static Color For(BeamZone zone, bool colorblind) { //IL_0076: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (colorblind) { return (Color)(zone switch { BeamZone.Danger => new Color(0.84f, 0.15f, 0.2f), BeamZone.Warn => new Color(0.95f, 0.85f, 0.15f), _ => new Color(0.15f, 0.5f, 0.85f), }); } return (Color)(zone switch { BeamZone.Danger => new Color(0.85f, 0.2f, 0.16f), BeamZone.Warn => new Color(0.85f, 0.7f, 0.15f), _ => new Color(0.25f, 0.75f, 0.3f), }); } } internal class BeamRenderer : MonoBehaviour { private const float BeamHeight = 1f; private readonly Dictionary _lines = new Dictionary(); private readonly List _current = new List(); private readonly List _stale = new List(); private Material? _material; private void Update() { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: 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_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0174: 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) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ActiveEnabled || !Plugin.BeamsEnabled.Value || !Plugin.IsInGameplay()) { HideAll(); return; } StatusHud instance = StatusHud.Instance; if ((Object)(object)instance == (Object)null) { HideAll(); return; } PlayerAvatar instance2 = PlayerAvatar.instance; bool value = Plugin.BeamsShowAll.Value; bool value2 = Plugin.BeamsAlwaysShow.Value; bool value3 = Plugin.BeamsColorblind.Value; float beamWidthWorld = Plugin.BeamWidthWorld; float beamOpacity = Plugin.BeamOpacity; _current.Clear(); int playerCount = instance.PlayerCount; for (int i = 0; i < playerCount; i++) { PlayerAvatar val = instance.AvatarAt(i); if (!((Object)(object)val == (Object)null)) { _current.Add(val); AnchorResult anchorResult = instance.AnchorAt(i); BeamZone zone = instance.ZoneAt(i); if (!DamageCalculator.ShouldDrawBeam(zone, anchorResult.HasAnchor, value2) || (!value && !((Object)(object)val == (Object)(object)instance2))) { HideLine(val); continue; } LineRenderer line = GetLine(val); Vector3 val2 = instance.PlayerPosAt(i) + Vector3.up * 1f; Vector3 val3 = new Vector3(anchorResult.X, anchorResult.Y, anchorResult.Z) + Vector3.up * 1f; ((Renderer)line).enabled = true; line.startWidth = beamWidthWorld; line.endWidth = beamWidthWorld; line.SetPosition(0, val2); line.SetPosition(1, val3); Color val4 = BeamColors.For(zone, value3); val4.a = beamOpacity; line.startColor = val4; line.endColor = val4; } } CleanupDeparted(); } private void OnDisable() { HideAll(); } private void OnDestroy() { if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } } private LineRenderer GetLine(PlayerAvatar pa) { //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) if (_lines.TryGetValue(pa, out LineRenderer value) && (Object)(object)value != (Object)null) { return value; } GameObject val = new GameObject("FF_Beam"); val.transform.SetParent(((Component)this).transform, false); LineRenderer val2 = val.AddComponent(); ((Renderer)val2).material = BeamMaterial(); val2.positionCount = 2; val2.numCapVertices = 2; val2.numCornerVertices = 2; val2.useWorldSpace = true; val2.textureMode = (LineTextureMode)0; val2.alignment = (LineAlignment)0; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val2).receiveShadows = false; ((Renderer)val2).lightProbeUsage = (LightProbeUsage)0; _lines[pa] = val2; return val2; } private void HideLine(PlayerAvatar pa) { if (_lines.TryGetValue(pa, out LineRenderer value) && (Object)(object)value != (Object)null) { ((Renderer)value).enabled = false; } } private void HideAll() { foreach (LineRenderer value in _lines.Values) { if ((Object)(object)value != (Object)null) { ((Renderer)value).enabled = false; } } } private void CleanupDeparted() { _stale.Clear(); foreach (PlayerAvatar key in _lines.Keys) { if ((Object)(object)key == (Object)null || !_current.Contains(key)) { _stale.Add(key); } } foreach (PlayerAvatar item in _stale) { if (_lines.TryGetValue(item, out LineRenderer value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } _lines.Remove(item); } } private Material BeamMaterial() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if ((Object)(object)_material == (Object)null) { Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Transparent"); _material = new Material(val); } return _material; } } internal static class CartLocator { internal static List FindMainCarts(List buffer) { buffer.Clear(); PhysGrabCart[] array = Object.FindObjectsOfType(); PhysGrabCart val = null; PhysGrabCart[] array2 = array; foreach (PhysGrabCart val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { if (!val2.isSmallCart) { buffer.Add(val2); } else if (val == null) { val = val2; } } } if (buffer.Count == 0 && (Object)(object)val != (Object)null) { buffer.Add(val); } return buffer; } } public readonly struct PlayerState { public readonly float X; public readonly float Y; public readonly float Z; public readonly bool Alive; public readonly bool InTruck; public PlayerState(float x, float y, float z, bool alive, bool inTruck = false) { X = x; Y = y; Z = z; Alive = alive; InTruck = inTruck; } } public readonly struct DamageSettings { public readonly bool Enabled; public readonly float SafeDistance; public readonly float BandWidth; public readonly int DamagePerBand; public DamageSettings(bool enabled, float safeDistance, float bandWidth, int damagePerBand) { Enabled = enabled; SafeDistance = safeDistance; BandWidth = bandWidth; DamagePerBand = damagePerBand; } } public enum AnchorMode { Buddy, Cart } public enum BeamZone { Safe, Warn, Danger } public readonly struct Vec3 { public readonly float X; public readonly float Y; public readonly float Z; public Vec3(float x, float y, float z) { X = x; Y = y; Z = z; } } public readonly struct AnchorResult { public readonly float X; public readonly float Y; public readonly float Z; public readonly float Distance; public readonly bool HasAnchor; public readonly bool Safe; public static AnchorResult None => new AnchorResult(0f, 0f, 0f, 0f, hasAnchor: false); public AnchorResult(float x, float y, float z, float distance, bool hasAnchor, bool safe = false) { X = x; Y = y; Z = z; Distance = distance; HasAnchor = hasAnchor; Safe = safe; } } public static class DamageCalculator { public static float Distance(in PlayerState a, in PlayerState b) { return Distance(in a, in b, includeHeight: true); } public static float Distance(in PlayerState a, in PlayerState b, bool includeHeight) { float num = a.X - b.X; float num2 = (includeHeight ? (a.Y - b.Y) : 0f); float num3 = a.Z - b.Z; return (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3); } private static float Distance(in PlayerState a, in Vec3 b, bool includeHeight) { float num = a.X - b.X; float num2 = (includeHeight ? (a.Y - b.Y) : 0f); float num3 = a.Z - b.Z; return (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3); } public static int Band(float distance, float safeDistance, float bandWidth) { if (distance <= safeDistance) { return 0; } if (bandWidth <= 0f) { return 1; } return (int)Math.Floor((distance - safeDistance) / bandWidth) + 1; } public static AnchorResult[] ResolveAnchors(IReadOnlyList players, AnchorMode mode, IReadOnlyList carts, bool includeHeight = true) { AnchorResult[] array = new AnchorResult[players.Count]; bool flag = mode == AnchorMode.Cart && carts != null && carts.Count > 0; for (int i = 0; i < players.Count; i++) { PlayerState a = players[i]; if (!a.Alive) { array[i] = AnchorResult.None; continue; } if (flag) { float num = float.PositiveInfinity; int index = -1; for (int j = 0; j < carts.Count; j++) { float num2 = Distance(in a, carts[j], includeHeight); if (num2 < num) { num = num2; index = j; } } Vec3 vec = carts[index]; array[i] = new AnchorResult(vec.X, vec.Y, vec.Z, num, hasAnchor: true, a.InTruck); continue; } float num3 = float.PositiveInfinity; int num4 = -1; bool flag2 = false; for (int k = 0; k < players.Count; k++) { if (k == i) { continue; } PlayerState b = players[k]; if (b.Alive) { if (!b.InTruck) { flag2 = true; } float num5 = Distance(in a, in b, includeHeight); if (num5 < num3) { num3 = num5; num4 = k; } } } if (num4 < 0) { array[i] = AnchorResult.None; continue; } bool safe = a.InTruck || !flag2; PlayerState playerState = players[num4]; array[i] = new AnchorResult(playerState.X, playerState.Y, playerState.Z, num3, hasAnchor: true, safe); } return array; } public static BeamZone Classify(float distance, float safeDistance, float warnPercent) { if (distance > safeDistance) { return BeamZone.Danger; } if (warnPercent <= 0f) { return BeamZone.Safe; } float num = ((warnPercent > 1f) ? 1f : warnPercent); float num2 = safeDistance * (1f - num); if (!(distance >= num2)) { return BeamZone.Safe; } return BeamZone.Warn; } public static int[] EvaluateDamage(IReadOnlyList anchors, in DamageSettings s) { int[] array = new int[anchors.Count]; if (!s.Enabled) { return array; } for (int i = 0; i < anchors.Count; i++) { AnchorResult anchorResult = anchors[i]; if (anchorResult.HasAnchor && !anchorResult.Safe) { int num = Band(anchorResult.Distance, s.SafeDistance, s.BandWidth); array[i] = num * s.DamagePerBand; } } return array; } public static BeamZone ZoneForAnchor(in AnchorResult a, float safeDistance, float warnPercent) { if (!a.Safe) { return Classify(a.Distance, safeDistance, warnPercent); } return BeamZone.Safe; } public static bool ShouldDrawBeam(BeamZone zone, bool hasAnchor, bool alwaysShow) { if (hasAnchor) { if (!alwaysShow) { return zone != BeamZone.Safe; } return true; } return false; } public static int[] Evaluate(IReadOnlyList players, in DamageSettings s) { return EvaluateDamage(ResolveAnchors(players, AnchorMode.Buddy, Array.Empty()), in s); } } internal class ForcedFriendshipDriver : MonoBehaviour { private float _accum; private readonly List _states = new List(); private readonly List _avatars = new List(); private readonly List _carts = new List(); private readonly List _cartPositions = new List(); private void Update() { //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) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ActiveEnabled || (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) || !Plugin.IsInGameplay()) { return; } _accum += Time.deltaTime; if (_accum < (float)Plugin.ActiveTickInterval) { return; } _accum = 0f; List list = GameDirector.instance?.PlayerList; if (list == null) { return; } _states.Clear(); _avatars.Clear(); foreach (PlayerAvatar item in list) { if (!((Object)(object)item == (Object)null)) { Vector3 position = ((Component)item).transform.position; bool alive = PlayerLiveness.IsAlive(item); bool inTruck = PlayerLiveness.IsInTruck(item); _states.Add(new PlayerState(position.x, position.y, position.z, alive, inTruck)); _avatars.Add(item); } } DamageSettings s = new DamageSettings(enabled: true, Plugin.ActiveSafeDistance, Plugin.ActiveBandWidth, Plugin.ActiveDamagePerBand); _cartPositions.Clear(); if (Plugin.ActiveMode == AnchorMode.Cart) { CartLocator.FindMainCarts(_carts); foreach (PhysGrabCart cart in _carts) { if (!((Object)(object)cart == (Object)null)) { Vector3 position2 = ((Component)cart).transform.position; _cartPositions.Add(new Vec3(position2.x, position2.y, position2.z)); } } } int[] array = DamageCalculator.EvaluateDamage(DamageCalculator.ResolveAnchors(_states, Plugin.ActiveMode, _cartPositions, Plugin.ActiveIncludeHeight), in s); StatusHud instance = StatusHud.Instance; for (int i = 0; i < array.Length; i++) { if (array[i] > 0) { PlayerAvatar val = _avatars[i]; if ((!((Object)(object)instance != (Object)null) || !instance.IsInGrace(val)) && !((Object)(object)val.playerHealth == (Object)null)) { val.playerHealth.HurtOther(array[i], Vector3.zero, false, -1, false); } } } } } internal static class PlayerLiveness { private static readonly FieldRef DeadSetRef = AccessTools.FieldRefAccess("deadSet"); private static readonly FieldRef IsDisabledRef = AccessTools.FieldRefAccess("isDisabled"); private static readonly FieldRef InTruckRef = AccessTools.FieldRefAccess("inTruck"); internal static bool IsAlive(PlayerAvatar pa) { if (!DeadSetRef.Invoke(pa)) { return !IsDisabledRef.Invoke(pa); } return false; } internal static bool IsInTruck(PlayerAvatar pa) { RoomVolumeCheck roomVolumeCheck = pa.RoomVolumeCheck; if ((Object)(object)roomVolumeCheck != (Object)null) { return InTruckRef.Invoke(roomVolumeCheck); } return false; } } [BepInPlugin("darkharasho.ForcedFriendship", "ForcedFriendship", "0.3.1")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry SafeDistance; internal static ConfigEntry BandWidth; internal static ConfigEntry DamagePerBand; internal static ConfigEntry TickInterval; internal static ConfigEntry IncludeHeight; internal static ConfigEntry GracePeriod; internal static ConfigEntry Mode; internal static ConfigEntry BeamsEnabled; internal static ConfigEntry BeamsShowAll; internal static ConfigEntry BeamsAlwaysShow; internal static ConfigEntry BeamsWarnPercent; internal static ConfigEntry BeamsWidth; internal static ConfigEntry BeamsOpacity; internal static ConfigEntry BeamsColorblind; internal static ConfigEntry StatusIndicator; internal static bool ActiveEnabled; internal static AnchorMode ActiveMode; internal static int ActiveSafeDistance; internal static int ActiveBandWidth; internal static int ActiveDamagePerBand; internal static int ActiveTickInterval; internal static bool ActiveIncludeHeight; internal static int ActiveGracePeriod; internal static float BeamWidthWorld => (float)BeamsWidth.Value * 0.01f; internal static float WarnFraction => (float)BeamsWarnPercent.Value / 100f; internal static float BeamOpacity => (float)BeamsOpacity.Value / 100f; private void Awake() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Expected O, but got Unknown //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master on/off switch for Forced Friendship."); SafeDistance = ((BaseUnityPlugin)this).Config.Bind("General", "SafeDistance", 20, new ConfigDescription("Units within which your anchor (nearest player or cart) keeps you safe.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); BandWidth = ((BaseUnityPlugin)this).Config.Bind("General", "BandWidth", 8, new ConfigDescription("Units per additional damage band beyond the safe radius.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); DamagePerBand = ((BaseUnityPlugin)this).Config.Bind("General", "DamagePerBand", 5, new ConfigDescription("HP per tick, multiplied by the band number.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); TickInterval = ((BaseUnityPlugin)this).Config.Bind("General", "TickInterval", 2, new ConfigDescription("Seconds between damage evaluations.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 30), Array.Empty())); IncludeHeight = ((BaseUnityPlugin)this).Config.Bind("General", "IncludeHeight", false, "If true, vertical distance counts toward the safe radius. Default false so being on a different floor of the same tall room doesn't trigger damage."); GracePeriod = ((BaseUnityPlugin)this).Config.Bind("General", "GracePeriod", 30, new ConfigDescription("Seconds of safety after leaving the truck before damage can start. 0 disables.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 60), Array.Empty())); Mode = ((BaseUnityPlugin)this).Config.Bind("General", "AnchorMode", AnchorMode.Buddy, "Buddy = stay near the nearest living player (default). Cart = stay near the nearest hauling cart instead."); BeamsEnabled = ((BaseUnityPlugin)this).Config.Bind("Beams", "Enabled", true, "Draw a tether beam from each player to their anchor."); BeamsShowAll = ((BaseUnityPlugin)this).Config.Bind("Beams", "ShowAllPlayers", true, "Show beams for every living player. If false, only your own beam is drawn."); BeamsAlwaysShow = ((BaseUnityPlugin)this).Config.Bind("Beams", "AlwaysShow", true, "Always draw the tether (color conveys safety). If false, the beam hides while you're safe and only appears in the warn/danger zone."); BeamsWarnPercent = ((BaseUnityPlugin)this).Config.Bind("Beams", "WarnPercent", 25, new ConfigDescription("Percent of SafeDistance, at the outer edge, where the beam turns yellow before it turns red. 0 disables the yellow zone.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); BeamsWidth = ((BaseUnityPlugin)this).Config.Bind("Beams", "Width", 2, new ConfigDescription("Tether thickness (1 = thinnest). The default approximates the game's grab beam.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), Array.Empty())); BeamsOpacity = ((BaseUnityPlugin)this).Config.Bind("Beams", "Opacity", 40, new ConfigDescription("Beam opacity percent (1 = faint, 100 = solid). Lower is more translucent.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); BeamsColorblind = ((BaseUnityPlugin)this).Config.Bind("Beams", "Colorblind", false, "Use a colorblind-friendly palette (blue safe / yellow warn / red danger) instead of green/yellow/red. Local to you — not synced from the host."); StatusIndicator = ((BaseUnityPlugin)this).Config.Bind("Beams", "StatusIndicator", false, "Show a subtle screen-edge border tinted by your current safety color (green/yellow/red) so you always know your status. Local to you."); ResetToLocalConfig(); new Harmony("darkharasho.ForcedFriendship").PatchAll(); Log.LogInfo((object)"ForcedFriendship v0.3.1 loaded."); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); } internal static void ResetToLocalConfig() { ActiveEnabled = Enabled.Value; ActiveMode = Mode.Value; ActiveSafeDistance = SafeDistance.Value; ActiveBandWidth = BandWidth.Value; ActiveDamagePerBand = DamagePerBand.Value; ActiveTickInterval = TickInterval.Value; ActiveIncludeHeight = IncludeHeight.Value; ActiveGracePeriod = GracePeriod.Value; } internal static bool IsInGameplay() { if (!PhotonNetwork.InRoom) { return false; } RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null) { return false; } Level levelCurrent = instance.levelCurrent; if ((Object)(object)levelCurrent == (Object)null) { return false; } if ((Object)(object)levelCurrent == (Object)(object)instance.levelLobby || (Object)(object)levelCurrent == (Object)(object)instance.levelLobbyMenu) { return false; } if (SemiFunc.IsLevelShop(levelCurrent)) { return false; } return true; } } internal class SettingsSyncer : MonoBehaviour { private const string K_ENABLED = "FF_EN"; private const string K_MODE = "FF_MODE"; private const string K_SAFE = "FF_SAFE"; private const string K_BAND = "FF_BAND"; private const string K_DMG = "FF_DMG"; private const string K_TICK = "FF_TICK"; private const string K_HEIGHT = "FF_HGT"; private const string K_GRACE = "FF_GRC"; internal static SettingsSyncer? Instance; private bool _wasInRoom; private float _pullPollDelay; private bool? _lastEnabled; private int _lastMode = int.MinValue; private int _lastSafe = int.MinValue; private int _lastBand = int.MinValue; private int _lastTick = int.MinValue; private int _lastDmg = int.MinValue; private bool? _lastHeight; private int _lastGrace = int.MinValue; private void Awake() { Instance = this; } private void Start() { Plugin.Log.LogInfo((object)"[Sync] SettingsSyncer ready (polling mode)"); } private void Update() { bool inRoom = PhotonNetwork.InRoom; if (inRoom && PhotonNetwork.IsMasterClient) { PushHostSettings(); } else if (inRoom) { if (!_wasInRoom) { PullHostSettings(); _pullPollDelay = 1f; } else { _pullPollDelay -= Time.unscaledDeltaTime; if (_pullPollDelay <= 0f) { _pullPollDelay = 1f; PullHostSettings(); } } } else { if (_wasInRoom) { Plugin.Log.LogInfo((object)"[Sync] Left room"); } Plugin.ResetToLocalConfig(); } _wasInRoom = inRoom; } private void PullHostSettings() { Room currentRoom = PhotonNetwork.CurrentRoom; Hashtable val = ((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null); if (val == null) { return; } bool flag = false; if (((Dictionary)(object)val).ContainsKey((object)"FF_EN")) { bool flag2 = (bool)val[(object)"FF_EN"]; if (Plugin.ActiveEnabled != flag2) { Plugin.ActiveEnabled = flag2; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_MODE")) { AnchorMode anchorMode = (AnchorMode)(int)val[(object)"FF_MODE"]; if (Plugin.ActiveMode != anchorMode) { Plugin.ActiveMode = anchorMode; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_SAFE")) { int num = (int)val[(object)"FF_SAFE"]; if (Plugin.ActiveSafeDistance != num) { Plugin.ActiveSafeDistance = num; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_BAND")) { int num2 = (int)val[(object)"FF_BAND"]; if (Plugin.ActiveBandWidth != num2) { Plugin.ActiveBandWidth = num2; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_DMG")) { int num3 = (int)val[(object)"FF_DMG"]; if (Plugin.ActiveDamagePerBand != num3) { Plugin.ActiveDamagePerBand = num3; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_TICK")) { int num4 = (int)val[(object)"FF_TICK"]; if (Plugin.ActiveTickInterval != num4) { Plugin.ActiveTickInterval = num4; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_HGT")) { bool flag3 = (bool)val[(object)"FF_HGT"]; if (Plugin.ActiveIncludeHeight != flag3) { Plugin.ActiveIncludeHeight = flag3; flag = true; } } if (((Dictionary)(object)val).ContainsKey((object)"FF_GRC")) { int num5 = (int)val[(object)"FF_GRC"]; if (Plugin.ActiveGracePeriod != num5) { Plugin.ActiveGracePeriod = num5; flag = true; } } if (flag) { Plugin.Log.LogInfo((object)$"[Sync] Pulled host rule — enabled={Plugin.ActiveEnabled} mode={Plugin.ActiveMode} safe={Plugin.ActiveSafeDistance} band={Plugin.ActiveBandWidth} dmg={Plugin.ActiveDamagePerBand} tick={Plugin.ActiveTickInterval}"); } } private void PushHostSettings() { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown if (PhotonNetwork.CurrentRoom != null) { bool value = Plugin.Enabled.Value; int value2 = (int)Plugin.Mode.Value; int value3 = Plugin.SafeDistance.Value; int value4 = Plugin.BandWidth.Value; int value5 = Plugin.TickInterval.Value; int value6 = Plugin.DamagePerBand.Value; bool value7 = Plugin.IncludeHeight.Value; int value8 = Plugin.GracePeriod.Value; if (value == _lastEnabled && value2 == _lastMode && value3 == _lastSafe && value4 == _lastBand && value6 == _lastDmg && value5 == _lastTick && value7 == _lastHeight && value8 == _lastGrace) { Plugin.ResetToLocalConfig(); return; } _lastEnabled = value; _lastMode = value2; _lastSafe = value3; _lastBand = value4; _lastDmg = value6; _lastTick = value5; _lastHeight = value7; _lastGrace = value8; Hashtable val = new Hashtable { [(object)"FF_EN"] = value, [(object)"FF_MODE"] = value2, [(object)"FF_SAFE"] = value3, [(object)"FF_BAND"] = value4, [(object)"FF_DMG"] = value6, [(object)"FF_TICK"] = value5, [(object)"FF_HGT"] = value7, [(object)"FF_GRC"] = value8 }; PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); Plugin.ResetToLocalConfig(); Plugin.Log.LogInfo((object)$"[Sync] Host pushed rule — enabled={value} mode={(AnchorMode)value2} safe={value3} band={value4} dmg={value6} tick={value5} height={value7} grace={value8}"); } } } internal class StatusHud : MonoBehaviour { internal static StatusHud? Instance; private readonly Dictionary _grace = new Dictionary(); private readonly List _stale = new List(); private readonly List _states = new List(); private readonly List _avatars = new List(); private readonly List _carts = new List(); private readonly List _cartPositions = new List(); private float _cartRescan; private AnchorResult[] _anchors = Array.Empty(); private BeamZone[] _zones = Array.Empty(); private static Texture2D? _tex; private GUIStyle? _timerStyle; internal int PlayerCount { get; private set; } internal BeamZone LocalZone { get; private set; } internal bool LocalActive { get; private set; } internal PlayerAvatar AvatarAt(int i) { return _avatars[i]; } internal AnchorResult AnchorAt(int i) { return _anchors[i]; } internal BeamZone ZoneAt(int i) { return _zones[i]; } internal Vector3 PlayerPosAt(int i) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) PlayerState playerState = _states[i]; return new Vector3(playerState.X, playerState.Y, playerState.Z); } private void Awake() { Instance = this; } internal bool IsInGrace(PlayerAvatar pa) { if (Plugin.ActiveGracePeriod > 0 && _grace.TryGetValue(pa, out var value)) { return value > 0f; } return false; } internal float Remaining(PlayerAvatar pa) { if (!_grace.TryGetValue(pa, out var value)) { return 0f; } return value; } private void Update() { //IL_00e4: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ActiveEnabled || !Plugin.IsInGameplay()) { _grace.Clear(); PlayerCount = 0; LocalActive = false; return; } List list = GameDirector.instance?.PlayerList; if (list == null) { PlayerCount = 0; LocalActive = false; return; } float num = Plugin.ActiveGracePeriod; float deltaTime = Time.deltaTime; _states.Clear(); _avatars.Clear(); foreach (PlayerAvatar item in list) { if (!((Object)(object)item == (Object)null)) { bool flag = PlayerLiveness.IsInTruck(item); if (!_grace.TryGetValue(item, out var value)) { value = num; } value = (flag ? num : Mathf.Max(0f, value - deltaTime)); if (value > num) { value = num; } _grace[item] = value; Vector3 position = ((Component)item).transform.position; _states.Add(new PlayerState(position.x, position.y, position.z, PlayerLiveness.IsAlive(item), flag)); _avatars.Add(item); } } _stale.Clear(); foreach (PlayerAvatar key in _grace.Keys) { if ((Object)(object)key == (Object)null || !_avatars.Contains(key)) { _stale.Add(key); } } foreach (PlayerAvatar item2 in _stale) { _grace.Remove(item2); } AnchorMode activeMode = Plugin.ActiveMode; _cartRescan -= deltaTime; if (activeMode != AnchorMode.Cart) { _carts.Clear(); } else if (_carts.Count == 0 || _cartRescan <= 0f) { CartLocator.FindMainCarts(_carts); _cartRescan = 1f; } _cartPositions.Clear(); if (activeMode == AnchorMode.Cart) { foreach (PhysGrabCart cart in _carts) { if (!((Object)(object)cart == (Object)null)) { Vector3 position2 = ((Component)cart).transform.position; _cartPositions.Add(new Vec3(position2.x, position2.y, position2.z)); } } } _anchors = DamageCalculator.ResolveAnchors(_states, activeMode, _cartPositions, Plugin.ActiveIncludeHeight); int count = _states.Count; if (_zones.Length < count) { _zones = new BeamZone[count]; } float safeDistance = Plugin.ActiveSafeDistance; float warnFraction = Plugin.WarnFraction; for (int i = 0; i < count; i++) { BeamZone beamZone = DamageCalculator.ZoneForAnchor(in _anchors[i], safeDistance, warnFraction); if (IsInGrace(_avatars[i])) { beamZone = BeamZone.Safe; } _zones[i] = beamZone; } PlayerCount = count; LocalActive = false; PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance != (Object)null) { int num2 = _avatars.IndexOf(instance); if (num2 >= 0) { LocalZone = _zones[num2]; LocalActive = true; } } } private void OnGUI() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (!LocalActive) { return; } PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return; } if (Plugin.ActiveGracePeriod > 0 && !PlayerLiveness.IsInTruck(instance)) { float num = Remaining(instance); if (num > 0f) { DrawTimer(Mathf.CeilToInt(num)); } } if (Plugin.StatusIndicator.Value) { Color c = BeamColors.For(LocalZone, Plugin.BeamsColorblind.Value); c.a = 0.5f; DrawBorder(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), c, 4f); } } private void DrawTimer(int seconds) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (_timerStyle == null) { _timerStyle = new GUIStyle(GUI.skin.label) { fontSize = 22, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; } Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Screen.width * 0.5f - 110f, (float)Screen.height * 0.16f, 220f, 32f); Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, 0.6f); GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), $"Safe for {seconds}s", _timerStyle); GUI.color = Color.white; GUI.Label(val, $"Safe for {seconds}s", _timerStyle); GUI.color = color; } private static void DrawBorder(Rect r, Color c, float t) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = WhiteTex(); Color color = GUI.color; GUI.color = c; GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, t), (Texture)(object)val); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - t, ((Rect)(ref r)).width, t), (Texture)(object)val); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)val); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - t, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)val); GUI.color = color; } private static Texture2D WhiteTex() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_tex == (Object)null) { _tex = new Texture2D(1, 1); _tex.SetPixel(0, 0, Color.white); _tex.Apply(); } return _tex; } } public static class PluginInfo { public const string PLUGIN_GUID = "darkharasho.ForcedFriendship"; public const string PLUGIN_NAME = "ForcedFriendship"; public const string PLUGIN_VERSION = "0.3.1"; } }