Decompiled source of CaptureTheFlag v1.0.4
BepInEx/plugins/StraftatCTF/StraftatCTF.dll
Decompiled 7 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ComputerysModdingUtilities; using FishNet; using HarmonyLib; using MyceliumNetworking; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Capture the Flag for STRAFTAT")] [assembly: AssemblyProduct("StraftatCTF")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: StraftatMod(false)] [assembly: AssemblyVersion("1.0.4.0")] namespace StraftatCTF; internal static class CtfArt { private static readonly List<Object> Owned = new List<Object>(); private static Shader _litShader; private static Shader _unlitShader; internal static Color AllyColor { get { //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_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { return Saturate(OutlineHandler.TeamColor); } catch { return new Color(0.58f, 0.67f, 1f); } } } internal static Color EnemyColor { get { //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_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { return Saturate(OutlineHandler.EnemyColor); } catch { return new Color(1f, 0.24f, 0.2f); } } } private static Shader LitShader { get { if ((Object)(object)_litShader != (Object)null) { return _litShader; } _litShader = Shader.Find("Standard") ?? Shader.Find("Legacy Shaders/Diffuse") ?? Shader.Find("Sprites/Default") ?? ScavengeShader(); return _litShader; } } private static Shader UnlitShader { get { if ((Object)(object)_unlitShader != (Object)null) { return _unlitShader; } _unlitShader = Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default") ?? Shader.Find("Unlit/Transparent") ?? LitShader; return _unlitShader; } } internal static void DisposeAll() { foreach (Object item in Owned) { if (item != (Object)null) { Object.Destroy(item); } } Owned.Clear(); } internal static Color ColorForTeam(CtfState state, int teamId) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (state != null) { int team = CtfGame.GetTeam(CtfGame.LocalPlayerId); if (state.IsPlayingTeam(team) && state.IsPlayingTeam(teamId)) { return (teamId == team) ? AllyColor : EnemyColor; } } return FallbackColor(teamId); } private static Color FallbackColor(int teamId) { //IL_0018: 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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) Color[] array = (Color[])(object)new Color[4] { new Color(0.98f, 0.28f, 0.22f), new Color(0.36f, 0.62f, 1f), new Color(0.44f, 0.94f, 0.42f), new Color(1f, 0.79f, 0.22f) }; int num = teamId % array.Length; if (num < 0) { num += array.Length; } return array[num]; } private static Color Saturate(Color color) { //IL_0001: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) float num = default(float); float num2 = default(float); float num3 = default(float); Color.RGBToHSV(color, ref num, ref num2, ref num3); num2 = Mathf.Clamp(num2, 0.45f, 1f); num3 = Mathf.Clamp(num3, 0.65f, 1f); Color result = Color.HSVToRGB(num, num2, num3); result.a = 1f; return result; } private static Shader ScavengeShader() { try { Renderer[] array = Object.FindObjectsOfType<Renderer>(); foreach (Renderer val in array) { Material val2 = (((Object)(object)val != (Object)null) ? val.sharedMaterial : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.shader != (Object)null) { return val2.shader; } } } catch { } return null; } internal static Material Solid(Color color, float emission = 0f) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0068: 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) Shader litShader = LitShader; if ((Object)(object)litShader == (Object)null) { return null; } Material val = new Material(litShader) { hideFlags = (HideFlags)61, color = color }; if (emission > 0f && val.HasProperty("_EmissionColor")) { val.EnableKeyword("_EMISSION"); val.globalIlluminationFlags = (MaterialGlobalIlluminationFlags)4; val.SetColor("_EmissionColor", color * emission); } if (val.HasProperty("_Glossiness")) { val.SetFloat("_Glossiness", 0.15f); } if (val.HasProperty("_Metallic")) { val.SetFloat("_Metallic", 0.1f); } Owned.Add((Object)(object)val); return val; } internal static Material Glow(Color color, bool depthTested = true) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Shader unlitShader = UnlitShader; if ((Object)(object)unlitShader == (Object)null) { return null; } Material val = new Material(unlitShader) { hideFlags = (HideFlags)61, color = color }; val.renderQueue = (depthTested ? 3050 : 4000); if (val.HasProperty("_ZWrite")) { val.SetFloat("_ZWrite", 0f); } if (!depthTested && val.HasProperty("_ZTest")) { val.SetFloat("_ZTest", 8f); } Owned.Add((Object)(object)val); return val; } internal static void SetColor(Material material, Color color, float emission = 0f) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)material == (Object)null)) { material.color = color; if (emission > 0f && material.HasProperty("_EmissionColor")) { material.SetColor("_EmissionColor", color * emission); } } } private static Mesh NewMesh(string name) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown Mesh val = new Mesh { name = name, hideFlags = (HideFlags)61 }; Owned.Add((Object)(object)val); return val; } internal static Mesh Cloth(int columns, int rows, float width, float height, out Vector3[] restPositions) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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) columns = Mathf.Max(2, columns); rows = Mathf.Max(2, rows); int num = columns * rows; restPositions = (Vector3[])(object)new Vector3[num]; Vector2[] array = (Vector2[])(object)new Vector2[num * 2]; Vector3[] array2 = (Vector3[])(object)new Vector3[num * 2]; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { int num2 = i * columns + j; float num3 = (float)j / (float)(columns - 1); float num4 = (float)i / (float)(rows - 1); restPositions[num2] = new Vector3(num3 * width, (num4 - 0.5f) * height, 0f); array2[num2] = restPositions[num2]; array2[num2 + num] = restPositions[num2]; array[num2] = new Vector2(num3, num4); array[num2 + num] = new Vector2(num3, num4); } } List<int> list = new List<int>((columns - 1) * (rows - 1) * 12); for (int k = 0; k < rows - 1; k++) { for (int l = 0; l < columns - 1; l++) { int num5 = k * columns + l; int num6 = num5 + 1; int num7 = num5 + columns; int num8 = num7 + 1; list.Add(num5); list.Add(num7); list.Add(num6); list.Add(num6); list.Add(num7); list.Add(num8); list.Add(num5 + num); list.Add(num6 + num); list.Add(num7 + num); list.Add(num6 + num); list.Add(num8 + num); list.Add(num7 + num); } } Mesh val = NewMesh("CTF Cloth"); val.MarkDynamic(); val.vertices = array2; val.uv = array; val.triangles = list.ToArray(); val.RecalculateNormals(); val.RecalculateBounds(); return val; } internal static void WaveCloth(Mesh mesh, Vector3[] rest, Vector3[] scratch, float time, float strength) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00ce: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)mesh == (Object)null) && rest != null && scratch != null) { int num = rest.Length; for (int i = 0; i < num; i++) { Vector3 val = rest[i]; float x = val.x; float num2 = Mathf.Sin(x * 7f + time * 6.5f) * 0.09f + Mathf.Sin(val.y * 5f - time * 4.1f) * 0.035f; val.z += num2 * x * strength * 4f; val.y += Mathf.Sin(x * 4f + time * 5f) * 0.02f * x * strength * 4f; scratch[i] = val; scratch[i + num] = val; } mesh.vertices = scratch; mesh.RecalculateNormals(); } } internal static Mesh Tube(float radius, float height, int segments = 10) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) segments = Mathf.Max(3, segments); Vector3[] array = (Vector3[])(object)new Vector3[(segments + 1) * 2]; Vector2[] array2 = (Vector2[])(object)new Vector2[array.Length]; int[] array3 = new int[segments * 6]; for (int i = 0; i <= segments; i++) { float num = (float)i / (float)segments; float num2 = num * (float)Math.PI * 2f; float num3 = Mathf.Cos(num2) * radius; float num4 = Mathf.Sin(num2) * radius; array[i] = new Vector3(num3, 0f, num4); array[i + segments + 1] = new Vector3(num3, height, num4); array2[i] = new Vector2(num, 0f); array2[i + segments + 1] = new Vector2(num, 1f); } for (int j = 0; j < segments; j++) { int num5 = j; int num6 = j + 1; int num7 = j + segments + 1; int num8 = num7 + 1; int num9 = j * 6; array3[num9] = num5; array3[num9 + 1] = num7; array3[num9 + 2] = num6; array3[num9 + 3] = num6; array3[num9 + 4] = num7; array3[num9 + 5] = num8; } Mesh val = NewMesh("CTF Tube"); val.vertices = array; val.uv = array2; val.triangles = array3; val.RecalculateNormals(); val.RecalculateBounds(); return val; } internal static Mesh Ring(float innerRadius, float outerRadius, int segments = 48) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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) segments = Mathf.Max(6, segments); Vector3[] array = (Vector3[])(object)new Vector3[(segments + 1) * 2]; Vector2[] array2 = (Vector2[])(object)new Vector2[array.Length]; List<int> list = new List<int>(segments * 12); for (int i = 0; i <= segments; i++) { float num = (float)i / (float)segments; float num2 = num * (float)Math.PI * 2f; float num3 = Mathf.Cos(num2); float num4 = Mathf.Sin(num2); array[i * 2] = new Vector3(num3 * innerRadius, 0f, num4 * innerRadius); array[i * 2 + 1] = new Vector3(num3 * outerRadius, 0f, num4 * outerRadius); array2[i * 2] = new Vector2(num, 0f); array2[i * 2 + 1] = new Vector2(num, 1f); } for (int j = 0; j < segments; j++) { int num5 = j * 2; int item = num5 + 1; int item2 = num5 + 2; int item3 = num5 + 3; list.Add(num5); list.Add(item2); list.Add(item); list.Add(item); list.Add(item2); list.Add(item3); list.Add(num5); list.Add(item); list.Add(item2); list.Add(item); list.Add(item3); list.Add(item2); } Mesh val = NewMesh("CTF Ring"); val.vertices = array; val.uv = array2; val.triangles = list.ToArray(); val.RecalculateNormals(); val.RecalculateBounds(); return val; } internal static Mesh Beam(float radius, float height, int segments = 14) { //IL_0077: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) segments = Mathf.Max(3, segments); int num = segments + 1; Vector3[] array = (Vector3[])(object)new Vector3[num * 2]; Color[] array2 = (Color[])(object)new Color[array.Length]; Vector2[] array3 = (Vector2[])(object)new Vector2[array.Length]; List<int> list = new List<int>(segments * 12); for (int i = 0; i <= segments; i++) { float num2 = (float)i / (float)segments; float num3 = num2 * (float)Math.PI * 2f; float num4 = Mathf.Cos(num3) * radius; float num5 = Mathf.Sin(num3) * radius; array[i] = new Vector3(num4, 0f, num5); array[i + num] = new Vector3(num4, height, num5); array2[i] = new Color(1f, 1f, 1f, 1f); array2[i + num] = new Color(1f, 1f, 1f, 0f); array3[i] = new Vector2(num2, 0f); array3[i + num] = new Vector2(num2, 1f); } for (int j = 0; j < segments; j++) { int item = j; int item2 = j + 1; int num6 = j + num; int item3 = num6 + 1; list.Add(item); list.Add(num6); list.Add(item2); list.Add(item2); list.Add(num6); list.Add(item3); list.Add(item); list.Add(item2); list.Add(num6); list.Add(item2); list.Add(item3); list.Add(num6); } Mesh val = NewMesh("CTF Beam"); val.vertices = array; val.colors = array2; val.uv = array3; val.triangles = list.ToArray(); val.RecalculateNormals(); val.RecalculateBounds(); return val; } internal static Mesh Disc(float radius, int segments = 32) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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) //IL_00cf: 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) segments = Mathf.Max(6, segments); Vector3[] array = (Vector3[])(object)new Vector3[segments + 2]; Color[] array2 = (Color[])(object)new Color[array.Length]; Vector2[] array3 = (Vector2[])(object)new Vector2[array.Length]; List<int> list = new List<int>(segments * 6); array[0] = Vector3.zero; array2[0] = Color.white; array3[0] = new Vector2(0.5f, 0.5f); for (int i = 0; i <= segments; i++) { float num = (float)i / (float)segments; float num2 = num * (float)Math.PI * 2f; array[i + 1] = new Vector3(Mathf.Cos(num2) * radius, 0f, Mathf.Sin(num2) * radius); array2[i + 1] = new Color(1f, 1f, 1f, 0f); array3[i + 1] = new Vector2(num, 1f); } for (int j = 1; j <= segments; j++) { list.Add(0); list.Add(j); list.Add(j + 1); list.Add(0); list.Add(j + 1); list.Add(j); } Mesh val = NewMesh("CTF Disc"); val.vertices = array; val.colors = array2; val.uv = array3; val.triangles = list.ToArray(); val.RecalculateNormals(); val.RecalculateBounds(); return val; } internal static GameObject Node(string name, Transform parent) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown GameObject val = new GameObject(name) { hideFlags = (HideFlags)61 }; if ((Object)(object)parent != (Object)null) { val.transform.SetParent(parent, false); } return val; } internal static MeshRenderer Renderer(string name, Transform parent, Mesh mesh, Material material) { GameObject val = Node(name, parent); val.AddComponent<MeshFilter>().sharedMesh = mesh; MeshRenderer val2 = val.AddComponent<MeshRenderer>(); ((Renderer)val2).sharedMaterial = material; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val2).receiveShadows = false; ((Renderer)val2).lightProbeUsage = (LightProbeUsage)0; ((Renderer)val2).reflectionProbeUsage = (ReflectionProbeUsage)0; return val2; } } internal sealed class CtfAudio { private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private readonly CtfConfig _config; private AudioClip _taken; private AudioClip _dropped; private AudioClip _returned; private AudioClip _captured; private AudioClip _matchStart; private bool _resolved; internal CtfAudio(CtfConfig config) { _config = config; } internal void OnSceneChanged() { _resolved = false; } internal void OnRuntimeLost() { _resolved = false; } internal void Play(int kind) { if (!_config.PlaySounds.Value) { return; } float num = Mathf.Clamp01(_config.SoundVolume.Value); if (num <= 0f) { return; } Resolve(); AudioClip val; float pitch; switch (kind) { default: return; case 1: val = _taken; pitch = 1.15f; break; case 2: val = _dropped; pitch = 0.85f; break; case 3: val = _returned; pitch = 1f; break; case 4: val = _captured; pitch = 1f; break; case 5: val = _matchStart; pitch = 1f; break; } if ((Object)(object)val == (Object)null) { CtfLog.Debug("No sound available for CTF event " + kind + "; staying quiet."); return; } try { AudioSource val2 = (((Object)(object)SoundManager.Instance != (Object)null) ? SoundManager.Instance._effectsSource : null); if (!((Object)(object)val2 == (Object)null)) { val2.pitch = pitch; val2.PlayOneShot(val, num); val2.pitch = 1f; } } catch { } } private void Resolve() { if (_resolved) { return; } _resolved = true; _taken = null; _dropped = null; _returned = null; _captured = null; _matchStart = null; try { RoundManager instance = RoundManager.Instance; PauseManager instance2 = PauseManager.Instance; if ((Object)(object)instance != (Object)null && instance.swooshClip != null) { if (instance.swooshClip.Length != 0) { _taken = instance.swooshClip[0]; } if (instance.swooshClip.Length > 1) { _returned = instance.swooshClip[1]; } } if ((Object)(object)instance2 != (Object)null) { _dropped = (((Object)(object)instance2.releaseMenuClip != (Object)null) ? instance2.releaseMenuClip : instance2.closeMenuClip); _matchStart = Private<AudioClip>((object)instance2, "startMatchClip"); } if ((Object)(object)instance != (Object)null) { _captured = Private<AudioClip>((object)instance, "plusOneClip"); if ((Object)(object)_captured == (Object)null) { _captured = Private<AudioClip>((object)instance, "winClip"); } } if ((Object)(object)_captured == (Object)null && (Object)(object)Crosshair.Instance != (Object)null) { _captured = Crosshair.Instance.headshotHitClip; } if ((Object)(object)_matchStart == (Object)null) { _matchStart = _taken; } if ((Object)(object)_returned == (Object)null) { _returned = _taken; } if ((Object)(object)_dropped == (Object)null) { _dropped = _returned; } CtfLog.Debug("CTF audio resolved: taken=" + Name((Object)(object)_taken) + " dropped=" + Name((Object)(object)_dropped) + " returned=" + Name((Object)(object)_returned) + " captured=" + Name((Object)(object)_captured)); } catch (Exception ex) { CtfLog.Debug("Could not resolve CTF audio clips: " + ex.Message); } } private static T Private<T>(object target, string fieldName) where T : Object { if (target == null) { return default(T); } try { object? obj = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target); return (T)((obj is T) ? obj : null); } catch { return default(T); } } private static string Name(Object obj) { return (obj == (Object)null) ? "none" : obj.name; } } internal sealed class CtfConfig { internal readonly ConfigEntry<bool> Enabled; internal readonly ConfigEntry<int> CapturesToWin; internal readonly ConfigEntry<float> RespawnDelay; internal readonly ConfigEntry<float> PickupRadius; internal readonly ConfigEntry<float> CaptureRadius; internal readonly ConfigEntry<float> DroppedReturnSeconds; internal readonly ConfigEntry<bool> RequireOwnFlagHome; internal readonly ConfigEntry<float> CarrierSpeedPenalty; internal readonly ConfigEntry<float> VoidDepth; internal readonly ConfigEntry<bool> SmartRespawns; internal readonly ConfigEntry<bool> RespawnUsesAllSpawnPoints; internal readonly ConfigEntry<float> RespawnEnemyClearance; internal readonly ConfigEntry<float> RespawnHazardRadius; internal readonly ConfigEntry<float> RespawnVariety; internal readonly ConfigEntry<float> SnapshotInterval; internal readonly ConfigEntry<float> RuleTickInterval; internal readonly ConfigEntry<float> MinimumBaseSeparation; internal readonly ConfigEntry<float> AutoBaseHeightOffset; internal readonly ConfigEntry<KeyCode> SetBaseAKey; internal readonly ConfigEntry<KeyCode> SetBaseBKey; internal readonly ConfigEntry<KeyCode> ClearBasesKey; internal readonly ConfigEntry<bool> ShowHud; internal readonly ConfigEntry<float> HudScale; internal readonly ConfigEntry<bool> ShowFlagBeacons; internal readonly ConfigEntry<bool> ShowWorldMarkers; internal readonly ConfigEntry<bool> WriteMatchLog; internal readonly ConfigEntry<bool> PlaySounds; internal readonly ConfigEntry<float> SoundVolume; private readonly ConfigFile _file; private readonly Dictionary<string, ConfigEntry<string>> _mapBases = new Dictionary<string, ConfigEntry<string>>(); internal CtfConfig(ConfigFile file) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Expected O, but got Unknown //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Expected O, but got Unknown //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Expected O, but got Unknown //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Expected O, but got Unknown //IL_0443: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Expected O, but got Unknown //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_04f6: Expected O, but got Unknown _file = file; Enabled = file.Bind<bool>("General", "Enabled", true, "Enable Capture the Flag. Every player in the lobby must have the same version of this mod installed."); CapturesToWin = file.Bind<int>("Rules", "CapturesToWin", 3, new ConfigDescription("Captures needed to win the current STRAFTAT round. Set to 0 to follow the lobby's own 'takes to win' setting.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); RespawnDelay = file.Bind<float>("Rules", "RespawnDelay", 3f, new ConfigDescription("Seconds a killed player spends spectating before respawning.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 15f), Array.Empty<object>())); PickupRadius = file.Bind<float>("Rules", "PickupRadius", 1.75f, new ConfigDescription("How close you must get to take or return a flag.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 6f), Array.Empty<object>())); CaptureRadius = file.Bind<float>("Rules", "CaptureRadius", 2.5f, new ConfigDescription("How close to your own base you must get to score a capture.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 8f), Array.Empty<object>())); DroppedReturnSeconds = file.Bind<float>("Rules", "DroppedReturnSeconds", 15f, new ConfigDescription("Seconds before a dropped flag returns home on its own.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 60f), Array.Empty<object>())); RequireOwnFlagHome = file.Bind<bool>("Rules", "RequireOwnFlagHome", true, "Require your own flag to be at your base before a capture counts. Turn this off for faster, messier games."); CarrierSpeedPenalty = file.Bind<float>("Rules", "CarrierSpeedPenalty", 0f, new ConfigDescription("Fraction of movement speed the flag carrier loses (0 = no penalty, 0.15 = 15% slower).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>())); VoidDepth = file.Bind<float>("Rules", "VoidDepth", 40f, new ConfigDescription("A dropped flag this far below the lower base is considered lost and returns home immediately.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 500f), Array.Empty<object>())); SmartRespawns = file.Bind<bool>("Respawns", "SmartRespawns", true, "Host only: choose where players come back instead of taking the game's random spawn point. Avoids arriving inside the enemy base, on top of somebody, or in a blast that is already going off. Turn this off to use STRAFTAT's own placement exactly as it ships."); RespawnUsesAllSpawnPoints = file.Bind<bool>("Respawns", "RespawnUsesAllSpawnPoints", true, "Consider every spawn point in the map rather than only the group the lobby size selected. A two-player lobby normally has just two points to choose between, which is not enough for placement to mean anything. Only used when SmartRespawns is on."); RespawnEnemyClearance = file.Bind<float>("Respawns", "RespawnEnemyClearance", 10f, new ConfigDescription("Metres of room to try to leave between a respawning player and the nearest enemy who can see them.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 40f), Array.Empty<object>())); RespawnHazardRadius = file.Bind<float>("Respawns", "RespawnHazardRadius", 6f, new ConfigDescription("Refuse a spawn point with a live mine, claymore, grenade or damage zone within this many metres. Set to 0 to skip the check.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 25f), Array.Empty<object>())); RespawnVariety = file.Bind<float>("Respawns", "RespawnVariety", 0.35f, new ConfigDescription("How much the choice is allowed to wander from the best-scoring spawn point. Near 0 always takes the best one, which is safest and also completely predictable; higher spreads it out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 3f), Array.Empty<object>())); SnapshotInterval = file.Bind<float>("Networking", "SnapshotInterval", 2f, new ConfigDescription("Seconds between full state broadcasts. This is also how quickly a late joiner catches up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); RuleTickInterval = file.Bind<float>("Networking", "RuleTickInterval", 0.06f, new ConfigDescription("Seconds between host flag-rule evaluations. Lower feels crisper and costs slightly more CPU.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 0.5f), Array.Empty<object>())); MinimumBaseSeparation = file.Bind<float>("Bases", "MinimumBaseSeparation", 8f, new ConfigDescription("Refuse to start CTF if the two bases end up closer together than this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 50f), Array.Empty<object>())); AutoBaseHeightOffset = file.Bind<float>("Bases", "AutoBaseHeightOffset", 0.25f, new ConfigDescription("Vertical offset applied to automatically discovered bases.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 5f), Array.Empty<object>())); SetBaseAKey = file.Bind<KeyCode>("Bases", "SetBaseAKey", (KeyCode)289, "Host only: save the first team's base at your current position, for this map."); SetBaseBKey = file.Bind<KeyCode>("Bases", "SetBaseBKey", (KeyCode)290, "Host only: save the second team's base at your current position, for this map."); ClearBasesKey = file.Bind<KeyCode>("Bases", "ClearBasesKey", (KeyCode)291, "Host only: forget this map's saved bases and go back to automatic placement."); ShowHud = file.Bind<bool>("Display", "ShowHud", true, "Show the CTF scoreboard, objective and announcements."); HudScale = file.Bind<float>("Display", "HudScale", 1f, new ConfigDescription("Size multiplier for the CTF HUD.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); ShowFlagBeacons = file.Bind<bool>("Display", "ShowFlagBeacons", true, "Draw a light beam above flags and bases so they are readable from across the map."); ShowWorldMarkers = file.Bind<bool>("Display", "ShowWorldMarkers", true, "Show on-screen edge markers pointing at the flags and your base."); WriteMatchLog = file.Bind<bool>("Display", "WriteMatchLog", true, "Also print CTF events into STRAFTAT's own match log feed."); PlaySounds = file.Bind<bool>("Audio", "PlaySounds", true, "Play sound cues for pickups, returns and captures."); SoundVolume = file.Bind<float>("Audio", "SoundVolume", 0.8f, new ConfigDescription("Volume of the CTF sound cues.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); } internal int ResolveCapturesToWin(int lobbyTakesToWin) { int value = CapturesToWin.Value; if (value > 0) { return value; } return (lobbyTakesToWin >= 1 && lobbyTakesToWin <= 100) ? lobbyTakesToWin : 3; } internal bool TryGetMapBases(string sceneName, out Vector3 baseA, out Vector3 baseB) { ConfigEntry<string> val = MapEntry(sceneName, isBaseA: true); ConfigEntry<string> val2 = MapEntry(sceneName, isBaseA: false); bool flag = CtfProtocol.TryParseVector(val.Value, out baseA); bool flag2 = CtfProtocol.TryParseVector(val2.Value, out baseB); return flag && flag2; } internal void SetMapBase(string sceneName, bool isBaseA, Vector3 position) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) MapEntry(sceneName, isBaseA).Value = CtfProtocol.FormatVector(position); Save(); } internal void ClearMapBases(string sceneName) { MapEntry(sceneName, isBaseA: true).Value = ""; MapEntry(sceneName, isBaseA: false).Value = ""; Save(); } private void Save() { try { _file.Save(); } catch (Exception ex) { CtfLog.Warning("Could not write the CTF config file: " + ex.Message); } } private ConfigEntry<string> MapEntry(string sceneName, bool isBaseA) { string text = MapSection(sceneName); string text2 = (isBaseA ? "BaseA" : "BaseB"); string key = text + "/" + text2; if (_mapBases.TryGetValue(key, out var value)) { return value; } ConfigEntry<string> val = _file.Bind<string>(text, text2, "", isBaseA ? "Base for the lower-numbered team, as x,y,z. Set it in-game with the SetBaseAKey hotkey while hosting." : "Base for the higher-numbered team, as x,y,z. Set it in-game with the SetBaseBKey hotkey while hosting."); _mapBases[key] = val; return val; } private static string MapSection(string sceneName) { if (string.IsNullOrWhiteSpace(sceneName)) { return "Map.Unknown"; } char[] array = sceneName.ToCharArray(); for (int i = 0; i < array.Length; i++) { if (!char.IsLetterOrDigit(array[i]) && array[i] != '-' && array[i] != '_') { array[i] = '_'; } } return "Map." + new string(array); } } internal sealed class CtfController { private enum EndStage { None, AwardPending, AdvancePending, Done } private const int PlayerLayerMask = -65601; private const float MaxSweptStep = 6f; private const float SweepSampleSpacing = 0.35f; private const float FreshBodyGraceSeconds = 2.5f; private const float MissingConfirmSeconds = 1.5f; private readonly CtfPlugin _plugin; private readonly CtfConfig _config; private readonly Dictionary<int, float> _respawnAt = new Dictionary<int, float>(); private readonly List<int> _dueBuffer = new List<int>(); private readonly Dictionary<int, float> _unfreezeAt = new Dictionary<int, float>(); private readonly Dictionary<int, Vector3> _lastPosition = new Dictionary<int, Vector3>(); private readonly Dictionary<int, int> _bodyId = new Dictionary<int, int>(); private readonly Dictionary<int, float> _bodySince = new Dictionary<int, float>(); private readonly Dictionary<int, float> _missingSince = new Dictionary<int, float>(); private readonly List<int> _auditSeen = new List<int>(); private readonly List<int> _auditDeparted = new List<int>(); private CtfState _state = new CtfState(); private string _status = "Waiting for a two-team match..."; private float _nextDiscovery; private float _nextRuleTick; private float _nextSnapshot; private float _nextCompatibilityCheck; private float _nextRespawnAudit; private EndStage _endStage = EndStage.None; private float _endStageAt; private int _endingTeam = -1; private int _baselineScoreA; private int _baselineScoreB; private bool _scoresMirrored; private string _remoteSession = ""; private int _remoteSequence = -1; internal CtfState State => _state; internal string Status => _status; internal bool Active => _config.Enabled.Value && _state.Active; internal bool Ending => _state.Active && _state.Ending; internal bool IsAuthority => CtfGame.IsServer && CtfNetwork.IsLobbyHost; internal CtfController(CtfPlugin plugin, CtfConfig config) { _plugin = plugin; _config = config; } internal void ResetForScene(string reason) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) RestoreTakeScores(); CtfSpawns.Reset(); _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); CtfState ctfState = new CtfState(); Scene activeScene = SceneManager.GetActiveScene(); ctfState.SceneName = ((Scene)(ref activeScene)).name; _state = ctfState; _remoteSession = ""; _remoteSequence = -1; _endStage = EndStage.None; _endingTeam = -1; _nextDiscovery = Time.unscaledTime + 1f; _status = reason; _plugin.OnStateApplied(); } internal void OnLobbyLeft() { if (_state.Active) { Deactivate("Left the lobby.", broadcast: false); } } internal void Tick() { float unscaledTime = Time.unscaledTime; if (!_config.Enabled.Value) { if (_state.Active) { Deactivate("Capture the Flag is switched off in the config.", broadcast: true); } return; } if (IsAuthority) { TickEnding(unscaledTime); } if (!CtfGame.IsPlayableScene) { string text = (CtfGame.IsExplorationMap ? "Not available in the exploration map." : "Waiting for a playable match..."); if (_state.Active && !_state.Ending && IsAuthority) { Deactivate(text, broadcast: true); } else if (!_state.Active) { _status = text; } } else if (!IsAuthority) { if (!_state.Active) { _status = "Waiting for the host to start Capture the Flag..."; } else { TickClientPrediction(); } } else { if (_state.Active && _state.Ending) { return; } TickRespawns(unscaledTime); if (!_state.Active) { if (unscaledTime >= _nextDiscovery) { _nextDiscovery = unscaledTime + 1f; TryStartHostState(); } } else if (ValidateRunningMatch(unscaledTime)) { if (unscaledTime >= _nextRuleTick) { _nextRuleTick = unscaledTime + _config.RuleTickInterval.Value; TickRules(unscaledTime); } if (unscaledTime >= _nextSnapshot) { _nextSnapshot = unscaledTime + _config.SnapshotInterval.Value; Broadcast(); } } } } private void TickClientPrediction() { float unscaledDeltaTime = Time.unscaledDeltaTime; CountDown(_state.FlagA, unscaledDeltaTime); CountDown(_state.FlagB, unscaledDeltaTime); } private static void CountDown(FlagState flag, float delta) { if (flag != null && flag.IsDropped && !(flag.ReturnIn <= 0f)) { flag.ReturnIn = Mathf.Max(0f, flag.ReturnIn - delta); } } private bool ValidateRunningMatch(float now) { if (!CtfGame.TryGetExactlyTwoTeams(out var teamA, out var teamB) || teamA != _state.TeamA || teamB != _state.TeamB) { Deactivate("Capture the Flag paused: the lobby no longer has these same two teams.", broadcast: true); return false; } if (CtfGame.CountPlayersOnTeam(teamA) == 0 || CtfGame.CountPlayersOnTeam(teamB) == 0) { Deactivate("Capture the Flag paused: a team has no players left.", broadcast: true); return false; } if (now >= _nextCompatibilityCheck) { _nextCompatibilityCheck = now + 2f; if (CtfNetwork.Instance != null && !CtfNetwork.Instance.AllPlayersCompatible(out var reason)) { Deactivate(reason, broadcast: true); return false; } } return true; } private void TryStartHostState() { //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) //IL_00ec: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) if (CtfNetwork.Instance != null && !CtfNetwork.Instance.AllPlayersCompatible(out var reason)) { _status = reason; return; } if (!CtfGame.TryGetExactlyTwoTeams(out var teamA, out var teamB)) { _status = "Capture the Flag needs exactly two teams with players on them."; return; } if (CtfGame.GetConnectedPlayerIds().Count < 2) { _status = "Capture the Flag needs at least two players."; return; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; string source; if (_config.TryGetMapBases(name, out var baseA, out var baseB)) { source = "this map's saved bases"; } else { if (!CtfGame.TryDiscoverBases(_config.AutoBaseHeightOffset.Value, out baseA, out baseB, out source)) { _status = "No usable base positions found on this map. Set them with the " + ((object)_config.SetBaseAKey.Value/*cast due to .constrained prefix*/).ToString() + " and " + ((object)_config.SetBaseBKey.Value/*cast due to .constrained prefix*/).ToString() + " hotkeys."; return; } CtfGame.OrientBasesTowardTeams(teamA, teamB, ref baseA, ref baseB); } float num = CtfGame.HorizontalDistance(baseA, baseB); float num2 = Mathf.Max(_config.MinimumBaseSeparation.Value, _config.PickupRadius.Value + _config.CaptureRadius.Value + 0.5f); if (num < num2) { _status = "The two bases are only " + num.ToString("0.0") + "m apart; " + num2.ToString("0.0") + "m is needed. Place them by hand with " + ((object)_config.SetBaseAKey.Value/*cast due to .constrained prefix*/).ToString() + " and " + ((object)_config.SetBaseBKey.Value/*cast due to .constrained prefix*/).ToString() + "."; return; } int capturesToWin = _config.ResolveCapturesToWin(CtfGame.LobbyTakesToWin); _state = new CtfState { SessionId = Guid.NewGuid().ToString("N"), Sequence = 1, SceneName = name, Active = true, Ending = false, TeamA = teamA, TeamB = teamB, CapturesToWin = capturesToWin, BaseA = baseA, BaseB = baseB, FlagA = new FlagState { Status = FlagStatus.Home, CarrierPlayerId = -1, Position = baseA }, FlagB = new FlagState { Status = FlagStatus.Home, CarrierPlayerId = -1, Position = baseB } }; _endStage = EndStage.None; _endingTeam = -1; _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); _nextRuleTick = Time.unscaledTime; _nextSnapshot = Time.unscaledTime; _nextCompatibilityCheck = Time.unscaledTime + 2f; _nextRespawnAudit = Time.unscaledTime + 4f; _status = "Capture the Flag is live. Bases from " + source + "."; CaptureScoreBaseline(); CtfGame.RepopulateAlivePlayers(); CtfLog.Info("CTF started on " + name + ": teams " + teamA + " vs " + teamB + ", first to " + capturesToWin + ", bases from " + source + " (" + num.ToString("0.0") + "m apart)."); _plugin.OnStateApplied(); Broadcast(); Announce(5, "CAPTURE THE FLAG - FIRST TO " + capturesToWin, "Capture the Flag: first team to " + capturesToWin + " captures wins the round."); } private void TickRules(float now) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: 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_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) bool flag = false; flag |= MaintainFlag(_state.FlagA, _state.BaseA, _state.TeamA, now); flag |= MaintainFlag(_state.FlagB, _state.BaseB, _state.TeamB, now); foreach (int connectedPlayerId in CtfGame.GetConnectedPlayerIds()) { int team = CtfGame.GetTeam(connectedPlayerId); if (!_state.IsPlayingTeam(team) || !CtfGame.HasLivingBody(connectedPlayerId) || CtfGame.IsFrozen(connectedPlayerId) || !CtfGame.TryGetPosition(connectedPlayerId, out var position)) { continue; } Vector3 value; Vector3 val = (_lastPosition.TryGetValue(connectedPlayerId, out value) ? value : position); _lastPosition[connectedPlayerId] = position; FlagState flagState = _state.FlagForTeam(team); FlagState flagState2 = _state.FlagForTeam(_state.OtherTeam(team)); Vector3 val2 = _state.BaseForTeam(team); if (flagState == null || flagState2 == null) { continue; } if (flagState.IsDropped && InZone(val, position, flagState.Position, _config.PickupRadius.Value)) { SendHome(flagState, val2); Announce(3, CtfGame.PlayerName(connectedPlayerId).ToUpperInvariant() + " RETURNED THE FLAG", FormatLog(connectedPlayerId, "returned their flag")); flag = true; } if (_state.TeamCarrying(connectedPlayerId) < 0 && !flagState2.IsCarried && InZone(val, position, flagState2.Position, _config.PickupRadius.Value)) { flagState2.Status = FlagStatus.Carried; flagState2.CarrierPlayerId = connectedPlayerId; flagState2.Position = position; flagState2.ReturnAt = 0f; flagState2.ReturnIn = 0f; Announce(1, CtfGame.PlayerName(connectedPlayerId).ToUpperInvariant() + " HAS THE FLAG", FormatLog(connectedPlayerId, "took the enemy flag")); flag = true; } if (flagState2.IsCarried && flagState2.CarrierPlayerId == connectedPlayerId && InZone(val, position, val2, _config.CaptureRadius.Value) && (!_config.RequireOwnFlagHome.Value || flagState.IsHome)) { Capture(connectedPlayerId, team, flagState2); flag = true; if (_state.Ending) { break; } } } if (flag) { StateChanged(); } } private bool MaintainFlag(FlagState flag, Vector3 home, int owningTeam, float now) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) if (flag.IsHome) { flag.Position = home; flag.CarrierPlayerId = -1; flag.ReturnIn = 0f; return false; } if (flag.IsDropped) { float num = Mathf.Min(_state.BaseA.y, _state.BaseB.y); if (flag.Position.y < num - _config.VoidDepth.Value) { SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG WAS LOST AND RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag fell out of the map and returned home."); return true; } if (now < flag.ReturnAt) { flag.ReturnIn = Mathf.Max(0f, flag.ReturnAt - now); return false; } SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag returned home."); return true; } int carrierPlayerId = flag.CarrierPlayerId; if (carrierPlayerId < 0 || !CtfGame.IsConnected(carrierPlayerId)) { SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag returned home after its carrier left."); return true; } int team = CtfGame.GetTeam(carrierPlayerId); if (team == owningTeam || !_state.IsPlayingTeam(team)) { SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag returned home after its carrier changed team."); return true; } if (!CtfGame.HasLivingBody(carrierPlayerId)) { Drop(flag, carrierPlayerId, now); return true; } if (CtfGame.TryGetCarryPoint(carrierPlayerId, out var position)) { flag.Position = position; } flag.ReturnIn = 0f; return false; } private void Capture(int playerId, int team, FlagState capturedFlag) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) SendHome(capturedFlag, _state.BaseForTeam(_state.OtherTeam(team))); if (team == _state.TeamA) { _state.ScoreA++; } else { _state.ScoreB++; } int num = _state.ScoreForTeam(team); MirrorTakeScores(); Announce(4, CtfGame.PlayerName(playerId).ToUpperInvariant() + " SCORED " + num + " / " + _state.CapturesToWin, FormatLog(playerId, "captured the flag (" + num + "/" + _state.CapturesToWin + ")")); if (num >= _state.CapturesToWin) { _state.Ending = true; _endingTeam = team; _endStage = EndStage.AwardPending; _endStageAt = Time.unscaledTime + 1.5f; _status = TeamLabel(team, upperCase: false) + " won the Capture the Flag round."; SendHome(_state.FlagA, _state.BaseA); SendHome(_state.FlagB, _state.BaseB); _respawnAt.Clear(); _unfreezeAt.Clear(); Announce(6, TeamLabel(team) + " WINS THE ROUND", null); } } private void TickEnding(float now) { if (_endStage == EndStage.None || now < _endStageAt) { return; } if (_endStage == EndStage.Done) { _endStage = EndStage.None; if (_state.Active) { Deactivate("Capture the Flag finished, but the next map did not load. Handing back to the game's normal rules.", broadcast: true); } } else if (!IsAuthority || !_state.Active) { _endStage = EndStage.None; } else if (_endStage == EndStage.AwardPending) { if (CtfGame.AwardRoundWin(_endingTeam)) { _scoresMirrored = false; _endStage = EndStage.AdvancePending; _endStageAt = now + 4f; } else { _endStage = EndStage.None; Deactivate("Capture the Flag could not end the round. Check the BepInEx log.", broadcast: true); } } else { _endStage = EndStage.Done; _endStageAt = now + 20f; CtfGame.AdvanceToNextRound(); } } internal void OnPlayerDied(int playerId) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (IsAuthority && Active && !_state.Ending) { if (CtfGame.TryGetPosition(playerId, out var position)) { CtfSpawns.RecordDeath(playerId, position); } float unscaledTime = Time.unscaledTime; bool flag = false; if (_state.FlagA.IsCarried && _state.FlagA.CarrierPlayerId == playerId) { Drop(_state.FlagA, playerId, unscaledTime); flag = true; } if (_state.FlagB.IsCarried && _state.FlagB.CarrierPlayerId == playerId) { Drop(_state.FlagB, playerId, unscaledTime); flag = true; } if (flag) { StateChanged(); } } } internal void ScheduleRespawn(int playerId) { if (IsAuthority && Active && !_state.Ending && !_respawnAt.ContainsKey(playerId)) { _respawnAt[playerId] = Time.unscaledTime + _config.RespawnDelay.Value; } } private void TickRespawns(float now) { if (Active && !_state.Ending && now >= _nextRespawnAudit) { _nextRespawnAudit = now + 1f; AuditForMissingPlayers(now); } if (_respawnAt.Count > 0) { _dueBuffer.Clear(); foreach (KeyValuePair<int, float> item in _respawnAt) { if (now >= item.Value) { _dueBuffer.Add(item.Key); } } foreach (int item2 in _dueBuffer) { _respawnAt.Remove(item2); if (!Active || _state.Ending || !CtfGame.IsConnected(item2)) { continue; } if (CtfGame.HasLivingBody(item2)) { if (!CtfGame.IsInAliveSet(item2)) { CtfGame.MarkAlive(item2); } } else if (!CtfGame.TryRespawn(item2)) { CtfLog.Debug("Respawn for player " + item2 + " did not go through; will retry."); _respawnAt[item2] = now + 1f; } else { CtfGame.MarkAlive(item2); CtfGame.ClearStartCountdown(); _unfreezeAt[item2] = now + 1f; _lastPosition.Remove(item2); } } } if (_unfreezeAt.Count == 0) { return; } _dueBuffer.Clear(); foreach (KeyValuePair<int, float> item3 in _unfreezeAt) { if (now >= item3.Value) { _dueBuffer.Add(item3.Key); } } foreach (int item4 in _dueBuffer) { _unfreezeAt.Remove(item4); if (Active && CtfGame.IsConnected(item4)) { CtfGame.ClearStartCountdown(); CtfGame.ReleaseRespawnFreeze(item4); } } } private void AuditForMissingPlayers(float now) { if (!CtfPatches.RoundStartedThisScene) { _missingSince.Clear(); return; } _auditSeen.Clear(); foreach (int connectedPlayerId in CtfGame.GetConnectedPlayerIds()) { _auditSeen.Add(connectedPlayerId); TrackBody(connectedPlayerId, now); float value; if (!_state.IsPlayingTeam(CtfGame.GetTeam(connectedPlayerId))) { _missingSince.Remove(connectedPlayerId); } else if (CtfGame.HasLivingBody(connectedPlayerId)) { if (!CtfGame.IsInAliveSet(connectedPlayerId)) { CtfLog.Debug("Correcting the alive set for " + CtfGame.PlayerName(connectedPlayerId) + ": listed as dead while standing there with health. Not respawning."); CtfGame.MarkAlive(connectedPlayerId); } _missingSince.Remove(connectedPlayerId); } else if (_respawnAt.ContainsKey(connectedPlayerId) || _unfreezeAt.ContainsKey(connectedPlayerId) || HasFreshBody(connectedPlayerId, now)) { _missingSince.Remove(connectedPlayerId); } else if (!_missingSince.TryGetValue(connectedPlayerId, out value)) { _missingSince[connectedPlayerId] = now; } else if (!(now - value < 1.5f)) { _missingSince.Remove(connectedPlayerId); CtfLog.Debug("Booking a catch-up respawn for " + CtfGame.PlayerName(connectedPlayerId) + "; bodyless for " + (now - value).ToString("0.0") + "s."); _respawnAt[connectedPlayerId] = now + _config.RespawnDelay.Value; } } ForgetDeparted(_auditSeen); } private void TrackBody(int playerId, float now) { int value; if (!CtfGame.TryGetBodyId(playerId, out var bodyId)) { _bodyId.Remove(playerId); _bodySince.Remove(playerId); } else if (!_bodyId.TryGetValue(playerId, out value) || value != bodyId) { _bodyId[playerId] = bodyId; _bodySince[playerId] = now; } } private bool HasFreshBody(int playerId, float now) { float value; return _bodySince.TryGetValue(playerId, out value) && now - value < 2.5f; } private void ForgetDeparted(List<int> stillConnected) { if (_bodyId.Count == 0 && _missingSince.Count == 0) { return; } _auditDeparted.Clear(); foreach (int key in _bodyId.Keys) { if (!stillConnected.Contains(key)) { _auditDeparted.Add(key); } } foreach (int key2 in _missingSince.Keys) { if (!stillConnected.Contains(key2) && !_auditDeparted.Contains(key2)) { _auditDeparted.Add(key2); } } foreach (int item in _auditDeparted) { _bodyId.Remove(item); _bodySince.Remove(item); _missingSince.Remove(item); } } internal bool TrySetHostBase(bool baseA) { //IL_0054: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00fe: Unknown result type (might be due to invalid IL or missing references) if (!IsAuthority) { _plugin.OnCtfEvent(0, "ONLY THE HOST CAN PLACE CTF BASES"); return false; } if (!CtfGame.TryGetLocalPosition(out var position)) { _plugin.OnCtfEvent(0, "STAND SOMEWHERE IN THE MAP FIRST"); return false; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!_config.TryGetMapBases(name, out var _, out var _) && _state.Active) { _config.SetMapBase(name, !baseA, baseA ? _state.BaseB : _state.BaseA); } _config.SetMapBase(name, baseA, position); string text = (baseA ? "A" : "B"); CtfLog.Info("Saved base " + text + " for " + name + " at " + CtfProtocol.FormatVector(position) + "."); _plugin.OnCtfEvent(0, "SAVED BASE " + text + " FOR " + name.ToUpperInvariant()); RestartHostState("Reloading this map's saved bases..."); return true; } internal void ClearHostBases() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!IsAuthority) { _plugin.OnCtfEvent(0, "ONLY THE HOST CAN PLACE CTF BASES"); return; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; _config.ClearMapBases(name); CtfLog.Info("Cleared the saved bases for " + name + "; falling back to automatic placement."); _plugin.OnCtfEvent(0, "CLEARED SAVED BASES FOR " + name.ToUpperInvariant()); RestartHostState("Returning to automatic base placement..."); } private void RestartHostState(string reason) { Deactivate(reason, broadcast: true); _nextDiscovery = 0f; TryStartHostState(); } internal void ApplyRemoteSnapshot(string payload) { //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 (IsAuthority) { return; } if (!CtfProtocol.TryDeserialize(payload, out var state, out var error)) { CtfLog.Warning("Rejected a CTF snapshot: " + error); return; } string sceneName = state.SceneName; Scene activeScene = SceneManager.GetActiveScene(); if (string.Equals(sceneName, ((Scene)(ref activeScene)).name, StringComparison.Ordinal) && (!(state.SessionId == _remoteSession) || state.Sequence >= _remoteSequence)) { _remoteSession = state.SessionId; _remoteSequence = state.Sequence; _state = state; _status = (state.Active ? "Capture the Flag is live." : "The host has paused Capture the Flag."); _plugin.OnStateApplied(); } } internal void ApplyRemoteEvent(string sessionId, int kind, string message) { if (!IsAuthority && CtfEventKind.IsValid(kind) && (string.IsNullOrEmpty(_remoteSession) || string.IsNullOrEmpty(sessionId) || string.Equals(sessionId, _remoteSession, StringComparison.Ordinal)) && CtfProtocol.TrySanitiseAnnouncement(message, out var sanitised)) { _plugin.OnCtfEvent(kind, sanitised); } } private void Drop(FlagState flag, int carrierId, float now) { //IL_0003: 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Vector3 position = DropPosition(carrierId, flag); flag.Status = FlagStatus.Dropped; flag.CarrierPlayerId = -1; flag.Position = position; flag.ReturnAt = now + _config.DroppedReturnSeconds.Value; flag.ReturnIn = _config.DroppedReturnSeconds.Value; Announce(2, CtfGame.PlayerName(carrierId).ToUpperInvariant() + " DROPPED THE FLAG", FormatLog(carrierId, "dropped the flag")); } private static Vector3 DropPosition(int carrierId, FlagState flag) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) if (!CtfGame.TryGetPosition(carrierId, out var position)) { return flag.Position; } try { Vector3 val = position + Vector3.up * 1.2f; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val, Vector3.down, ref val2, 6f, -65601, (QueryTriggerInteraction)1)) { return ((RaycastHit)(ref val2)).point + Vector3.up * 0.15f; } } catch { } return position + Vector3.up * 0.15f; } private static void SendHome(FlagState flag, Vector3 home) { //IL_0010: 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) flag.Status = FlagStatus.Home; flag.CarrierPlayerId = -1; flag.Position = home; flag.ReturnAt = 0f; flag.ReturnIn = 0f; } private static bool InZone(Vector3 point, Vector3 centre, float radius) { //IL_000d: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(2.2f, radius); if (Mathf.Abs(point.y - centre.y) > num) { return false; } return CtfGame.HorizontalDistance(point, centre) <= radius; } private static bool InZone(Vector3 from, Vector3 to, Vector3 centre, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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) if (InZone(to, centre, radius)) { return true; } Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.35f || magnitude > 6f) { return false; } int num = Mathf.CeilToInt(magnitude / 0.35f); for (int i = 1; i < num; i++) { if (InZone(from + val * ((float)i / (float)num), centre, radius)) { return true; } } return false; } private void StateChanged() { _state.Sequence++; _plugin.OnStateApplied(); Broadcast(); } private void Broadcast() { if (IsAuthority) { CtfNetwork.Instance?.BroadcastSnapshot(_state); } } private void Announce(int kind, string headline, string matchLogLine) { _plugin.OnCtfEvent(kind, headline); CtfNetwork.Instance?.BroadcastEvent(_state.SessionId, kind, headline); if (_config.WriteMatchLog.Value && !string.IsNullOrEmpty(matchLogLine)) { CtfGame.WriteMatchLog(matchLogLine); } CtfLog.Debug("CTF event " + kind + ": " + headline); } private static string FormatLog(int playerId, string what) { return CtfGame.NameTag(playerId) + " " + what; } private string TeamLabel(int teamId, bool upperCase = true) { string text = ((teamId == _state.TeamA) ? "team 1" : ((teamId == _state.TeamB) ? "team 2" : "a team")); return upperCase ? text.ToUpperInvariant() : text; } private void Deactivate(string reason, bool broadcast) { bool active = _state.Active; RestoreTakeScores(); _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); _endStage = EndStage.None; _endingTeam = -1; if (active) { _state.Active = false; _state.Ending = false; _state.Sequence++; if (broadcast) { Broadcast(); } if (IsAuthority) { CtfGame.ClearWaitForDrawHandle(); CtfGame.RepopulateAlivePlayers(); } } _status = reason; _nextDiscovery = Time.unscaledTime + 2f; _plugin.OnStateApplied(); if (active) { CtfLog.Info("Capture the Flag stopped: " + reason); } else { CtfLog.Debug("CTF stayed inactive: " + reason); } } private void CaptureScoreBaseline() { _baselineScoreA = 0; _baselineScoreB = 0; _scoresMirrored = false; try { if (!((Object)(object)ScoreManager.Instance == (Object)null)) { _baselineScoreA = ScoreManager.Instance.GetRoundScore(_state.TeamA); _baselineScoreB = ScoreManager.Instance.GetRoundScore(_state.TeamB); } } catch { } } private void MirrorTakeScores() { if (IsAuthority) { _scoresMirrored = true; CtfGame.SyncTakeScores(_state.TeamA, _baselineScoreA + _state.ScoreA, _state.TeamB, _baselineScoreB + _state.ScoreB); } } private void RestoreTakeScores() { if (_scoresMirrored) { _scoresMirrored = false; if (IsAuthority) { CtfGame.SyncTakeScores(_state.TeamA, _baselineScoreA, _state.TeamB, _baselineScoreB); } } } } internal static class CtfGame { private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo _roundWon; private static MethodInfo _updateMatchPointsHud; private static MethodInfo _respawnLogic; private static MethodInfo _setPlayerMoveLogic; private static FieldInfo _waitForDrawCoroutine; private static FieldInfo _roundHasStarted; private static FieldInfo _currentSpawnPoints; private static bool _resolved; private static float _nextLatchReport; internal static bool IsServer { get { try { return (Object)(object)InstanceFinder.NetworkManager != (Object)null && InstanceFinder.NetworkManager.IsServer; } catch { return false; } } } internal static bool IsPlayableScene { get { try { if ((Object)(object)GameManager.Instance == (Object)null || (Object)(object)ScoreManager.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } if ((Object)(object)PauseManager.Instance == (Object)null) { return false; } if (PauseManager.Instance.inMainMenu || PauseManager.Instance.inVictoryMenu) { return false; } if (PauseManager.Instance.onEndRoundScreen) { return false; } if (IsExplorationMap) { return false; } return true; } catch { return false; } } } internal static bool IsExplorationMap { get { try { if ((Object)(object)MapsManager.Instance != (Object)null && MapsManager.Instance.inExplorationMap) { return true; } return (Object)(object)SceneMotor.Instance != (Object)null && SceneMotor.Instance.testMap; } catch { return false; } } } internal static int LobbyTakesToWin { get { try { return ((Object)(object)ScoreManager.Instance != (Object)null) ? ScoreManager.Instance.RoundScoreRequiredToWin : 3; } catch { return 3; } } } internal static int LocalPlayerId { get { try { return ((Object)(object)ClientInstance.Instance != (Object)null) ? ClientInstance.Instance.PlayerId : (-1); } catch { return -1; } } } internal static FirstPersonController LocalPlayer { get { try { int localPlayerId = LocalPlayerId; PlayerManager manager; FirstPersonController player; return (localPlayerId >= 0 && TryGetPlayer(localPlayerId, out manager, out player)) ? player : null; } catch { return null; } } } internal static int RespawningPlayerId { get; private set; } = -1; internal static bool ResolveReflection() { if (_resolved) { return true; } bool ok = true; _roundWon = Require(typeof(GameManager), "RoundWon", ref ok, (MethodInfo m) => m.GetParameters().Length == 1); _waitForDrawCoroutine = RequireField(typeof(GameManager), "waitForDrawCoroutine", ref ok); _respawnLogic = Require(typeof(PlayerManager), null, ref ok, (MethodInfo m) => m.Name.StartsWith("RpcLogic___CmdRespawn_", StringComparison.Ordinal) && m.GetParameters().Length == 0, "PlayerManager.RpcLogic___CmdRespawn_*"); _setPlayerMoveLogic = Require(typeof(PlayerManager), null, ref ok, (MethodInfo m) => m.Name.StartsWith("RpcLogic___SetPlayerMove_", StringComparison.Ordinal) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool), "PlayerManager.RpcLogic___SetPlayerMove_*"); _updateMatchPointsHud = Optional(typeof(GameManager), "UpdateMatchPointsHUD"); _roundHasStarted = typeof(FirstPersonController).GetField("roundHasStarted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_roundHasStarted == null) { CtfLog.Warning("FirstPersonController.roundHasStarted was not found; respawned players may not trigger teleporters or void kills."); } _currentSpawnPoints = typeof(PlayerManager).GetField("CurrentSpawnPoints", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_currentSpawnPoints == null) { CtfLog.Debug("PlayerManager.CurrentSpawnPoints was not found; respawn placement will always consider every spawn point in the map."); } _resolved = ok; return ok; } internal static List<int> GetConnectedPlayerIds() { try { List<int> list = new List<int>(); foreach (KeyValuePair<int, ClientInstance> playerInstance in ClientInstance.playerInstances) { if ((Object)(object)playerInstance.Value != (Object)null) { list.Add(playerInstance.Key); } } list.Sort(); return list; } catch { return new List<int>(); } } internal static bool IsConnected(int playerId) { try { ClientInstance value; return ClientInstance.playerInstances.TryGetValue(playerId, out value) && (Object)(object)value != (Object)null; } catch { return false; } } internal static bool TryGetExactlyTwoTeams(out int teamA, out int teamB) { teamA = -1; teamB = -1; if ((Object)(object)ScoreManager.Instance == (Object)null) { return false; } HashSet<int> hashSet = new HashSet<int>(); foreach (int connectedPlayerId in GetConnectedPlayerIds()) { int team = GetTeam(connectedPlayerId); if (team >= 0) { hashSet.Add(team); } if (hashSet.Count > 2) { return false; } } if (hashSet.Count != 2) { return false; } List<int> list = hashSet.ToList(); list.Sort(); teamA = list[0]; teamB = list[1]; return true; } internal static int GetTeam(int playerId) { try { return ((Object)(object)ScoreManager.Instance != (Object)null) ? ScoreManager.Instance.GetTeamId(playerId) : (-1); } catch { return -1; } } internal static int CountPlayersOnTeam(int teamId) { int num = 0; foreach (int connectedPlayerId in GetConnectedPlayerIds()) { if (GetTeam(connectedPlayerId) == teamId) { num++; } } return num; } internal static bool TryGetPlayer(int playerId, out PlayerManager manager, out FirstPersonController player) { manager = null; player = null; try { if (!ClientInstance.playerInstances.TryGetValue(playerId, out var value) || (Object)(object)value == (Object)null) { return false; } manager = (((Object)(object)value.PlayerSpawner != (Object)null) ? value.PlayerSpawner : ((Component)value).GetComponent<PlayerManager>()); if ((Object)(object)manager == (Object)null) { return false; } player = manager.player; return (Object)(object)player != (Object)null; } catch { return false; } } internal static bool TryGetPosition(int playerId, out Vector3 position) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0042: 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) position = Vector3.zero; if (!TryGetPlayer(playerId, out var _, out var player)) { return false; } Transform transform = ((Component)player).transform; if ((Object)(object)transform == (Object)null) { return false; } position = transform.position; return IsFinite(position); } internal static bool TryGetCarryPoint(int playerId, out Vector3 position) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if (!TryGetPosition(playerId, out var position2)) { return false; } position = position2 + Vector3.up * 1.1f; return true; } internal static bool IsInAliveSet(int playerId) { try { return (Object)(object)GameManager.Instance != (Object)null && GameManager.Instance.alivePlayers.Contains(playerId); } catch { return false; } } internal static bool HasLivingBody(int playerId) { try { if (!TryGetPlayer(playerId, out var _, out var player)) { return false; } if (!((Component)player).gameObject.activeInHierarchy) { return false; } PlayerHealth val = ((Component)player).GetComponent<PlayerHealth>(); if ((Object)(object)val == (Object)null) { val = ((Component)player).GetComponentInChildren<PlayerHealth>(true); } if ((Object)(object)val == (Object)null) { return true; } return val.health > 0f; } catch { return false; } } internal static bool TryGetBodyId(int playerId, out int bodyId) { bodyId = 0; if (!TryGetPlayer(playerId, out var _, out var player)) { return false; } try { bodyId = ((Object)player).GetInstanceID(); return true; } catch { return false; } } internal static bool IsFrozen(int playerId) { try { if (!TryGetPlayer(playerId, out var _, out var player)) { return true; } return !player.canMove; } catch { return true; } } internal static string PlayerName(int playerId) { try { if (ClientInstance.playerInstances.TryGetValue(playerId, out var value) && (Object)(object)value != (Object)null && !string.IsNullOrEmpty(value.PlayerName)) { return value.PlayerName; } } catch { } return "player " + playerId; } internal static bool TryGetLocalPosition(out Vector3 position) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; int localPlayerId = LocalPlayerId; return localPlayerId >= 0 && TryGetPosition(localPlayerId, out position); } internal static bool TryDiscoverBases(float heightOffset, out Vector3 baseA, out Vector3 baseB, out string source) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0012: 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_0069: 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_00f4: 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) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028b: 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) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) baseA = Vector3.zero; baseB = Vector3.zero; source = ""; List<Vector3> list = new List<Vector3>(); try { SpawnPoint[] array = Object.FindObjectsOfType<SpawnPoint>(true); foreach (SpawnPoint val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null)) { Vector3 position = ((Component)val).transform.position; if (IsFinite(position)) { list.Add(position); } } } } catch (Exception ex) { CtfLog.Debug("Spawn point discovery failed: " + ex.Message); } if (list.Count >= 2) { source = "the map's spawn points"; } else { foreach (int connectedPlayerId in GetConnectedPlayerIds()) { if (TryGetPosition(connectedPlayerId, out var position2)) { list.Add(position2); } } source = "player positions"; } if (list.Count < 2) { return false; } list = (from p in list orderby p.x, p.z, p.y select p).ToList(); float num = -1f; int num2 = -1; int index = -1; for (int num3 = 0; num3 < list.Count - 1; num3++) { for (int num4 = num3 + 1; num4 < list.Count; num4++) { float num5 = list[num3].x - list[num4].x; float num6 = list[num3].z - list[num4].z; float num7 = num5 * num5 + num6 * num6; if (!(num7 <= num)) { num = num7; num2 = num3; index = num4; } } } if (num2 < 0) { return false; } baseA = list[num2] + Vector3.up * heightOffset; baseB = list[index] + Vector3.up * heightOffset; return true; } internal static float HorizontalDistance(Vector3 a, Vector3 b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } internal static void OrientBasesTowardTeams(int teamA, int teamB, ref Vector3 baseA, ref Vector3 baseB) { //IL_0022: 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_002e: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_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_0079: 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) if (TryTeamCentroid(teamA, out var centroid) && TryTeamCentroid(teamB, out var centroid2)) { float num = HorizontalDistance(baseA, centroid) + HorizontalDistance(baseB, centroid2); float num2 = HorizontalDistance(baseB, centroid) + HorizontalDistance(baseA, centroid2); if (!(num2 >= num)) { Vector3 val = baseA; baseA = baseB; baseB = val; } } } private static bool TryTeamCentroid(int team, out Vector3 centroid) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_0051: Unknown result type (might be due to invalid IL or missing references) centroid = Vector3.zero; int num = 0; foreach (int connectedPlayerId in GetConnectedPlayerIds()) { if (GetTeam(connectedPlayerId) == team && TryGetPosition(connectedPlayerId, out var position)) { centroid += position; num++; } } if (num == 0) { return false; } centroid /= (float)num; return true; } internal static bool TryRespawn(int playerId) { if (_respawnLogic == null) { return false; } if (!TryGetManager(playerId, out var manager)) { return false; } int hostSuit; int hostCig; bool flag = TryWearPlayerCosmetics(manager, out hostSuit, out hostCig); RespawningPlayerId = playerId; try { _respawnLogic.Invoke(manager, null); return true; } catch (Exception ex) { CtfLog.Error("Could not respawn " + PlayerName(playerId) + ": " + ex); return false; } finally { RespawningPlayerId = -1; if (flag) { RestoreCosmetics(hostSuit, hostCig); } } } internal static void CollectAssignedSpawnPoints(int playerId, List<SpawnPoint> into) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) into.Clear(); if (_currentSpawnPoints == null) { return; } try { if (!TryGetManager(playerId, out var manager) || !(_currentSpawnPoints.GetValue(manager) is SpawnPoint[] array)) { return; } SpawnPoint[] array2 = array; foreach (SpawnPoint val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null) && ((Component)val).gameObject.activeInHierarchy && IsFinite(((Component)val).transform.position)) { into.Add(val); } } } catch (Exception ex) { CtfLog.Debug("Could not read the game's assigned spawn points: " + ex.Message); into.Clear(); } } private static bool TryWearPlayerCosmetics(PlayerManager manager, out int hostSuit, out int hostCig) { hostSuit = 0; hostCig = 0; try { CosmeticsManager instance = CosmeticsManager.Instance; if ((Object)(object)instance == (Object)null) { return false; } PlayerSetup val = (((Object)(object)manager.player != (Object)null) ? manager.player.setupScript : null); if ((Object)(object)val == (Object)null) { return false; } hostSuit = instance.currentsuitIndex; hostCig = instance.currentcigIndex; if (hostSuit == val.mat && hostCig == val.cig) { return false; } instance.currentsuitIndex = val.mat; instance.currentcigIndex = val.cig; return true; } catch (Exception ex) { CtfLog.Debug("Could not preserve respawn cosmetics: " + ex.Message); return false; } } private static void RestoreCosmetics(int suit, int cig) { try { CosmeticsManager instance = CosmeticsManager.Instance; if (!((Object)(object)instance == (Object)null)) { instance.currentsuitIndex = suit; instance.currentcigIndex = cig; } } catch (Exception ex) { CtfLog.Error("Could not restore the host's cosmetics: " + ex); } } internal static void ReleaseRespawnFreeze(int playerId) { if (!TryGetManager(playerId, out var manager) || (Object)(object)manager.player == (Object)null) { return; } try { _setPlayerMoveLogic?.Invoke(manager, new object[1] { true }); } catch (Exception ex) { CtfLog.Debug("Could not release the respawn freeze: " + ex.Message); } } internal static void ClearStartCountdown() { if (!CtfPatches.RoundStartedThisScene) { return; } try { GameManager instance = GameManager.Instance; if (instance != null) { instance.SetStartTime(0f); } } catch (Exception ex) { CtfLog.Debug("Could not clear the start countdown: " + ex.Message); } } internal static void EnforceRoundIsLive() { if (!CtfPatches.RoundStartedThisScene) { return; } try { if (PauseManager.BetweenRounds) { PauseManager.BetweenRounds = false; float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextLatchReport)) { _nextLatchReport = unscaledTime + 5f; CtfLog.Debug("Cleared a between-rounds latch during a live CTF round; left set it would have disarmed every explosive on this machine."); } } } catch { } } internal static void MarkAlive(int playerId) { try { GameManager.Instance?.alivePlayers.Add(playerId); } catch { } } internal static void RepopulateAlivePlayers() { try { if ((Object)(object)GameManager.Instance == (Object)null) { return; } GameManager.Instance.alivePlayers.Clear(); foreach (int connectedPlayerId in GetConnectedPlayerIds()) { GameManager.Instance.alivePlayers.Add(connectedPlayerId); } } catch { } } internal static void ClearWaitForDrawHandle() { try { _waitForDrawCoroutine?.SetValue(GameManager.Instance, null); } catch { } } internal static void StopWaitForDraw() { try { GameManager instance = GameManager.Instance; if (!((Object)(object)instance == (Object)null) && !(_waitForDrawCoroutine == null)) { object? value = _waitForDrawCoroutine.GetValue(instance); Coroutine val = (Coroutine)((value is Coroutine) ? value : null); if (val != null) { ((MonoBehaviour)instance).StopCoroutine(val); } _waitForDrawCoroutine.SetValue(instance, null); } } catch (Exception ex) { CtfLog.Debug("Could not stop the draw coroutine: " + ex.Message); } } internal static void SyncTakeScores(int teamA, int scoreA, int teamB, int scoreB) { try { ScoreManager instance = ScoreManager.Instance; if (!((Object)(object)instance == (Object)null)) { SetTakeScore(instance, teamA, scoreA); SetTakeScore(instance, teamB, scoreB); PushMatchPointsHud(-1); } } catch (Exception ex) { CtfLog.Debug("Could not mirror CTF scores: " + ex.Message); } } private static void SetTakeScore(ScoreManager scores, int teamId, int value) { if (teamId >= 0) { int roundScore = scores.GetRoundScore(teamId); if (roundScore != value) { scores.AddRoundScore(teamId, value - roundScore); } } } internal static void PushMatchPointsHud(int winningTeamId) { if (_updateMatchPointsHud == null || (Object)(object)GameManager.Instance == (Object)null || (Object)(object)ScoreManager.Instance == (Object)null) { return; } try { Dictionary<int, int> roundScoreDictionary = ScoreManager.Instance.GetRoundScoreDictionary(); _updateMatchPointsHud.Invoke(GameManager.Instance, new object[2] { winningTeamId, roundScoreDictionary }); } catch (Exception ex) { CtfLog.Debug("Could not refresh the match points HUD: " + ex.Message); } } internal static bool AwardRoundWin(int winningTeamId) { if (_roundWon == null) { CtfLog.Error("Cannot award the CTF round win: GameManager.RoundWon is missing."); return false; } try { PushMatchPointsHud(winningTeamId); _roundWon.Invoke(GameManager.Instance, new object[1] { winningTeamId }); RoundManager.Instance.CmdEndRound(winningTeamId); CtfLog.Info("Team " + winningTeamId + " won the CTF round; handed off to STRAFTAT's round-end flow."); return true; } catch (Exception ex) { CtfLog.Error("Could not finish the CTF round: " + ex); return false; } } internal static void AdvanceToNextRound() { try { RepopulateAlivePlayers(); SceneMotor instance = SceneMotor.Instance; if (instance != null) { instance.ChangeNetworkScene(); } } catch (Exception ex) { CtfLog.Error("Could not advance to the next round: " + ex); } } internal static void WriteMatchLog(string text) { if (string.IsNullOrEmpty(text)) { return; } try { PauseManager instance = PauseManager.Instance; if (instance != null) { instance.WriteLog(text); } } catch (Exception ex) { CtfLog.Debug("Could not write to the match log: " + ex.Message); } } internal static string NameTag(int playerId) { try { if (ClientInstance.playerInstances.TryGetValue(playerId, out var value) && (Object)(object)value != (Object)null) { return value.PlayerNameTag; } } catch { } return PlayerName(playerId); } internal static void EnsureRoundStartedFlag(FirstPersonController player) { if (_roundHasStarted == null || (Object)(object)player == (Object)null) { return; } try { if (!(bool)_roundHasStarted.GetValue(player)) { _roundHasStarted.SetValue(player, true); } } catch { } } internal static bool IsFinite(Vector3 v) { //IL_0000: 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_001a: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) return !float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z) && !float.IsInfinity(v.x) && !float.IsInfinity(v.y) && !float.IsInfinity(v.z); } private static bool TryGetManager(int playerId, out PlayerManager manager) { manager = null; try { if (!ClientInstance.playerInstances.TryGetValue(playerId, out var value) || (Object)(object)value == (Object)null) { return false; } manager = (((Object)(object)value.PlayerSpawner != (Object)null) ? value.PlayerSpawner : ((Component)value).GetComponent<PlayerManager>()); return (Object)(object)manager != (Object)null; } catch { return false; } } private static MethodInfo Require(Type type, string name, ref bool ok, Func<MethodInfo, bool> predicate = null, string label = null) { MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => (name == null || m.Name == name) && (predicate == null || predicate(m))); if (methodInfo != null) { return methodInfo; } ok = false; CtfLog.Error("Missing required game member: " + (label ?? (type.Name + "." + name)) + ". This STRAFTAT version is not supported; Capture the Flag will stay inactive."); return null; } private static FieldInfo RequireField(Type type, string name, ref bool ok) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } ok = false; CtfLog.Error("Missing required game field: " + type.Name + "." + name + ". This STRAFTAT version is not supported; Capture the Flag will stay inactive."); return null; } private static MethodInfo Optional(Type type, string name) { MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { CtfLog.Debug("Optional game member not found: " + type.Name + "." + name); } return method; } } internal sealed class CtfHud { private sealed class CtfMarker { private const float EdgeInset = 90f; private readonly TextMeshProU