Decompiled source of CaptureTheFlag v1.1.2
BepInEx/plugins/StraftatCTF/StraftatCTF.dll
Decompiled 2 months 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 FishySteamworks.Client; using FishySteamworks.Server; 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.1.2.0")] [assembly: StraftatMod(false)] [assembly: AssemblyVersion("1.1.2.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 string _teamBalanceNote = ""; private float _nextDiscovery; private float _nextRuleTick; private float _nextSnapshot; 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 string _sceneMismatch = ""; private const float RosterGrace = 20f; private ulong _verifiedRoster; private float _rosterUnknownSince; private ulong _unknownRosterStamp; private float _lastSnapshotAt; private float _clientSilenceSince; private bool _clientSilenceReported; 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) { RestoreTakeScores(); CtfSpawns.Reset(); _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); _state = new CtfState { SceneName = CtfGame.MapSceneName }; _remoteSession = ""; _remoteSequence = -1; _sceneMismatch = ""; _lastSnapshotAt = 0f; _clientSilenceSince = 0f; _clientSilenceReported = false; _unknownRosterStamp = 0uL; _endStage = EndStage.None; _endingTeam = -1; _nextDiscovery = Time.unscaledTime + 1f; _status = reason; _teamBalanceNote = ""; _plugin.OnStateApplied(); } internal void OnLobbyLeft() { _verifiedRoster = 0uL; _rosterUnknownSince = 0f; _unknownRosterStamp = 0uL; 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) { TickClientWatchdog(unscaledTime); if (!_state.Active) { if (!_clientSilenceReported) { _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 CheckCompatibilityOnRosterChange(float now) { ulong lobbyRosterStamp = CtfNetwork.LobbyRosterStamp; if (lobbyRosterStamp == _verifiedRoster) { return true; } if (CtfNetwork.Instance == null) { _verifiedRoster = lobbyRosterStamp; return true; } string reason; switch (CtfNetwork.Instance.CheckCompatibility(out reason)) { case CtfCompatibility.Ok: _verifiedRoster = lobbyRosterStamp; _rosterUnknownSince = 0f; _unknownRosterStamp = 0uL; CtfLog.Debug("Lobby membership changed; every player still reports 1.1.2."); return true; case CtfCompatibility.Incompatible: _rosterUnknownSince = 0f; _unknownRosterStamp = 0uL; Deactivate(reason, broadcast: true); return false; default: if (_rosterUnknownSince <= 0f || _unknownRosterStamp != lobbyRosterStamp) { _rosterUnknownSince = now; _unknownRosterStamp = lobbyRosterStamp; CtfLog.Debug("Lobby membership changed; waiting on a version. The match continues. " + reason); return true; } if (now - _rosterUnknownSince < 20f) { return true; } CtfLog.Info("Standing Capture the Flag down: a player who joined " + 20f.ToString("0") + "s ago has still not reported a Capture the Flag version. " + reason); _rosterUnknownSince = 0f; _unknownRosterStamp = 0uL; Deactivate(reason, broadcast: true); return false; } } private bool ValidateRunningMatch(float now) { if (!CtfGame.TryGetExactlyTwoTeams(out var teamA, out var teamB) || teamA != _state.TeamA || teamB != _state.TeamB) { if (!CtfTeams.TryAdoptStrays(_state.TeamA, _state.TeamB, out var summary) || !CtfGame.TryGetExactlyTwoTeams(out teamA, out 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; } CtfLog.Info(summary + " Capture the Flag continues on the same two teams."); } 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 (!CheckCompatibilityOnRosterChange(now)) { return false; } return true; } private void TryStartHostState() { //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) if (CtfNetwork.Instance != null && CtfNetwork.Instance.CheckCompatibility(out var reason) != CtfCompatibility.Ok) { _status = reason; return; } if (CtfGame.GetConnectedPlayerIds().Count < 2) { _status = "Capture the Flag needs at least two players."; return; } if (!CtfGame.TryGetExactlyTwoTeams(out var teamA, out var teamB)) { if (CtfTeams.TryBalanceIntoTwoTeams(out var summary)) { _teamBalanceNote = summary; } if (!CtfGame.TryGetExactlyTwoTeams(out teamA, out teamB)) { _status = DescribeMissingTeams(); return; } } string mapSceneName = CtfGame.MapSceneName; if (mapSceneName.Length == 0) { _status = "Waiting for the map to finish loading..."; return; } string source; if (_config.TryGetMapBases(mapSceneName, 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 = mapSceneName, 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; _verifiedRoster = CtfNetwork.LobbyRosterStamp; _rosterUnknownSince = 0f; _unknownRosterStamp = 0uL; _nextRespawnAudit = Time.unscaledTime + 4f; _status = "Capture the Flag is live. Bases from " + source + "." + ((_teamBalanceNote.Length > 0) ? (" " + _teamBalanceNote) : ""); _teamBalanceNote = ""; CaptureScoreBaseline(); CtfGame.RepopulateAlivePlayers(); int count = CtfGame.GetConnectedPlayerIds().Count; CtfLog.Info("CTF started on " + mapSceneName + ": team " + teamA + " (" + CtfGame.CountPlayersOnTeam(teamA) + ") vs team " + teamB + " (" + CtfGame.CountPlayersOnTeam(teamB) + "), first to " + capturesToWin + ", bases from " + source + " (" + num.ToString("0.0") + "m apart)."); int num3 = CtfSpawns.CountDistinctSpawnPoints(); if (num3 > 0 && count > num3) { CtfLog.Warning("This map has " + num3 + " distinct spawn points for " + count + " players. Respawns will step aside from points that are already taken, but expect tighter spawns than a four-player match."); } _plugin.OnStateApplied(); Broadcast(); Announce(5, "CAPTURE THE FLAG - FIRST TO " + capturesToWin, "Capture the Flag: first team to " + capturesToWin + " captures wins the round."); } private static string DescribeMissingTeams() { List<int> connectedPlayerIds = CtfGame.GetConnectedPlayerIds(); HashSet<int> hashSet = new HashSet<int>(); foreach (int item in connectedPlayerIds) { int team = CtfGame.GetTeam(item); if (team >= 0) { hashSet.Add(team); } } if (hashSet.Count <= 1) { return "Capture the Flag needs two teams and every player is on the same one. Split the lobby with the team dropdowns in the main menu."; } if (!CtfTeams.LobbyIsPlayingTeams) { return "Capture the Flag needs two teams; this is a free-for-all with " + connectedPlayerIds.Count + " players on " + hashSet.Count + " teams. Switch the lobby to Teams in the main menu."; } return "Capture the Flag needs exactly two teams with players on them; this lobby has " + hashSet.Count + "."; } 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_011f: 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; } string mapSceneName = CtfGame.MapSceneName; if (mapSceneName.Length == 0) { _plugin.OnCtfEvent(0, "WAIT FOR THE MAP TO FINISH LOADING"); return false; } if (!_config.TryGetMapBases(mapSceneName, out var _, out var _) && _state.Active) { _config.SetMapBase(mapSceneName, !baseA, baseA ? _state.BaseB : _state.BaseA); } _config.SetMapBase(mapSceneName, baseA, position); string text = (baseA ? "A" : "B"); CtfLog.Info("Saved base " + text + " for " + mapSceneName + " at " + CtfProtocol.FormatVector(position) + "."); _plugin.OnCtfEvent(0, "SAVED BASE " + text + " FOR " + mapSceneName.ToUpperInvariant()); RestartHostState("Reloading this map's saved bases..."); return true; } internal void ClearHostBases() { if (!IsAuthority) { _plugin.OnCtfEvent(0, "ONLY THE HOST CAN PLACE CTF BASES"); return; } string mapSceneName = CtfGame.MapSceneName; if (mapSceneName.Length == 0) { _plugin.OnCtfEvent(0, "WAIT FOR THE MAP TO FINISH LOADING"); return; } _config.ClearMapBases(mapSceneName); CtfLog.Info("Cleared the saved bases for " + mapSceneName + "; falling back to automatic placement."); _plugin.OnCtfEvent(0, "CLEARED SAVED BASES FOR " + mapSceneName.ToUpperInvariant()); RestartHostState("Returning to automatic base placement..."); } private void RestartHostState(string reason) { Deactivate(reason, broadcast: true); _nextDiscovery = 0f; TryStartHostState(); } internal void ApplyRemoteSnapshot(string payload) { if (IsAuthority) { return; } if (!CtfProtocol.TryDeserialize(payload, out var state, out var error)) { CtfLog.Warning("Rejected a CTF snapshot: " + error); return; } string mapSceneName = CtfGame.MapSceneName; if (!string.Equals(state.SceneName, mapSceneName, StringComparison.Ordinal)) { NoteSceneMismatch(state.SceneName, mapSceneName); return; } _sceneMismatch = ""; if (!(state.SessionId == _remoteSession) || state.Sequence >= _remoteSequence) { _remoteSession = state.SessionId; _remoteSequence = state.Sequence; _lastSnapshotAt = Time.unscaledTime; _state = state; _status = (state.Active ? "Capture the Flag is live." : "The host has paused Capture the Flag."); _plugin.OnStateApplied(); } } private void TickClientWatchdog(float now) { if (!CtfNetwork.InLobby) { _clientSilenceSince = 0f; _clientSilenceReported = false; return; } if (!_state.Active && !string.IsNullOrEmpty(_remoteSession)) { _clientSilenceSince = 0f; _clientSilenceReported = false; return; } if (_lastSnapshotAt <= 0f) { if (_clientSilenceSince <= 0f) { _clientSilenceSince = now; } } else { _clientSilenceSince = _lastSnapshotAt; } float num = Mathf.Max(10f, _config.SnapshotInterval.Value * 4f); if (!(now - _clientSilenceSince >= num)) { if (_clientSilenceReported) { _clientSilenceReported = false; CtfLog.Info("Capture the Flag state is arriving from the host again."); } } else if (!_clientSilenceReported) { _clientSilenceReported = true; string text = ((_lastSnapshotAt <= 0f) ? "No Capture the Flag state has ever arrived from the host on this machine." : ("Capture the Flag state from the host stopped arriving " + (now - _lastSnapshotAt).ToString("0") + "s ago.")); CtfLog.Info(text + " Either the host has not started the mode, or this machine cannot receive its messages. If the host says Capture the Flag is live, keep this log and the host's log; flags cannot be drawn without that state."); _status = "No Capture the Flag state from the host. Flags cannot be shown on this machine."; } } private void NoteSceneMismatch(string incomingScene, string currentMap) { string text = incomingScene + " -> " + currentMap; if (!string.Equals(text, _sceneMismatch, StringComparison.Ordinal)) { _sceneMismatch = text; string text2 = (string.IsNullOrEmpty(currentMap) ? "no map (still loading)" : currentMap); CtfLog.Info("Ignoring the host's Capture the Flag state: it is for '" + incomingScene + "' and this machine is on " + text2 + ". If this does not clear within a few seconds the flags will not appear on this machine."); _status = "Waiting for the host's map to match this one..."; } } 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 readonly List<string> _loadedSceneNames = new List<string>(); private const float MinTeamSpreadFraction = 0.15f; private static float _nextLatchReport; private static string _latchLoggedScene; internal static bool IsServer { get { try { return (Object)(object)InstanceFinder.NetworkManager != (Object)null && InstanceFinder.NetworkManager.IsServer; } catch { return false; } } } internal static string MapSceneName { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) try { _loadedSceneNames.Clear(); int sceneCount = SceneManager.sceneCount; for (int i = 0; i < sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); if (((Scene)(ref sceneAt)).isLoaded) { _loadedSceneNames.Add(((Scene)(ref sceneAt)).name); } } string mapScene = CtfPatches.MapScene; Scene activeScene = SceneManager.GetActiveScene(); return CtfSceneNames.Resolve(mapScene, ((Scene)(ref activeScene)).name, _loadedSceneNames); } catch { return ""; } } } 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; } if (MapSceneName.Length == 0) { 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_0025: 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_0036: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00bf: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if (!TryTeamCentroid(teamA, out var centroid) || !TryTeamCentroid(teamB, out var centroid2)) { return; } float num = HorizontalDistance(baseA, baseB); float num2 = HorizontalDistance(centroid, centroid2); if (num <= 0f || num2 < num * 0.15f) { CtfLog.Debug("Not orienting the bases from the spawn layout: the two teams are only " + num2.ToString("0.0") + "m apart across " + num.ToString("0.0") + "m of map, which says nothing about which base is whose. Keeping the discovered order."); return; } float num3 = HorizontalDistance(baseA, centroid) + HorizontalDistance(baseB, centroid2); float num4 = HorizontalDistance(baseB, centroid) + HorizontalDistance(baseA, centroid2); if (!(num4 >= num3)) { 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() { try { if (!PauseManager.BetweenRounds) { return; } PauseManager.BetweenRounds = false; string mapSceneName = MapSceneName; if (!string.Equals(mapSceneName, _latchLoggedScene, StringComparison.Ordinal)) { _latchLoggedScene = mapSceneName; CtfLog.Info("Caught a between-rounds latch on " + mapSceneName + " a