using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ComputerysModdingUtilities; using FishNet; using HarmonyLib; using MyceliumNetworking; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Capture the Flag for STRAFTAT")] [assembly: AssemblyProduct("StraftatCTF")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: StraftatMod(false)] [assembly: AssemblyVersion("1.0.4.0")] namespace StraftatCTF; internal static class CtfArt { private static readonly List Owned = new List(); 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(); 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 list = new List((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 list = new List(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 list = new List(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 list = new List(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().sharedMesh = mesh; MeshRenderer val2 = val.AddComponent(); ((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((object)instance2, "startMatchClip"); } if ((Object)(object)instance != (Object)null) { _captured = Private((object)instance, "plusOneClip"); if ((Object)(object)_captured == (Object)null) { _captured = Private((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(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 Enabled; internal readonly ConfigEntry CapturesToWin; internal readonly ConfigEntry RespawnDelay; internal readonly ConfigEntry PickupRadius; internal readonly ConfigEntry CaptureRadius; internal readonly ConfigEntry DroppedReturnSeconds; internal readonly ConfigEntry RequireOwnFlagHome; internal readonly ConfigEntry CarrierSpeedPenalty; internal readonly ConfigEntry VoidDepth; internal readonly ConfigEntry SmartRespawns; internal readonly ConfigEntry RespawnUsesAllSpawnPoints; internal readonly ConfigEntry RespawnEnemyClearance; internal readonly ConfigEntry RespawnHazardRadius; internal readonly ConfigEntry RespawnVariety; internal readonly ConfigEntry SnapshotInterval; internal readonly ConfigEntry RuleTickInterval; internal readonly ConfigEntry MinimumBaseSeparation; internal readonly ConfigEntry AutoBaseHeightOffset; internal readonly ConfigEntry SetBaseAKey; internal readonly ConfigEntry SetBaseBKey; internal readonly ConfigEntry ClearBasesKey; internal readonly ConfigEntry ShowHud; internal readonly ConfigEntry HudScale; internal readonly ConfigEntry ShowFlagBeacons; internal readonly ConfigEntry ShowWorldMarkers; internal readonly ConfigEntry WriteMatchLog; internal readonly ConfigEntry PlaySounds; internal readonly ConfigEntry SoundVolume; private readonly ConfigFile _file; private readonly Dictionary> _mapBases = new Dictionary>(); 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("General", "Enabled", true, "Enable Capture the Flag. Every player in the lobby must have the same version of this mod installed."); CapturesToWin = file.Bind("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(0, 20), Array.Empty())); RespawnDelay = file.Bind("Rules", "RespawnDelay", 3f, new ConfigDescription("Seconds a killed player spends spectating before respawning.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 15f), Array.Empty())); PickupRadius = file.Bind("Rules", "PickupRadius", 1.75f, new ConfigDescription("How close you must get to take or return a flag.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 6f), Array.Empty())); CaptureRadius = file.Bind("Rules", "CaptureRadius", 2.5f, new ConfigDescription("How close to your own base you must get to score a capture.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 8f), Array.Empty())); DroppedReturnSeconds = file.Bind("Rules", "DroppedReturnSeconds", 15f, new ConfigDescription("Seconds before a dropped flag returns home on its own.", (AcceptableValueBase)(object)new AcceptableValueRange(3f, 60f), Array.Empty())); RequireOwnFlagHome = file.Bind("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("Rules", "CarrierSpeedPenalty", 0f, new ConfigDescription("Fraction of movement speed the flag carrier loses (0 = no penalty, 0.15 = 15% slower).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.5f), Array.Empty())); VoidDepth = file.Bind("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(5f, 500f), Array.Empty())); SmartRespawns = file.Bind("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("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("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(0f, 40f), Array.Empty())); RespawnHazardRadius = file.Bind("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(0f, 25f), Array.Empty())); RespawnVariety = file.Bind("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(0.01f, 3f), Array.Empty())); SnapshotInterval = file.Bind("Networking", "SnapshotInterval", 2f, new ConfigDescription("Seconds between full state broadcasts. This is also how quickly a late joiner catches up.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 10f), Array.Empty())); RuleTickInterval = file.Bind("Networking", "RuleTickInterval", 0.06f, new ConfigDescription("Seconds between host flag-rule evaluations. Lower feels crisper and costs slightly more CPU.", (AcceptableValueBase)(object)new AcceptableValueRange(0.02f, 0.5f), Array.Empty())); MinimumBaseSeparation = file.Bind("Bases", "MinimumBaseSeparation", 8f, new ConfigDescription("Refuse to start CTF if the two bases end up closer together than this.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 50f), Array.Empty())); AutoBaseHeightOffset = file.Bind("Bases", "AutoBaseHeightOffset", 0.25f, new ConfigDescription("Vertical offset applied to automatically discovered bases.", (AcceptableValueBase)(object)new AcceptableValueRange(-2f, 5f), Array.Empty())); SetBaseAKey = file.Bind("Bases", "SetBaseAKey", (KeyCode)289, "Host only: save the first team's base at your current position, for this map."); SetBaseBKey = file.Bind("Bases", "SetBaseBKey", (KeyCode)290, "Host only: save the second team's base at your current position, for this map."); ClearBasesKey = file.Bind("Bases", "ClearBasesKey", (KeyCode)291, "Host only: forget this map's saved bases and go back to automatic placement."); ShowHud = file.Bind("Display", "ShowHud", true, "Show the CTF scoreboard, objective and announcements."); HudScale = file.Bind("Display", "HudScale", 1f, new ConfigDescription("Size multiplier for the CTF HUD.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 2f), Array.Empty())); ShowFlagBeacons = file.Bind("Display", "ShowFlagBeacons", true, "Draw a light beam above flags and bases so they are readable from across the map."); ShowWorldMarkers = file.Bind("Display", "ShowWorldMarkers", true, "Show on-screen edge markers pointing at the flags and your base."); WriteMatchLog = file.Bind("Display", "WriteMatchLog", true, "Also print CTF events into STRAFTAT's own match log feed."); PlaySounds = file.Bind("Audio", "PlaySounds", true, "Play sound cues for pickups, returns and captures."); SoundVolume = file.Bind("Audio", "SoundVolume", 0.8f, new ConfigDescription("Volume of the CTF sound cues.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); } 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 val = MapEntry(sceneName, isBaseA: true); ConfigEntry 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 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 val = _file.Bind(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 _respawnAt = new Dictionary(); private readonly List _dueBuffer = new List(); private readonly Dictionary _unfreezeAt = new Dictionary(); private readonly Dictionary _lastPosition = new Dictionary(); private readonly Dictionary _bodyId = new Dictionary(); private readonly Dictionary _bodySince = new Dictionary(); private readonly Dictionary _missingSince = new Dictionary(); private readonly List _auditSeen = new List(); private readonly List _auditDeparted = new List(); private CtfState _state = new CtfState(); private string _status = "Waiting for a two-team match..."; private float _nextDiscovery; private float _nextRuleTick; private float _nextSnapshot; private float _nextCompatibilityCheck; private float _nextRespawnAudit; private EndStage _endStage = EndStage.None; private float _endStageAt; private int _endingTeam = -1; private int _baselineScoreA; private int _baselineScoreB; private bool _scoresMirrored; private string _remoteSession = ""; private int _remoteSequence = -1; internal CtfState State => _state; internal string Status => _status; internal bool Active => _config.Enabled.Value && _state.Active; internal bool Ending => _state.Active && _state.Ending; internal bool IsAuthority => CtfGame.IsServer && CtfNetwork.IsLobbyHost; internal CtfController(CtfPlugin plugin, CtfConfig config) { _plugin = plugin; _config = config; } internal void ResetForScene(string reason) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) RestoreTakeScores(); CtfSpawns.Reset(); _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); CtfState ctfState = new CtfState(); Scene activeScene = SceneManager.GetActiveScene(); ctfState.SceneName = ((Scene)(ref activeScene)).name; _state = ctfState; _remoteSession = ""; _remoteSequence = -1; _endStage = EndStage.None; _endingTeam = -1; _nextDiscovery = Time.unscaledTime + 1f; _status = reason; _plugin.OnStateApplied(); } internal void OnLobbyLeft() { if (_state.Active) { Deactivate("Left the lobby.", broadcast: false); } } internal void Tick() { float unscaledTime = Time.unscaledTime; if (!_config.Enabled.Value) { if (_state.Active) { Deactivate("Capture the Flag is switched off in the config.", broadcast: true); } return; } if (IsAuthority) { TickEnding(unscaledTime); } if (!CtfGame.IsPlayableScene) { string text = (CtfGame.IsExplorationMap ? "Not available in the exploration map." : "Waiting for a playable match..."); if (_state.Active && !_state.Ending && IsAuthority) { Deactivate(text, broadcast: true); } else if (!_state.Active) { _status = text; } } else if (!IsAuthority) { if (!_state.Active) { _status = "Waiting for the host to start Capture the Flag..."; } else { TickClientPrediction(); } } else { if (_state.Active && _state.Ending) { return; } TickRespawns(unscaledTime); if (!_state.Active) { if (unscaledTime >= _nextDiscovery) { _nextDiscovery = unscaledTime + 1f; TryStartHostState(); } } else if (ValidateRunningMatch(unscaledTime)) { if (unscaledTime >= _nextRuleTick) { _nextRuleTick = unscaledTime + _config.RuleTickInterval.Value; TickRules(unscaledTime); } if (unscaledTime >= _nextSnapshot) { _nextSnapshot = unscaledTime + _config.SnapshotInterval.Value; Broadcast(); } } } } private void TickClientPrediction() { float unscaledDeltaTime = Time.unscaledDeltaTime; CountDown(_state.FlagA, unscaledDeltaTime); CountDown(_state.FlagB, unscaledDeltaTime); } private static void CountDown(FlagState flag, float delta) { if (flag != null && flag.IsDropped && !(flag.ReturnIn <= 0f)) { flag.ReturnIn = Mathf.Max(0f, flag.ReturnIn - delta); } } private bool ValidateRunningMatch(float now) { if (!CtfGame.TryGetExactlyTwoTeams(out var teamA, out var teamB) || teamA != _state.TeamA || teamB != _state.TeamB) { Deactivate("Capture the Flag paused: the lobby no longer has these same two teams.", broadcast: true); return false; } if (CtfGame.CountPlayersOnTeam(teamA) == 0 || CtfGame.CountPlayersOnTeam(teamB) == 0) { Deactivate("Capture the Flag paused: a team has no players left.", broadcast: true); return false; } if (now >= _nextCompatibilityCheck) { _nextCompatibilityCheck = now + 2f; if (CtfNetwork.Instance != null && !CtfNetwork.Instance.AllPlayersCompatible(out var reason)) { Deactivate(reason, broadcast: true); return false; } } return true; } private void TryStartHostState() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) if (CtfNetwork.Instance != null && !CtfNetwork.Instance.AllPlayersCompatible(out var reason)) { _status = reason; return; } if (!CtfGame.TryGetExactlyTwoTeams(out var teamA, out var teamB)) { _status = "Capture the Flag needs exactly two teams with players on them."; return; } if (CtfGame.GetConnectedPlayerIds().Count < 2) { _status = "Capture the Flag needs at least two players."; return; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; string source; if (_config.TryGetMapBases(name, out var baseA, out var baseB)) { source = "this map's saved bases"; } else { if (!CtfGame.TryDiscoverBases(_config.AutoBaseHeightOffset.Value, out baseA, out baseB, out source)) { _status = "No usable base positions found on this map. Set them with the " + ((object)_config.SetBaseAKey.Value/*cast due to .constrained prefix*/).ToString() + " and " + ((object)_config.SetBaseBKey.Value/*cast due to .constrained prefix*/).ToString() + " hotkeys."; return; } CtfGame.OrientBasesTowardTeams(teamA, teamB, ref baseA, ref baseB); } float num = CtfGame.HorizontalDistance(baseA, baseB); float num2 = Mathf.Max(_config.MinimumBaseSeparation.Value, _config.PickupRadius.Value + _config.CaptureRadius.Value + 0.5f); if (num < num2) { _status = "The two bases are only " + num.ToString("0.0") + "m apart; " + num2.ToString("0.0") + "m is needed. Place them by hand with " + ((object)_config.SetBaseAKey.Value/*cast due to .constrained prefix*/).ToString() + " and " + ((object)_config.SetBaseBKey.Value/*cast due to .constrained prefix*/).ToString() + "."; return; } int capturesToWin = _config.ResolveCapturesToWin(CtfGame.LobbyTakesToWin); _state = new CtfState { SessionId = Guid.NewGuid().ToString("N"), Sequence = 1, SceneName = name, Active = true, Ending = false, TeamA = teamA, TeamB = teamB, CapturesToWin = capturesToWin, BaseA = baseA, BaseB = baseB, FlagA = new FlagState { Status = FlagStatus.Home, CarrierPlayerId = -1, Position = baseA }, FlagB = new FlagState { Status = FlagStatus.Home, CarrierPlayerId = -1, Position = baseB } }; _endStage = EndStage.None; _endingTeam = -1; _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); _nextRuleTick = Time.unscaledTime; _nextSnapshot = Time.unscaledTime; _nextCompatibilityCheck = Time.unscaledTime + 2f; _nextRespawnAudit = Time.unscaledTime + 4f; _status = "Capture the Flag is live. Bases from " + source + "."; CaptureScoreBaseline(); CtfGame.RepopulateAlivePlayers(); CtfLog.Info("CTF started on " + name + ": teams " + teamA + " vs " + teamB + ", first to " + capturesToWin + ", bases from " + source + " (" + num.ToString("0.0") + "m apart)."); _plugin.OnStateApplied(); Broadcast(); Announce(5, "CAPTURE THE FLAG - FIRST TO " + capturesToWin, "Capture the Flag: first team to " + capturesToWin + " captures wins the round."); } private void TickRules(float now) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) bool flag = false; flag |= MaintainFlag(_state.FlagA, _state.BaseA, _state.TeamA, now); flag |= MaintainFlag(_state.FlagB, _state.BaseB, _state.TeamB, now); foreach (int connectedPlayerId in CtfGame.GetConnectedPlayerIds()) { int team = CtfGame.GetTeam(connectedPlayerId); if (!_state.IsPlayingTeam(team) || !CtfGame.HasLivingBody(connectedPlayerId) || CtfGame.IsFrozen(connectedPlayerId) || !CtfGame.TryGetPosition(connectedPlayerId, out var position)) { continue; } Vector3 value; Vector3 val = (_lastPosition.TryGetValue(connectedPlayerId, out value) ? value : position); _lastPosition[connectedPlayerId] = position; FlagState flagState = _state.FlagForTeam(team); FlagState flagState2 = _state.FlagForTeam(_state.OtherTeam(team)); Vector3 val2 = _state.BaseForTeam(team); if (flagState == null || flagState2 == null) { continue; } if (flagState.IsDropped && InZone(val, position, flagState.Position, _config.PickupRadius.Value)) { SendHome(flagState, val2); Announce(3, CtfGame.PlayerName(connectedPlayerId).ToUpperInvariant() + " RETURNED THE FLAG", FormatLog(connectedPlayerId, "returned their flag")); flag = true; } if (_state.TeamCarrying(connectedPlayerId) < 0 && !flagState2.IsCarried && InZone(val, position, flagState2.Position, _config.PickupRadius.Value)) { flagState2.Status = FlagStatus.Carried; flagState2.CarrierPlayerId = connectedPlayerId; flagState2.Position = position; flagState2.ReturnAt = 0f; flagState2.ReturnIn = 0f; Announce(1, CtfGame.PlayerName(connectedPlayerId).ToUpperInvariant() + " HAS THE FLAG", FormatLog(connectedPlayerId, "took the enemy flag")); flag = true; } if (flagState2.IsCarried && flagState2.CarrierPlayerId == connectedPlayerId && InZone(val, position, val2, _config.CaptureRadius.Value) && (!_config.RequireOwnFlagHome.Value || flagState.IsHome)) { Capture(connectedPlayerId, team, flagState2); flag = true; if (_state.Ending) { break; } } } if (flag) { StateChanged(); } } private bool MaintainFlag(FlagState flag, Vector3 home, int owningTeam, float now) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) if (flag.IsHome) { flag.Position = home; flag.CarrierPlayerId = -1; flag.ReturnIn = 0f; return false; } if (flag.IsDropped) { float num = Mathf.Min(_state.BaseA.y, _state.BaseB.y); if (flag.Position.y < num - _config.VoidDepth.Value) { SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG WAS LOST AND RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag fell out of the map and returned home."); return true; } if (now < flag.ReturnAt) { flag.ReturnIn = Mathf.Max(0f, flag.ReturnAt - now); return false; } SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag returned home."); return true; } int carrierPlayerId = flag.CarrierPlayerId; if (carrierPlayerId < 0 || !CtfGame.IsConnected(carrierPlayerId)) { SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag returned home after its carrier left."); return true; } int team = CtfGame.GetTeam(carrierPlayerId); if (team == owningTeam || !_state.IsPlayingTeam(team)) { SendHome(flag, home); Announce(3, TeamLabel(owningTeam) + "'S FLAG RETURNED", TeamLabel(owningTeam, upperCase: false) + "'s flag returned home after its carrier changed team."); return true; } if (!CtfGame.HasLivingBody(carrierPlayerId)) { Drop(flag, carrierPlayerId, now); return true; } if (CtfGame.TryGetCarryPoint(carrierPlayerId, out var position)) { flag.Position = position; } flag.ReturnIn = 0f; return false; } private void Capture(int playerId, int team, FlagState capturedFlag) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) SendHome(capturedFlag, _state.BaseForTeam(_state.OtherTeam(team))); if (team == _state.TeamA) { _state.ScoreA++; } else { _state.ScoreB++; } int num = _state.ScoreForTeam(team); MirrorTakeScores(); Announce(4, CtfGame.PlayerName(playerId).ToUpperInvariant() + " SCORED " + num + " / " + _state.CapturesToWin, FormatLog(playerId, "captured the flag (" + num + "/" + _state.CapturesToWin + ")")); if (num >= _state.CapturesToWin) { _state.Ending = true; _endingTeam = team; _endStage = EndStage.AwardPending; _endStageAt = Time.unscaledTime + 1.5f; _status = TeamLabel(team, upperCase: false) + " won the Capture the Flag round."; SendHome(_state.FlagA, _state.BaseA); SendHome(_state.FlagB, _state.BaseB); _respawnAt.Clear(); _unfreezeAt.Clear(); Announce(6, TeamLabel(team) + " WINS THE ROUND", null); } } private void TickEnding(float now) { if (_endStage == EndStage.None || now < _endStageAt) { return; } if (_endStage == EndStage.Done) { _endStage = EndStage.None; if (_state.Active) { Deactivate("Capture the Flag finished, but the next map did not load. Handing back to the game's normal rules.", broadcast: true); } } else if (!IsAuthority || !_state.Active) { _endStage = EndStage.None; } else if (_endStage == EndStage.AwardPending) { if (CtfGame.AwardRoundWin(_endingTeam)) { _scoresMirrored = false; _endStage = EndStage.AdvancePending; _endStageAt = now + 4f; } else { _endStage = EndStage.None; Deactivate("Capture the Flag could not end the round. Check the BepInEx log.", broadcast: true); } } else { _endStage = EndStage.Done; _endStageAt = now + 20f; CtfGame.AdvanceToNextRound(); } } internal void OnPlayerDied(int playerId) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (IsAuthority && Active && !_state.Ending) { if (CtfGame.TryGetPosition(playerId, out var position)) { CtfSpawns.RecordDeath(playerId, position); } float unscaledTime = Time.unscaledTime; bool flag = false; if (_state.FlagA.IsCarried && _state.FlagA.CarrierPlayerId == playerId) { Drop(_state.FlagA, playerId, unscaledTime); flag = true; } if (_state.FlagB.IsCarried && _state.FlagB.CarrierPlayerId == playerId) { Drop(_state.FlagB, playerId, unscaledTime); flag = true; } if (flag) { StateChanged(); } } } internal void ScheduleRespawn(int playerId) { if (IsAuthority && Active && !_state.Ending && !_respawnAt.ContainsKey(playerId)) { _respawnAt[playerId] = Time.unscaledTime + _config.RespawnDelay.Value; } } private void TickRespawns(float now) { if (Active && !_state.Ending && now >= _nextRespawnAudit) { _nextRespawnAudit = now + 1f; AuditForMissingPlayers(now); } if (_respawnAt.Count > 0) { _dueBuffer.Clear(); foreach (KeyValuePair 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 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 stillConnected) { if (_bodyId.Count == 0 && _missingSince.Count == 0) { return; } _auditDeparted.Clear(); foreach (int key in _bodyId.Keys) { if (!stillConnected.Contains(key)) { _auditDeparted.Add(key); } } foreach (int key2 in _missingSince.Keys) { if (!stillConnected.Contains(key2) && !_auditDeparted.Contains(key2)) { _auditDeparted.Add(key2); } } foreach (int item in _auditDeparted) { _bodyId.Remove(item); _bodySince.Remove(item); _missingSince.Remove(item); } } internal bool TrySetHostBase(bool baseA) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) if (!IsAuthority) { _plugin.OnCtfEvent(0, "ONLY THE HOST CAN PLACE CTF BASES"); return false; } if (!CtfGame.TryGetLocalPosition(out var position)) { _plugin.OnCtfEvent(0, "STAND SOMEWHERE IN THE MAP FIRST"); return false; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!_config.TryGetMapBases(name, out var _, out var _) && _state.Active) { _config.SetMapBase(name, !baseA, baseA ? _state.BaseB : _state.BaseA); } _config.SetMapBase(name, baseA, position); string text = (baseA ? "A" : "B"); CtfLog.Info("Saved base " + text + " for " + name + " at " + CtfProtocol.FormatVector(position) + "."); _plugin.OnCtfEvent(0, "SAVED BASE " + text + " FOR " + name.ToUpperInvariant()); RestartHostState("Reloading this map's saved bases..."); return true; } internal void ClearHostBases() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!IsAuthority) { _plugin.OnCtfEvent(0, "ONLY THE HOST CAN PLACE CTF BASES"); return; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; _config.ClearMapBases(name); CtfLog.Info("Cleared the saved bases for " + name + "; falling back to automatic placement."); _plugin.OnCtfEvent(0, "CLEARED SAVED BASES FOR " + name.ToUpperInvariant()); RestartHostState("Returning to automatic base placement..."); } private void RestartHostState(string reason) { Deactivate(reason, broadcast: true); _nextDiscovery = 0f; TryStartHostState(); } internal void ApplyRemoteSnapshot(string payload) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (IsAuthority) { return; } if (!CtfProtocol.TryDeserialize(payload, out var state, out var error)) { CtfLog.Warning("Rejected a CTF snapshot: " + error); return; } string sceneName = state.SceneName; Scene activeScene = SceneManager.GetActiveScene(); if (string.Equals(sceneName, ((Scene)(ref activeScene)).name, StringComparison.Ordinal) && (!(state.SessionId == _remoteSession) || state.Sequence >= _remoteSequence)) { _remoteSession = state.SessionId; _remoteSequence = state.Sequence; _state = state; _status = (state.Active ? "Capture the Flag is live." : "The host has paused Capture the Flag."); _plugin.OnStateApplied(); } } internal void ApplyRemoteEvent(string sessionId, int kind, string message) { if (!IsAuthority && CtfEventKind.IsValid(kind) && (string.IsNullOrEmpty(_remoteSession) || string.IsNullOrEmpty(sessionId) || string.Equals(sessionId, _remoteSession, StringComparison.Ordinal)) && CtfProtocol.TrySanitiseAnnouncement(message, out var sanitised)) { _plugin.OnCtfEvent(kind, sanitised); } } private void Drop(FlagState flag, int carrierId, float now) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Vector3 position = DropPosition(carrierId, flag); flag.Status = FlagStatus.Dropped; flag.CarrierPlayerId = -1; flag.Position = position; flag.ReturnAt = now + _config.DroppedReturnSeconds.Value; flag.ReturnIn = _config.DroppedReturnSeconds.Value; Announce(2, CtfGame.PlayerName(carrierId).ToUpperInvariant() + " DROPPED THE FLAG", FormatLog(carrierId, "dropped the flag")); } private static Vector3 DropPosition(int carrierId, FlagState flag) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (!CtfGame.TryGetPosition(carrierId, out var position)) { return flag.Position; } try { Vector3 val = position + Vector3.up * 1.2f; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val, Vector3.down, ref val2, 6f, -65601, (QueryTriggerInteraction)1)) { return ((RaycastHit)(ref val2)).point + Vector3.up * 0.15f; } } catch { } return position + Vector3.up * 0.15f; } private static void SendHome(FlagState flag, Vector3 home) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) flag.Status = FlagStatus.Home; flag.CarrierPlayerId = -1; flag.Position = home; flag.ReturnAt = 0f; flag.ReturnIn = 0f; } private static bool InZone(Vector3 point, Vector3 centre, float radius) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(2.2f, radius); if (Mathf.Abs(point.y - centre.y) > num) { return false; } return CtfGame.HorizontalDistance(point, centre) <= radius; } private static bool InZone(Vector3 from, Vector3 to, Vector3 centre, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (InZone(to, centre, radius)) { return true; } Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.35f || magnitude > 6f) { return false; } int num = Mathf.CeilToInt(magnitude / 0.35f); for (int i = 1; i < num; i++) { if (InZone(from + val * ((float)i / (float)num), centre, radius)) { return true; } } return false; } private void StateChanged() { _state.Sequence++; _plugin.OnStateApplied(); Broadcast(); } private void Broadcast() { if (IsAuthority) { CtfNetwork.Instance?.BroadcastSnapshot(_state); } } private void Announce(int kind, string headline, string matchLogLine) { _plugin.OnCtfEvent(kind, headline); CtfNetwork.Instance?.BroadcastEvent(_state.SessionId, kind, headline); if (_config.WriteMatchLog.Value && !string.IsNullOrEmpty(matchLogLine)) { CtfGame.WriteMatchLog(matchLogLine); } CtfLog.Debug("CTF event " + kind + ": " + headline); } private static string FormatLog(int playerId, string what) { return CtfGame.NameTag(playerId) + " " + what; } private string TeamLabel(int teamId, bool upperCase = true) { string text = ((teamId == _state.TeamA) ? "team 1" : ((teamId == _state.TeamB) ? "team 2" : "a team")); return upperCase ? text.ToUpperInvariant() : text; } private void Deactivate(string reason, bool broadcast) { bool active = _state.Active; RestoreTakeScores(); _respawnAt.Clear(); _unfreezeAt.Clear(); _lastPosition.Clear(); _bodyId.Clear(); _bodySince.Clear(); _missingSince.Clear(); _endStage = EndStage.None; _endingTeam = -1; if (active) { _state.Active = false; _state.Ending = false; _state.Sequence++; if (broadcast) { Broadcast(); } if (IsAuthority) { CtfGame.ClearWaitForDrawHandle(); CtfGame.RepopulateAlivePlayers(); } } _status = reason; _nextDiscovery = Time.unscaledTime + 2f; _plugin.OnStateApplied(); if (active) { CtfLog.Info("Capture the Flag stopped: " + reason); } else { CtfLog.Debug("CTF stayed inactive: " + reason); } } private void CaptureScoreBaseline() { _baselineScoreA = 0; _baselineScoreB = 0; _scoresMirrored = false; try { if (!((Object)(object)ScoreManager.Instance == (Object)null)) { _baselineScoreA = ScoreManager.Instance.GetRoundScore(_state.TeamA); _baselineScoreB = ScoreManager.Instance.GetRoundScore(_state.TeamB); } } catch { } } private void MirrorTakeScores() { if (IsAuthority) { _scoresMirrored = true; CtfGame.SyncTakeScores(_state.TeamA, _baselineScoreA + _state.ScoreA, _state.TeamB, _baselineScoreB + _state.ScoreB); } } private void RestoreTakeScores() { if (_scoresMirrored) { _scoresMirrored = false; if (IsAuthority) { CtfGame.SyncTakeScores(_state.TeamA, _baselineScoreA, _state.TeamB, _baselineScoreB); } } } } internal static class CtfGame { private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo _roundWon; private static MethodInfo _updateMatchPointsHud; private static MethodInfo _respawnLogic; private static MethodInfo _setPlayerMoveLogic; private static FieldInfo _waitForDrawCoroutine; private static FieldInfo _roundHasStarted; private static FieldInfo _currentSpawnPoints; private static bool _resolved; private static float _nextLatchReport; internal static bool IsServer { get { try { return (Object)(object)InstanceFinder.NetworkManager != (Object)null && InstanceFinder.NetworkManager.IsServer; } catch { return false; } } } internal static bool IsPlayableScene { get { try { if ((Object)(object)GameManager.Instance == (Object)null || (Object)(object)ScoreManager.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } if ((Object)(object)PauseManager.Instance == (Object)null) { return false; } if (PauseManager.Instance.inMainMenu || PauseManager.Instance.inVictoryMenu) { return false; } if (PauseManager.Instance.onEndRoundScreen) { return false; } if (IsExplorationMap) { return false; } return true; } catch { return false; } } } internal static bool IsExplorationMap { get { try { if ((Object)(object)MapsManager.Instance != (Object)null && MapsManager.Instance.inExplorationMap) { return true; } return (Object)(object)SceneMotor.Instance != (Object)null && SceneMotor.Instance.testMap; } catch { return false; } } } internal static int LobbyTakesToWin { get { try { return ((Object)(object)ScoreManager.Instance != (Object)null) ? ScoreManager.Instance.RoundScoreRequiredToWin : 3; } catch { return 3; } } } internal static int LocalPlayerId { get { try { return ((Object)(object)ClientInstance.Instance != (Object)null) ? ClientInstance.Instance.PlayerId : (-1); } catch { return -1; } } } internal static FirstPersonController LocalPlayer { get { try { int localPlayerId = LocalPlayerId; PlayerManager manager; FirstPersonController player; return (localPlayerId >= 0 && TryGetPlayer(localPlayerId, out manager, out player)) ? player : null; } catch { return null; } } } internal static int RespawningPlayerId { get; private set; } = -1; internal static bool ResolveReflection() { if (_resolved) { return true; } bool ok = true; _roundWon = Require(typeof(GameManager), "RoundWon", ref ok, (MethodInfo m) => m.GetParameters().Length == 1); _waitForDrawCoroutine = RequireField(typeof(GameManager), "waitForDrawCoroutine", ref ok); _respawnLogic = Require(typeof(PlayerManager), null, ref ok, (MethodInfo m) => m.Name.StartsWith("RpcLogic___CmdRespawn_", StringComparison.Ordinal) && m.GetParameters().Length == 0, "PlayerManager.RpcLogic___CmdRespawn_*"); _setPlayerMoveLogic = Require(typeof(PlayerManager), null, ref ok, (MethodInfo m) => m.Name.StartsWith("RpcLogic___SetPlayerMove_", StringComparison.Ordinal) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool), "PlayerManager.RpcLogic___SetPlayerMove_*"); _updateMatchPointsHud = Optional(typeof(GameManager), "UpdateMatchPointsHUD"); _roundHasStarted = typeof(FirstPersonController).GetField("roundHasStarted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_roundHasStarted == null) { CtfLog.Warning("FirstPersonController.roundHasStarted was not found; respawned players may not trigger teleporters or void kills."); } _currentSpawnPoints = typeof(PlayerManager).GetField("CurrentSpawnPoints", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_currentSpawnPoints == null) { CtfLog.Debug("PlayerManager.CurrentSpawnPoints was not found; respawn placement will always consider every spawn point in the map."); } _resolved = ok; return ok; } internal static List GetConnectedPlayerIds() { try { List list = new List(); foreach (KeyValuePair playerInstance in ClientInstance.playerInstances) { if ((Object)(object)playerInstance.Value != (Object)null) { list.Add(playerInstance.Key); } } list.Sort(); return list; } catch { return new List(); } } 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 hashSet = new HashSet(); 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 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()); 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(); if ((Object)(object)val == (Object)null) { val = ((Component)player).GetComponentInChildren(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 list = new List(); try { SpawnPoint[] array = Object.FindObjectsOfType(true); foreach (SpawnPoint val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null)) { Vector3 position = ((Component)val).transform.position; if (IsFinite(position)) { list.Add(position); } } } } catch (Exception ex) { CtfLog.Debug("Spawn point discovery failed: " + ex.Message); } if (list.Count >= 2) { source = "the map's spawn points"; } else { foreach (int connectedPlayerId in GetConnectedPlayerIds()) { if (TryGetPosition(connectedPlayerId, out var position2)) { list.Add(position2); } } source = "player positions"; } if (list.Count < 2) { return false; } list = (from p in list orderby p.x, p.z, p.y select p).ToList(); float num = -1f; int num2 = -1; int index = -1; for (int num3 = 0; num3 < list.Count - 1; num3++) { for (int num4 = num3 + 1; num4 < list.Count; num4++) { float num5 = list[num3].x - list[num4].x; float num6 = list[num3].z - list[num4].z; float num7 = num5 * num5 + num6 * num6; if (!(num7 <= num)) { num = num7; num2 = num3; index = num4; } } } if (num2 < 0) { return false; } baseA = list[num2] + Vector3.up * heightOffset; baseB = list[index] + Vector3.up * heightOffset; return true; } internal static float HorizontalDistance(Vector3 a, Vector3 b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } internal static void OrientBasesTowardTeams(int teamA, int teamB, ref Vector3 baseA, ref Vector3 baseB) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (TryTeamCentroid(teamA, out var centroid) && TryTeamCentroid(teamB, out var centroid2)) { float num = HorizontalDistance(baseA, centroid) + HorizontalDistance(baseB, centroid2); float num2 = HorizontalDistance(baseB, centroid) + HorizontalDistance(baseA, centroid2); if (!(num2 >= num)) { Vector3 val = baseA; baseA = baseB; baseB = val; } } } private static bool TryTeamCentroid(int team, out Vector3 centroid) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) centroid = Vector3.zero; int num = 0; foreach (int connectedPlayerId in GetConnectedPlayerIds()) { if (GetTeam(connectedPlayerId) == team && TryGetPosition(connectedPlayerId, out var position)) { centroid += position; num++; } } if (num == 0) { return false; } centroid /= (float)num; return true; } internal static bool TryRespawn(int playerId) { if (_respawnLogic == null) { return false; } if (!TryGetManager(playerId, out var manager)) { return false; } int hostSuit; int hostCig; bool flag = TryWearPlayerCosmetics(manager, out hostSuit, out hostCig); RespawningPlayerId = playerId; try { _respawnLogic.Invoke(manager, null); return true; } catch (Exception ex) { CtfLog.Error("Could not respawn " + PlayerName(playerId) + ": " + ex); return false; } finally { RespawningPlayerId = -1; if (flag) { RestoreCosmetics(hostSuit, hostCig); } } } internal static void CollectAssignedSpawnPoints(int playerId, List into) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) into.Clear(); if (_currentSpawnPoints == null) { return; } try { if (!TryGetManager(playerId, out var manager) || !(_currentSpawnPoints.GetValue(manager) is SpawnPoint[] array)) { return; } SpawnPoint[] array2 = array; foreach (SpawnPoint val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null) && ((Component)val).gameObject.activeInHierarchy && IsFinite(((Component)val).transform.position)) { into.Add(val); } } } catch (Exception ex) { CtfLog.Debug("Could not read the game's assigned spawn points: " + ex.Message); into.Clear(); } } private static bool TryWearPlayerCosmetics(PlayerManager manager, out int hostSuit, out int hostCig) { hostSuit = 0; hostCig = 0; try { CosmeticsManager instance = CosmeticsManager.Instance; if ((Object)(object)instance == (Object)null) { return false; } PlayerSetup val = (((Object)(object)manager.player != (Object)null) ? manager.player.setupScript : null); if ((Object)(object)val == (Object)null) { return false; } hostSuit = instance.currentsuitIndex; hostCig = instance.currentcigIndex; if (hostSuit == val.mat && hostCig == val.cig) { return false; } instance.currentsuitIndex = val.mat; instance.currentcigIndex = val.cig; return true; } catch (Exception ex) { CtfLog.Debug("Could not preserve respawn cosmetics: " + ex.Message); return false; } } private static void RestoreCosmetics(int suit, int cig) { try { CosmeticsManager instance = CosmeticsManager.Instance; if (!((Object)(object)instance == (Object)null)) { instance.currentsuitIndex = suit; instance.currentcigIndex = cig; } } catch (Exception ex) { CtfLog.Error("Could not restore the host's cosmetics: " + ex); } } internal static void ReleaseRespawnFreeze(int playerId) { if (!TryGetManager(playerId, out var manager) || (Object)(object)manager.player == (Object)null) { return; } try { _setPlayerMoveLogic?.Invoke(manager, new object[1] { true }); } catch (Exception ex) { CtfLog.Debug("Could not release the respawn freeze: " + ex.Message); } } internal static void ClearStartCountdown() { if (!CtfPatches.RoundStartedThisScene) { return; } try { GameManager instance = GameManager.Instance; if (instance != null) { instance.SetStartTime(0f); } } catch (Exception ex) { CtfLog.Debug("Could not clear the start countdown: " + ex.Message); } } internal static void EnforceRoundIsLive() { if (!CtfPatches.RoundStartedThisScene) { return; } try { if (PauseManager.BetweenRounds) { PauseManager.BetweenRounds = false; float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextLatchReport)) { _nextLatchReport = unscaledTime + 5f; CtfLog.Debug("Cleared a between-rounds latch during a live CTF round; left set it would have disarmed every explosive on this machine."); } } } catch { } } internal static void MarkAlive(int playerId) { try { GameManager.Instance?.alivePlayers.Add(playerId); } catch { } } internal static void RepopulateAlivePlayers() { try { if ((Object)(object)GameManager.Instance == (Object)null) { return; } GameManager.Instance.alivePlayers.Clear(); foreach (int connectedPlayerId in GetConnectedPlayerIds()) { GameManager.Instance.alivePlayers.Add(connectedPlayerId); } } catch { } } internal static void ClearWaitForDrawHandle() { try { _waitForDrawCoroutine?.SetValue(GameManager.Instance, null); } catch { } } internal static void StopWaitForDraw() { try { GameManager instance = GameManager.Instance; if (!((Object)(object)instance == (Object)null) && !(_waitForDrawCoroutine == null)) { object? value = _waitForDrawCoroutine.GetValue(instance); Coroutine val = (Coroutine)((value is Coroutine) ? value : null); if (val != null) { ((MonoBehaviour)instance).StopCoroutine(val); } _waitForDrawCoroutine.SetValue(instance, null); } } catch (Exception ex) { CtfLog.Debug("Could not stop the draw coroutine: " + ex.Message); } } internal static void SyncTakeScores(int teamA, int scoreA, int teamB, int scoreB) { try { ScoreManager instance = ScoreManager.Instance; if (!((Object)(object)instance == (Object)null)) { SetTakeScore(instance, teamA, scoreA); SetTakeScore(instance, teamB, scoreB); PushMatchPointsHud(-1); } } catch (Exception ex) { CtfLog.Debug("Could not mirror CTF scores: " + ex.Message); } } private static void SetTakeScore(ScoreManager scores, int teamId, int value) { if (teamId >= 0) { int roundScore = scores.GetRoundScore(teamId); if (roundScore != value) { scores.AddRoundScore(teamId, value - roundScore); } } } internal static void PushMatchPointsHud(int winningTeamId) { if (_updateMatchPointsHud == null || (Object)(object)GameManager.Instance == (Object)null || (Object)(object)ScoreManager.Instance == (Object)null) { return; } try { Dictionary roundScoreDictionary = ScoreManager.Instance.GetRoundScoreDictionary(); _updateMatchPointsHud.Invoke(GameManager.Instance, new object[2] { winningTeamId, roundScoreDictionary }); } catch (Exception ex) { CtfLog.Debug("Could not refresh the match points HUD: " + ex.Message); } } internal static bool AwardRoundWin(int winningTeamId) { if (_roundWon == null) { CtfLog.Error("Cannot award the CTF round win: GameManager.RoundWon is missing."); return false; } try { PushMatchPointsHud(winningTeamId); _roundWon.Invoke(GameManager.Instance, new object[1] { winningTeamId }); RoundManager.Instance.CmdEndRound(winningTeamId); CtfLog.Info("Team " + winningTeamId + " won the CTF round; handed off to STRAFTAT's round-end flow."); return true; } catch (Exception ex) { CtfLog.Error("Could not finish the CTF round: " + ex); return false; } } internal static void AdvanceToNextRound() { try { RepopulateAlivePlayers(); SceneMotor instance = SceneMotor.Instance; if (instance != null) { instance.ChangeNetworkScene(); } } catch (Exception ex) { CtfLog.Error("Could not advance to the next round: " + ex); } } internal static void WriteMatchLog(string text) { if (string.IsNullOrEmpty(text)) { return; } try { PauseManager instance = PauseManager.Instance; if (instance != null) { instance.WriteLog(text); } } catch (Exception ex) { CtfLog.Debug("Could not write to the match log: " + ex.Message); } } internal static string NameTag(int playerId) { try { if (ClientInstance.playerInstances.TryGetValue(playerId, out var value) && (Object)(object)value != (Object)null) { return value.PlayerNameTag; } } catch { } return PlayerName(playerId); } internal static void EnsureRoundStartedFlag(FirstPersonController player) { if (_roundHasStarted == null || (Object)(object)player == (Object)null) { return; } try { if (!(bool)_roundHasStarted.GetValue(player)) { _roundHasStarted.SetValue(player, true); } } catch { } } internal static bool IsFinite(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) return !float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z) && !float.IsInfinity(v.x) && !float.IsInfinity(v.y) && !float.IsInfinity(v.z); } private static bool TryGetManager(int playerId, out PlayerManager manager) { manager = null; try { if (!ClientInstance.playerInstances.TryGetValue(playerId, out var value) || (Object)(object)value == (Object)null) { return false; } manager = (((Object)(object)value.PlayerSpawner != (Object)null) ? value.PlayerSpawner : ((Component)value).GetComponent()); return (Object)(object)manager != (Object)null; } catch { return false; } } private static MethodInfo Require(Type type, string name, ref bool ok, Func predicate = null, string label = null) { MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => (name == null || m.Name == name) && (predicate == null || predicate(m))); if (methodInfo != null) { return methodInfo; } ok = false; CtfLog.Error("Missing required game member: " + (label ?? (type.Name + "." + name)) + ". This STRAFTAT version is not supported; Capture the Flag will stay inactive."); return null; } private static FieldInfo RequireField(Type type, string name, ref bool ok) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } ok = false; CtfLog.Error("Missing required game field: " + type.Name + "." + name + ". This STRAFTAT version is not supported; Capture the Flag will stay inactive."); return null; } private static MethodInfo Optional(Type type, string name) { MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { CtfLog.Debug("Optional game member not found: " + type.Name + "." + name); } return method; } } internal sealed class CtfHud { private sealed class CtfMarker { private const float EdgeInset = 90f; private readonly TextMeshProUGUI _label; private readonly RectTransform _rect; internal CtfMarker(CtfHud hud, string name, float fontSize) { _label = hud.CreateMarkerLabel(name, fontSize); _rect = ((TMP_Text)_label).rectTransform; } internal void Hide() { if ((Object)(object)_label != (Object)null && ((Component)_label).gameObject.activeSelf) { ((Component)_label).gameObject.SetActive(false); } } internal void Show(Camera camera, bool visible, Vector3 worldPosition, string caption, Color color) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0217: 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) //IL_0153: 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_label == (Object)null) { return; } if (!visible || (Object)(object)camera == (Object)null) { Hide(); return; } Vector3 val = camera.WorldToScreenPoint(worldPosition + Vector3.up * 1.2f); bool flag = val.z <= 0f; if (flag) { ((Vector3)(ref val))..ctor((float)Screen.width - val.x, (float)Screen.height - val.y, 0f - val.z); } float num = (float)Screen.width * 0.5f; float num2 = (float)Screen.height * 0.5f; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(val.x - num, val.y - num2); bool flag2 = flag || val.x < 90f || val.x > (float)Screen.width - 90f || val.y < 90f || val.y > (float)Screen.height - 90f; if (flag2) { float num3 = num - 90f; float num4 = num2 - 90f; if (((Vector2)(ref val2)).sqrMagnitude < 0.01f) { ((Vector2)(ref val2))..ctor(0f, num4); } float num5 = Mathf.Min(num3 / Mathf.Max(1f, Mathf.Abs(val2.x)), num4 / Mathf.Max(1f, Mathf.Abs(val2.y))); val2 *= num5; } if (!((Component)_label).gameObject.activeSelf) { ((Component)_label).gameObject.SetActive(true); } float num6 = 1920f / Mathf.Max(1f, (float)Screen.width); float num7 = 1080f / Mathf.Max(1f, (float)Screen.height); _rect.anchoredPosition = new Vector2(val2.x * num6, val2.y * num7); float num8 = Vector3.Distance(((Component)camera).transform.position, worldPosition); string text = (flag2 ? (Arrow(val2) + " ") : ""); ((TMP_Text)_label).text = text + caption + " " + Mathf.RoundToInt(num8) + "m"; ((Graphic)_label).color = color; ((TMP_Text)_label).alpha = (flag2 ? 0.95f : 0.7f); } private static string Arrow(Vector2 direction) { //IL_0001: 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_0039: 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) if (Mathf.Abs(direction.x) > Mathf.Abs(direction.y)) { return (direction.x > 0f) ? ">" : "<"; } return (direction.y > 0f) ? "^" : "v"; } } private const float ReferenceWidth = 1920f; private const float ReferenceHeight = 1080f; private const float AnnouncementSeconds = 3.4f; private readonly CtfConfig _config; private GameObject _root; private Canvas _canvas; private TMP_FontAsset _font; private TextMeshProUGUI _score; private TextMeshProUGUI _target; private TextMeshProUGUI _flagStatus; private TextMeshProUGUI _objective; private TextMeshProUGUI _announcement; private TextMeshProUGUI _status; private TextMeshProUGUI _hostHint; private RectTransform _announcementRect; private CtfMarker _enemyFlagMarker; private CtfMarker _ownFlagMarker; private CtfMarker _homeBaseMarker; private string _announcementText = ""; private float _announcementUntil; private float _announcementStarted; internal CtfHud(CtfConfig config) { _config = config; } internal void OnSceneChanged() { _announcementUntil = 0f; } internal void OnRuntimeLost() { _root = null; _canvas = null; } internal void OnStateApplied(CtfController controller) { } internal void ShowAnnouncement(int kind, string message) { if (!string.IsNullOrEmpty(message)) { _announcementText = message; _announcementStarted = Time.unscaledTime; _announcementUntil = _announcementStarted + 3.4f; } } internal void Tick(CtfController controller) { if (controller == null) { return; } if (!_config.ShowHud.Value) { if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } return; } EnsureBuilt(); if (!((Object)(object)_root == (Object)null)) { if (!_root.activeSelf) { _root.SetActive(true); } CtfState state = controller.State; bool flag = state?.Active ?? false; UpdateAnnouncement(); if (!flag) { Set(_score, ""); Set(_target, ""); Set(_flagStatus, ""); Set(_objective, ""); Set(_status, CtfGame.IsPlayableScene ? ("capture the flag: " + controller.Status.ToLowerInvariant()) : ""); Set(_hostHint, ""); HideMarkers(); } else { Set(_status, ""); UpdateScoreboard(state); UpdateObjective(state); UpdateHostHint(controller); UpdateMarkers(state); } } } private void UpdateScoreboard(CtfState state) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) int team = CtfGame.GetTeam(CtfGame.LocalPlayerId); bool flag = state.IsPlayingTeam(team); int teamId = (flag ? team : state.TeamA); int teamId2 = (flag ? state.OtherTeam(team) : state.TeamB); string text = Hex(CtfArt.ColorForTeam(state, teamId)); string text2 = Hex(CtfArt.ColorForTeam(state, teamId2)); Set(_score, "" + state.ScoreForTeam(teamId) + " / " + state.ScoreForTeam(teamId2) + ""); Set(_target, "first to " + state.CapturesToWin); FlagState flag2 = state.FlagForTeam(teamId); FlagState flag3 = state.FlagForTeam(teamId2); Set(_flagStatus, "" + FlagWord(flag2, flag ? "your flag" : "team 1") + " " + FlagWord(flag3, flag ? "enemy flag" : "team 2") + ""); } private static string FlagWord(FlagState flag, string label) { if (flag == null) { return label; } if (flag.IsHome) { return label + " home"; } if (flag.IsCarried) { return label + " taken"; } return label + " down " + Mathf.CeilToInt(flag.ReturnIn) + "s"; } private void UpdateObjective(CtfState state) { int localPlayerId = CtfGame.LocalPlayerId; int team = CtfGame.GetTeam(localPlayerId); if (!state.IsPlayingTeam(team)) { Set(_objective, state.Ending ? "round over" : "spectating capture the flag"); return; } FlagState flagState = state.FlagForTeam(team); FlagState flagState2 = state.FlagForTeam(state.OtherTeam(team)); if (flagState != null && flagState2 != null) { if (state.Ending) { Set(_objective, "round over"); } else if (flagState2.IsCarried && flagState2.CarrierPlayerId == localPlayerId) { Set(_objective, (_config.RequireOwnFlagHome.Value && !flagState.IsHome) ? "you have the flag: your own flag must be home to score" : "you have the flag: get it to your base"); } else if (flagState.IsCarried) { Set(_objective, "your flag is being carried: kill the carrier"); } else if (flagState.IsDropped) { Set(_objective, "your flag is down: touch it to send it home"); } else if (flagState2.IsDropped) { Set(_objective, "the enemy flag is down: grab it before it resets"); } else { Set(_objective, "take the enemy flag and bring it home"); } } } private void UpdateHostHint(CtfController controller) { //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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (!controller.IsAuthority) { Set(_hostHint, ""); return; } Set(_hostHint, "host " + ((object)_config.SetBaseAKey.Value/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + " / " + ((object)_config.SetBaseBKey.Value/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + " place bases " + ((object)_config.ClearBasesKey.Value/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + " reset them"); } private void UpdateAnnouncement() { //IL_00c8: 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) float unscaledTime = Time.unscaledTime; if (unscaledTime > _announcementUntil || string.IsNullOrEmpty(_announcementText)) { Set(_announcement, ""); return; } float num = unscaledTime - _announcementStarted; float num2 = _announcementUntil - unscaledTime; float num3 = ((num < 0.16f) ? Mathf.Lerp(1.35f, 1f, num / 0.16f) : 1f); float alpha = ((num2 < 0.7f) ? Mathf.Clamp01(num2 / 0.7f) : 1f); Set(_announcement, _announcementText); ((TMP_Text)_announcement).alpha = alpha; if ((Object)(object)_announcementRect != (Object)null) { ((Transform)_announcementRect).localScale = Vector3.one * num3; } } private void UpdateMarkers(CtfState state) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) if (!_config.ShowWorldMarkers.Value) { HideMarkers(); return; } Camera main = Camera.main; int localPlayerId = CtfGame.LocalPlayerId; int team = CtfGame.GetTeam(localPlayerId); if ((Object)(object)main == (Object)null || !state.IsPlayingTeam(team) || state.Ending) { HideMarkers(); return; } FlagState flagState = state.FlagForTeam(team); FlagState flagState2 = state.FlagForTeam(state.OtherTeam(team)); Color color = CtfArt.ColorForTeam(state, team); Color color2 = CtfArt.ColorForTeam(state, state.OtherTeam(team)); bool flag = flagState2 != null && flagState2.IsCarried && flagState2.CarrierPlayerId == localPlayerId; _enemyFlagMarker.Show(main, flagState2 != null && !flag, flagState2?.Position ?? Vector3.zero, (flagState2 != null && flagState2.IsCarried) ? "carrier" : "flag", color2); _ownFlagMarker.Show(main, flagState != null && !flagState.IsHome, flagState?.Position ?? Vector3.zero, (flagState != null && flagState.IsCarried) ? "stolen" : "your flag", color); _homeBaseMarker.Show(main, flag, state.BaseForTeam(team), "base", color); } private void HideMarkers() { _enemyFlagMarker?.Hide(); _ownFlagMarker?.Hide(); _homeBaseMarker?.Hide(); } private void EnsureBuilt() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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_016c: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { CtfRuntime instance = CtfRuntime.Instance; if (!((Object)(object)instance == (Object)null)) { _font = FindGameFont(); _root = new GameObject("StraftatCTF HUD") { hideFlags = (HideFlags)61 }; _root.transform.SetParent(((Component)instance).transform, false); _canvas = _root.AddComponent(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 500; CanvasScaler val = _root.AddComponent(); val.uiScaleMode = (ScaleMode)1; val.referenceResolution = new Vector2(1920f, 1080f); val.screenMatchMode = (ScreenMatchMode)0; val.matchWidthOrHeight = 0.5f; float num = Mathf.Clamp(_config.HudScale.Value, 0.5f, 2f); _score = Label("Score", new Vector2(0.5f, 1f), new Vector2(0f, -54f), new Vector2(620f, 78f), 72f * num, (TextAlignmentOptions)514, (FontStyles)3); _target = Label("Target", new Vector2(0.5f, 1f), new Vector2(0f, -104f), new Vector2(620f, 32f), 22f * num, (TextAlignmentOptions)514, (FontStyles)2, (Color?)new Color(0.62f, 0.62f, 0.62f)); _flagStatus = Label("Flag status", new Vector2(0.5f, 1f), new Vector2(0f, -136f), new Vector2(900f, 34f), 26f * num, (TextAlignmentOptions)514, (FontStyles)3); _objective = Label("Objective", new Vector2(0.5f, 0f), new Vector2(0f, 128f), new Vector2(1100f, 40f), 27f * num, (TextAlignmentOptions)514, (FontStyles)2, (Color?)new Color(0.88f, 0.88f, 0.88f)); _announcement = Label("Announcement", new Vector2(0.5f, 0.5f), new Vector2(0f, 232f), new Vector2(1400f, 96f), 58f * num, (TextAlignmentOptions)514, (FontStyles)3); _announcementRect = ((TMP_Text)_announcement).rectTransform; _status = Label("Status", new Vector2(0f, 1f), new Vector2(24f, -24f), new Vector2(760f, 34f), 22f * num, (TextAlignmentOptions)513, (FontStyles)2, (Color?)new Color(0.78f, 0.82f, 0.9f)); ((TMP_Text)_status).rectTransform.pivot = new Vector2(0f, 1f); _hostHint = Label("Host hint", new Vector2(0f, 0f), new Vector2(24f, 24f), new Vector2(900f, 30f), 19f * num, (TextAlignmentOptions)513, (FontStyles)2, (Color?)new Color(0.55f, 0.55f, 0.58f)); ((TMP_Text)_hostHint).rectTransform.pivot = new Vector2(0f, 0f); _enemyFlagMarker = new CtfMarker(this, "Enemy flag marker", 24f * num); _ownFlagMarker = new CtfMarker(this, "Own flag marker", 22f * num); _homeBaseMarker = new CtfMarker(this, "Home base marker", 22f * num); CtfLog.Debug("CTF HUD built" + (((Object)(object)_font != (Object)null) ? (" using font " + ((Object)_font).name) : " with the default font")); } } } private TextMeshProUGUI Label(string name, Vector2 anchor, Vector2 offset, Vector2 size, float fontSize, TextAlignmentOptions alignment, FontStyles style, Color? color = null) { //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 //IL_0057: 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_007f: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name) { hideFlags = (HideFlags)61 }; val.transform.SetParent(_root.transform, false); TextMeshProUGUI val2 = val.AddComponent(); if ((Object)(object)_font != (Object)null) { ((TMP_Text)val2).font = _font; } ((TMP_Text)val2).fontSize = fontSize; ((TMP_Text)val2).alignment = alignment; ((TMP_Text)val2).fontStyle = style; ((Graphic)val2).color = (Color)(((??)color) ?? Color.white); ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).richText = true; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)0; ((TMP_Text)val2).outlineWidth = 0.22f; ((TMP_Text)val2).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220); RectTransform rectTransform = ((TMP_Text)val2).rectTransform; rectTransform.anchorMin = anchor; rectTransform.anchorMax = anchor; rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.sizeDelta = size; rectTransform.anchoredPosition = offset; return val2; } internal TextMeshProUGUI CreateMarkerLabel(string name, float fontSize) { //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_0021: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = Label(name, new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(260f, 34f), fontSize, (TextAlignmentOptions)514, (FontStyles)3); ((Component)val).gameObject.SetActive(false); return val; } private static TMP_FontAsset FindGameFont() { try { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); if (array != null && array.Length != 0) { TMP_FontAsset val = ((IEnumerable)array).FirstOrDefault((Func)((TMP_FontAsset f) => (Object)(object)f != (Object)null && ((Object)f).name.IndexOf("GOTHICBI", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable)array).FirstOrDefault((Func)((TMP_FontAsset f) => (Object)(object)f != (Object)null && ((Object)f).name.IndexOf("gothic", StringComparison.OrdinalIgnoreCase) >= 0)); if ((Object)(object)val != (Object)null) { return val; } } } catch { } try { return TMP_Settings.defaultFontAsset; } catch { return null; } } private static void Set(TextMeshProUGUI label, string value) { if (!((Object)(object)label == (Object)null)) { if (((TMP_Text)label).text != value) { ((TMP_Text)label).text = value; } if (((TMP_Text)label).alpha < 1f && !string.IsNullOrEmpty(value)) { ((TMP_Text)label).alpha = 1f; } } } private static string Hex(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ColorUtility.ToHtmlStringRGB(color); } } internal static class CtfLocal { private static FirstPersonController _tracked; private static float _naturalMovementFactor = 1f; private static float _lastWrittenFactor = float.NaN; internal static void Tick(CtfController controller) { FirstPersonController localPlayer = CtfGame.LocalPlayer; if ((Object)(object)localPlayer == (Object)null) { _tracked = null; return; } if (localPlayer != _tracked) { _tracked = localPlayer; _naturalMovementFactor = localPlayer.movementFactor; _lastWrittenFactor = float.NaN; } if (controller == null || !controller.Active) { ReleaseMovementPenalty(localPlayer); return; } CtfGame.EnsureRoundStartedFlag(localPlayer); ApplyCarrierMovementPenalty(controller, localPlayer); } internal static void Reset() { _tracked = null; _lastWrittenFactor = float.NaN; _naturalMovementFactor = 1f; } private static void ApplyCarrierMovementPenalty(CtfController controller, FirstPersonController player) { float valueOrDefault = (CtfPlugin.Instance?.Settings?.CarrierSpeedPenalty.Value).GetValueOrDefault(); if (valueOrDefault <= 0f) { ReleaseMovementPenalty(player); return; } float movementFactor = player.movementFactor; if (float.IsNaN(_lastWrittenFactor) || !Mathf.Approximately(movementFactor, _lastWrittenFactor)) { _naturalMovementFactor = movementFactor; } float num = ((controller.State != null && controller.State.TeamCarrying(CtfGame.LocalPlayerId) >= 0) ? (_naturalMovementFactor * (1f - valueOrDefault)) : _naturalMovementFactor); if (!Mathf.Approximately(movementFactor, num)) { player.movementFactor = num; _lastWrittenFactor = num; } } private static void ReleaseMovementPenalty(FirstPersonController player) { if (!float.IsNaN(_lastWrittenFactor)) { if (Mathf.Approximately(player.movementFactor, _lastWrittenFactor)) { player.movementFactor = _naturalMovementFactor; } _lastWrittenFactor = float.NaN; } } } internal static class CtfLog { private static ManualLogSource _source; internal static void Bind(ManualLogSource source) { _source = source; } internal static void Info(string message) { ManualLogSource source = _source; if (source != null) { source.LogInfo((object)message); } } internal static void Warning(string message) { ManualLogSource source = _source; if (source != null) { source.LogWarning((object)message); } } internal static void Error(string message) { ManualLogSource source = _source; if (source != null) { source.LogError((object)message); } } internal static void Debug(string message) { if (CtfPlugin.VerboseLogging) { ManualLogSource source = _source; if (source != null) { source.LogInfo((object)("[debug] " + message)); } } } internal static void Guard(string what, Action action) { try { action(); } catch (Exception ex) { Error("CTF failed while " + what + ": " + ex); } } } internal enum FlagStatus { Home, Carried, Dropped } internal sealed class FlagState { internal FlagStatus Status = FlagStatus.Home; internal int CarrierPlayerId = -1; internal Vector3 Position; internal float ReturnAt; internal float ReturnIn; internal bool IsHome => Status == FlagStatus.Home; internal bool IsCarried => Status == FlagStatus.Carried; internal bool IsDropped => Status == FlagStatus.Dropped; internal FlagState Clone() { //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) return new FlagState { Status = Status, CarrierPlayerId = CarrierPlayerId, Position = Position, ReturnAt = ReturnAt, ReturnIn = ReturnIn }; } } internal sealed class CtfState { internal string SessionId = ""; internal int Sequence; internal string SceneName = ""; internal bool Active; internal bool Ending; internal int TeamA = -1; internal int TeamB = -1; internal int ScoreA; internal int ScoreB; internal int CapturesToWin = 3; internal Vector3 BaseA; internal Vector3 BaseB; internal FlagState FlagA = new FlagState(); internal FlagState FlagB = new FlagState(); internal CtfState Clone() { //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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) return new CtfState { SessionId = SessionId, Sequence = Sequence, SceneName = SceneName, Active = Active, Ending = Ending, TeamA = TeamA, TeamB = TeamB, ScoreA = ScoreA, ScoreB = ScoreB, CapturesToWin = CapturesToWin, BaseA = BaseA, BaseB = BaseB, FlagA = FlagA.Clone(), FlagB = FlagB.Clone() }; } internal bool IsPlayingTeam(int teamId) { return teamId >= 0 && (teamId == TeamA || teamId == TeamB); } internal int ScoreForTeam(int teamId) { return (teamId == TeamA) ? ScoreA : ((teamId == TeamB) ? ScoreB : 0); } internal Vector3 BaseForTeam(int teamId) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) return (teamId == TeamB) ? BaseB : BaseA; } internal FlagState FlagForTeam(int teamId) { return (teamId == TeamA) ? FlagA : ((teamId == TeamB) ? FlagB : null); } internal int OtherTeam(int teamId) { return (teamId == TeamA) ? TeamB : ((teamId == TeamB) ? TeamA : (-1)); } internal int TeamCarrying(int playerId) { if (FlagA.IsCarried && FlagA.CarrierPlayerId == playerId) { return TeamA; } if (FlagB.IsCarried && FlagB.CarrierPlayerId == playerId) { return TeamB; } return -1; } } internal static class CtfProtocol { internal const int Version = 2; internal const int FieldCount = 29; internal const int MaxPayloadLength = 4096; internal const int MaxAnnouncementLength = 160; private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture; internal static string Serialize(CtfState s) { string[] obj = new string[29] { 2.ToString(Invariant), s.SessionId ?? "", s.Sequence.ToString(Invariant), Convert.ToBase64String(Encoding.UTF8.GetBytes(s.SceneName ?? "")), Bool(s.Active), Bool(s.Ending), s.TeamA.ToString(Invariant), s.TeamB.ToString(Invariant), s.ScoreA.ToString(Invariant), s.ScoreB.ToString(Invariant), s.CapturesToWin.ToString(Invariant), Float(s.BaseA.x), Float(s.BaseA.y), Float(s.BaseA.z), Float(s.BaseB.x), Float(s.BaseB.y), Float(s.BaseB.z), null, null, null, null, null, null, null, null, null, null, null, null }; int status = (int)s.FlagA.Status; obj[17] = status.ToString(Invariant); obj[18] = s.FlagA.CarrierPlayerId.ToString(Invariant); obj[19] = Float(s.FlagA.Position.x); obj[20] = Float(s.FlagA.Position.y); obj[21] = Float(s.FlagA.Position.z); obj[22] = Float(s.FlagA.ReturnIn); status = (int)s.FlagB.Status; obj[23] = status.ToString(Invariant); obj[24] = s.FlagB.CarrierPlayerId.ToString(Invariant); obj[25] = Float(s.FlagB.Position.x); obj[26] = Float(s.FlagB.Position.y); obj[27] = Float(s.FlagB.Position.z); obj[28] = Float(s.FlagB.ReturnIn); string[] value = obj; return string.Join("|", value); } internal static bool TryDeserialize(string payload, out CtfState state, out string error) { //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0217: 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) state = null; error = ""; if (string.IsNullOrEmpty(payload) || payload.Length > 4096) { error = "empty or oversized payload"; return false; } string[] array = payload.Split('|'); if (array.Length != 29) { error = "expected " + 29 + " fields, got " + array.Length; return false; } try { int num = Int(array[0]); if (num != 2) { throw new FormatException("unsupported protocol " + num); } string text = array[1]; if (text.Length < 8 || text.Length > 64) { throw new FormatException("bad session id"); } foreach (char c in text) { if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) { throw new FormatException("bad session id"); } } string text2 = Encoding.UTF8.GetString(Convert.FromBase64String(array[3])); if (text2.Length > 128) { throw new FormatException("scene name too long"); } CtfState ctfState = new CtfState { SessionId = text, Sequence = Int(array[2]), SceneName = text2, Active = ParseBool(array[4]), Ending = ParseBool(array[5]), TeamA = Int(array[6]), TeamB = Int(array[7]), ScoreA = Int(array[8]), ScoreB = Int(array[9]), CapturesToWin = Int(array[10]), BaseA = Vec(array, 11), BaseB = Vec(array, 14), FlagA = Flag(array, 17), FlagB = Flag(array, 23) }; if (ctfState.Sequence < 0) { throw new FormatException("negative sequence"); } if (ctfState.CapturesToWin < 1 || ctfState.CapturesToWin > 100) { throw new FormatException("out-of-range capture target"); } if (ctfState.ScoreA < 0 || ctfState.ScoreB < 0) { throw new FormatException("negative score"); } if (ctfState.ScoreA > ctfState.CapturesToWin || ctfState.ScoreB > ctfState.CapturesToWin) { throw new FormatException("score exceeds capture target"); } if (ctfState.Active) { if (ctfState.TeamA < 0 || ctfState.TeamB < 0 || ctfState.TeamA == ctfState.TeamB) { throw new FormatException("invalid teams"); } if (string.IsNullOrWhiteSpace(ctfState.SceneName)) { throw new FormatException("active state has no scene"); } if (ctfState.FlagA.IsCarried && ctfState.FlagB.IsCarried && ctfState.FlagA.CarrierPlayerId == ctfState.FlagB.CarrierPlayerId) { throw new FormatException("one player cannot carry both flags"); } } else if (ctfState.Ending) { throw new FormatException("inactive state cannot be ending"); } state = ctfState; return true; } catch (Exception ex) { error = ex.Message; return false; } } internal static bool TrySanitiseAnnouncement(string message, out string sanitised) { sanitised = ""; if (string.IsNullOrEmpty(message) || message.Length > 160) { return false; } StringBuilder stringBuilder = new StringBuilder(message.Length); foreach (char c in message) { if (!char.IsControl(c) && c != '<' && c != '>') { stringBuilder.Append(c); } } sanitised = stringBuilder.ToString().Trim(); return sanitised.Length > 0; } internal static string FormatVector(Vector3 v) { //IL_0008: 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_0034: Unknown result type (might be due to invalid IL or missing references) return Float(v.x) + "," + Float(v.y) + "," + Float(v.z); } internal static bool TryParseVector(string value, out Vector3 vector) { //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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) vector = Vector3.zero; if (string.IsNullOrWhiteSpace(value)) { return false; } string[] array = value.Split(','); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, Invariant, out var result) || !float.TryParse(array[1], NumberStyles.Float, Invariant, out var result2) || !float.TryParse(array[2], NumberStyles.Float, Invariant, out var result3)) { return false; } if (!Finite(result) || !Finite(result2) || !Finite(result3)) { return false; } vector = new Vector3(result, result2, result3); return true; } private static FlagState Flag(string[] p, int start) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) FlagState flagState = new FlagState { Status = Status(p[start]), CarrierPlayerId = Int(p[start + 1]), Position = Vec(p, start + 2), ReturnIn = Single(p[start + 5]) }; if (flagState.IsCarried && flagState.CarrierPlayerId < 0) { throw new FormatException("carried flag has no carrier"); } if (!flagState.IsCarried && flagState.CarrierPlayerId != -1) { throw new FormatException("uncarried flag has a carrier"); } if (flagState.CarrierPlayerId > 1024) { throw new FormatException("implausible carrier id"); } if (!Finite(flagState.ReturnIn) || flagState.ReturnIn < 0f || flagState.ReturnIn > 600f) { throw new FormatException("bad return timer"); } if (!flagState.IsDropped && flagState.ReturnIn != 0f) { throw new FormatException("only dropped flags have a return timer"); } return flagState; } private static Vector3 Vec(string[] p, int start) { //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_0057: Unknown result type (might be due to invalid IL or missing references) float num = Single(p[start]); float num2 = Single(p[start + 1]); float num3 = Single(p[start + 2]); if (!Finite(num) || !Finite(num2) || !Finite(num3)) { throw new FormatException("non-finite vector"); } return new Vector3(num, num2, num3); } private static FlagStatus Status(string value) { int num = Int(value); if (num < 0 || num > 2) { throw new FormatException("invalid flag status"); } return (FlagStatus)num; } private static int Int(string value) { return int.Parse(value, NumberStyles.Integer, Invariant); } private static float Single(string value) { return float.Parse(value, NumberStyles.Float, Invariant); } private static string Float(float value) { return value.ToString("R", Invariant); } private static string Bool(bool value) { return value ? "1" : "0"; } private static bool ParseBool(string value) { int result; if (!(value == "1")) { if (!(value == "0")) { throw new FormatException("invalid boolean"); } result = 0; } else { result = 1; } return (byte)result != 0; } private static bool Finite(float value) { return !float.IsNaN(value) && !float.IsInfinity(value) && Math.Abs(value) < 100000f; } } internal sealed class CtfNetwork { internal const uint ModId = 1129596465u; private const string CompatibilityKey = "straftatctf.version"; private static CtfNetwork _instance; private ulong _advertisedLobby; private bool _registered; internal static CtfNetwork Instance => _instance; internal static bool Registered => _instance != null && _instance._registered; internal static bool InLobby { get { try { return MyceliumNetwork.InLobby; } catch { return false; } } } internal static bool IsLobbyHost { get { try { return !MyceliumNetwork.InLobby || MyceliumNetwork.IsHost; } catch { return true; } } } internal static bool EnsureRegistered() { if (_instance != null) { return _instance._registered; } CtfNetwork ctfNetwork = (_instance = new CtfNetwork()); try { MyceliumNetwork.RegisterNetworkObject((object)ctfNetwork, 1129596465u, 0); MyceliumNetwork.RegisterPlayerDataKey("straftatctf.version"); MyceliumNetwork.LobbyEntered += ctfNetwork.OnLobbyEntered; MyceliumNetwork.LobbyLeft += ctfNetwork.OnLobbyLeft; ctfNetwork._registered = true; CtfLog.Info("CTF networking registered (mod id " + 1129596465u + ")."); } catch (Exception ex) { CtfLog.Error("Could not register CTF networking; CTF will stay inactive: " + ex); } return ctfNetwork._registered; } internal void Tick() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!_registered) { return; } try { if (!MyceliumNetwork.InLobby) { _advertisedLobby = 0uL; } else if (MyceliumNetwork.Lobby.m_SteamID != _advertisedLobby) { Advertise(); } } catch { } } internal bool AllPlayersCompatible(out string reason) { //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) //IL_0058: 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) reason = ""; try { if (!MyceliumNetwork.InLobby) { return true; } CSteamID[] players = MyceliumNetwork.Players; if (players == null || players.Length == 0) { reason = "Waiting for the Steam lobby player list..."; return false; } CSteamID[] array = players; foreach (CSteamID val in array) { if (!MyceliumNetwork.HasPlayerData(val, "straftatctf.version")) { reason = "Waiting for everyone to report Capture the Flag 1.0.4."; return false; } string playerData = MyceliumNetwork.GetPlayerData(val, "straftatctf.version"); if (!string.Equals(playerData, "1.0.4", StringComparison.Ordinal)) { reason = "Someone has Capture the Flag " + (string.IsNullOrEmpty(playerData) ? "missing" : playerData) + "; everyone needs 1.0.4."; return false; } } return true; } catch (Exception ex) { reason = "Waiting for Mycelium lobby data (" + ex.Message + ")."; return false; } } internal void BroadcastSnapshot(CtfState state) { if (!_registered || state == null) { return; } try { if (MyceliumNetwork.InLobby) { MyceliumNetwork.RPC(1129596465u, "ReceiveSnapshot", (ReliableType)1, new object[1] { CtfProtocol.Serialize(state) }); } } catch (Exception ex) { CtfLog.Warning("Could not broadcast the CTF snapshot: " + ex.Message); } } internal void BroadcastEvent(string sessionId, int kind, string message) { if (!_registered) { return; } try { if (MyceliumNetwork.InLobby) { MyceliumNetwork.RPC(1129596465u, "ReceiveEvent", (ReliableType)1, new object[3] { sessionId ?? "", kind, message ?? "" }); } } catch (Exception ex) { CtfLog.Warning("Could not broadcast a CTF event: " + ex.Message); } } [CustomRPC] public void ReceiveSnapshot(string payload, RPCInfo info) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!IsTrustedHost(info)) { CtfLog.Warning("Ignored a CTF snapshot that did not come from the lobby host."); } else { CtfPlugin.Instance?.Controller?.ApplyRemoteSnapshot(payload); } } [CustomRPC] public void ReceiveEvent(string sessionId, int kind, string message, RPCInfo info) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (IsTrustedHost(info)) { CtfPlugin.Instance?.Controller?.ApplyRemoteEvent(sessionId, kind, message); } } private static bool IsTrustedHost(RPCInfo info) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_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) try { if (!MyceliumNetwork.InLobby) { return false; } CSteamID lobbyHost = MyceliumNetwork.LobbyHost; return lobbyHost.m_SteamID != 0L && info.SenderSteamID.m_SteamID == lobbyHost.m_SteamID; } catch { return false; } } private void OnLobbyEntered() { Advertise(); } private void OnLobbyLeft() { _advertisedLobby = 0uL; CtfPlugin.Instance?.Controller?.OnLobbyLeft(); } private void Advertise() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { if (MyceliumNetwork.InLobby) { MyceliumNetwork.SetPlayerData("straftatctf.version", (object)"1.0.4"); _advertisedLobby = MyceliumNetwork.Lobby.m_SteamID; CtfLog.Info("Advertised CTF version 1.0.4 to the Steam lobby."); } } catch (Exception ex) { CtfLog.Debug("Could not advertise the CTF version yet: " + ex.Message); } } } internal static class CtfEventKind { internal const int Announcement = 0; internal const int FlagTaken = 1; internal const int FlagDropped = 2; internal const int FlagReturned = 3; internal const int Capture = 4; internal const int MatchStart = 5; internal const int RoundWon = 6; internal static bool IsValid(int kind) { return kind >= 0 && kind <= 6; } } internal static class CtfPatches { private const string HarmonyId = "com.straftatmods.capturetheflag.patches"; private const BindingFlags AnyMethod = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static Harmony _harmony; internal static bool RoundStartedThisScene { get; private set; } internal static bool Apply() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if (_harmony != null) { return true; } if (!CtfGame.ResolveReflection()) { return false; } _harmony = new Harmony("com.straftatmods.capturetheflag.patches"); bool flag = true; flag &= Patch(typeof(GameManager), "WaitForDraw", "GameManager_WaitForDraw_Prefix", null); flag &= PatchIterator(typeof(GameManager), "WaitForDraw", "GameManager_WaitForDraw_MoveNext_Prefix"); flag &= PatchByPrefix(typeof(GameManager), "RpcLogic___PlayerDied_", "GameManager_PlayerDied_Prefix", "GameManager_PlayerDied_Postfix"); flag &= PatchByPrefix(typeof(GameManager), "RpcLogic___SetStartTime_", "GameManager_SetStartTime_Prefix", null); flag &= PatchByPrefix(typeof(PlayerManager), "RpcLogic___IncrementReadyPlayers_", null, "PlayerManager_IncrementReadyPlayers_Postfix"); PatchOptional(typeof(PlayerManager), "ReturnSpawnPoint", null, "PlayerManager_ReturnSpawnPoint_Postfix", "respawns will use STRAFTAT's own random spawn point placement"); flag &= Patch(typeof(PauseManager), "StartRoundDelay", "PauseManager_StartRoundDelay_Prefix", null); flag &= Patch(typeof(PauseManager), "InvokeBeforeSpawn", "PauseManager_InvokeBeforeSpawn_Prefix", null); flag &= Patch(typeof(PauseManager), "InvokeRoundStarted", null, "PauseManager_InvokeRoundStarted_Postfix"); if (flag) { CtfLog.Info("All required CTF game hooks were applied."); } else { CtfLog.Error("One or more required game hooks are missing. This STRAFTAT version is not supported; Capture the Flag will stay inactive and the game will play normally."); Remove(); } return flag; } internal static void Remove() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { CtfLog.Debug("Could not remove CTF patches: " + ex.Message); } _harmony = null; } internal static void OnSceneLoaded() { RoundStartedThisScene = false; } private static bool GameManager_WaitForDraw_Prefix(ref IEnumerator __result) { if (!CtfPlugin.ModeActive) { return true; } __result = NoOp(); return false; } private static bool GameManager_WaitForDraw_MoveNext_Prefix(ref bool __result) { if (!CtfPlugin.ModeActive) { return true; } __result = false; return false; } private static void GameManager_PlayerDied_Prefix(int playerId) { CtfPlugin.Instance?.Controller?.OnPlayerDied(playerId); } private static void GameManager_PlayerDied_Postfix(int playerId) { CtfController ctfController = CtfPlugin.Instance?.Controller; if (ctfController != null && CtfPlugin.ModeActive) { CtfGame.StopWaitForDraw(); ctfController.ScheduleRespawn(playerId); } } private static void GameManager_SetStartTime_Prefix(ref float serverTimeTillStart) { if (CtfPlugin.ModeActive && RoundStartedThisScene) { serverTimeTillStart = 0f; } } private static void PlayerManager_IncrementReadyPlayers_Postfix() { if (CtfPlugin.ModeActive && RoundStartedThisScene) { CtfPlugin instance = CtfPlugin.Instance; if (instance != null && instance.Controller?.IsAuthority == true) { CtfGame.ClearStartCountdown(); } } } private static bool PauseManager_StartRoundDelay_Prefix() { return !CtfPlugin.ModeActive || !RoundStartedThisScene; } private static void PlayerManager_ReturnSpawnPoint_Postfix(ref Transform __result) { int respawningPlayerId = CtfGame.RespawningPlayerId; if (respawningPlayerId >= 0 && CtfPlugin.ModeActive) { CtfController ctfController = CtfPlugin.Instance?.Controller; if (ctfController != null && ctfController.IsAuthority && CtfSpawns.TryChoose(respawningPlayerId, ctfController.State, CtfPlugin.Instance?.Settings, out var chosen) && (Object)(object)chosen != (Object)null) { __result = chosen; } } } private static bool PauseManager_InvokeBeforeSpawn_Prefix() { return !CtfPlugin.ModeActive || !RoundStartedThisScene; } private static void PauseManager_InvokeRoundStarted_Postfix() { RoundStartedThisScene = true; } private static IEnumerator NoOp() { yield break; } private static bool Patch(Type targetType, string targetName, string prefixName, string postfixName) { MethodInfo method = targetType.GetMethod(targetName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { CtfLog.Error("Missing required hook: " + targetType.Name + "." + targetName); return false; } return ApplyTo(method, prefixName, postfixName); } private static void PatchOptional(Type targetType, string targetName, string prefixName, string postfixName, string consequence) { MethodInfo method = targetType.GetMethod(targetName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!(method != null) || !ApplyTo(method, prefixName, postfixName)) { CtfLog.Warning("Optional hook " + targetType.Name + "." + targetName + " could not be applied; " + consequence + "."); } } private static bool PatchIterator(Type declaringType, string iteratorName, string prefixName) { string namePrefix = "<" + iteratorName + ">d__"; Type[] array = (from t in declaringType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic) where t.Name.StartsWith(namePrefix, StringComparison.Ordinal) && typeof(IEnumerator).IsAssignableFrom(t) select t).ToArray(); if (array.Length == 0) { CtfLog.Error("Missing required hook: the state machine for " + declaringType.Name + "." + iteratorName + " was not found."); return false; } if (array.Length > 1) { CtfLog.Error("Ambiguous hook: " + declaringType.Name + "+" + namePrefix + "* matched " + array.Length + " types (" + string.Join(", ", array.Select((Type t) => t.Name)) + ")."); return false; } MethodInfo method = array[0].GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { CtfLog.Error("Missing required hook: " + array[0].Name + ".MoveNext"); return false; } return ApplyTo(method, prefixName, null); } private static bool PatchByPrefix(Type targetType, string namePrefix, string prefixName, string postfixName) { MethodInfo[] array = (from m in targetType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name.StartsWith(namePrefix, StringComparison.Ordinal) select m).ToArray(); if (array.Length == 0) { CtfLog.Error("Missing required hook: " + targetType.Name + "." + namePrefix + "*"); return false; } if (array.Length > 1) { CtfLog.Error("Ambiguous hook: " + targetType.Name + "." + namePrefix + "* matched " + array.Length + " methods (" + string.Join(", ", array.Select((MethodInfo m) => m.Name)) + ")."); return false; } return ApplyTo(array[0], prefixName, postfixName); } private static bool ApplyTo(MethodInfo target, string prefixName, string postfixName) { HarmonyMethod val = Resolve(prefixName); HarmonyMethod val2 = Resolve(postfixName); if ((prefixName != null && val == null) || (postfixName != null && val2 == null)) { return false; } try { _harmony.Patch((MethodBase)target, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); CtfLog.Debug("Patched " + target.DeclaringType?.Name + "." + target.Name); return true; } catch (Exception ex) { CtfLog.Error("Could not patch " + target.DeclaringType?.Name + "." + target.Name + ": " + ex); return false; } } private static HarmonyMethod Resolve(string name) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (name == null) { return null; } MethodInfo method = typeof(CtfPatches).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic); if (method == null) { CtfLog.Error("Internal error: CTF patch method " + name + " is missing."); } return (method == null) ? ((HarmonyMethod)null) : new HarmonyMethod(method); } } internal sealed class CtfRuntime : MonoBehaviour { private const string ObjectName = "StraftatCTF Runtime"; private static CtfRuntime _instance; private static bool _quitting; internal static CtfRuntime Instance => _instance; internal static void Ensure() { //IL_0021: 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_0030: Expected O, but got Unknown if (!_quitting && !((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("StraftatCTF Runtime") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent(); CtfLog.Debug("CTF runtime object created."); } } internal static void Shutdown() { _quitting = true; if (!((Object)(object)_instance == (Object)null)) { Object.Destroy((Object)(object)((Component)_instance).gameObject); _instance = null; } } private void Awake() { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Application.quitting += OnApplicationQuitting; } private void Update() { CtfPlugin.Instance?.RuntimeUpdate(); } private void LateUpdate() { CtfPlugin.Instance?.RuntimeLateUpdate(); } private void OnApplicationQuitting() { _quitting = true; } private void OnDestroy() { Application.quitting -= OnApplicationQuitting; if (!((Object)(object)_instance != (Object)(object)this)) { _instance = null; if (!_quitting) { CtfLog.Warning("The CTF runtime object was destroyed unexpectedly; recreating it."); CtfPlugin.Instance?.OnRuntimeLost(); Ensure(); } } } } internal static class CtfSpawnMath { internal static int Choose(float[] scores, int count, int shortlistSize, float variety, float roll) { if (scores == null) { return -1; } count = Mathf.Min(count, scores.Length); if (count <= 0) { return -1; } if (count == 1) { return 0; } int num = Mathf.Clamp(shortlistSize, 1, count); if (num == 1) { return 0; } variety = Mathf.Max(0.01f, variety); float best = scores[0]; float num2 = 0f; for (int i = 0; i < num; i++) { num2 += Weight(scores[i], best, variety); } if (!(num2 > 0f) || float.IsInfinity(num2)) { return 0; } float num3 = Mathf.Clamp01(roll) * num2; float num4 = 0f; for (int j = 0; j < num; j++) { num4 += Weight(scores[j], best, variety); if (num3 < num4) { return j; } } return num - 1; } private static float Weight(float score, float best, float variety) { if (float.IsNaN(score)) { return 0f; } float num = Mathf.Exp((score - best) / variety); return (float.IsNaN(num) || float.IsInfinity(num)) ? 0f : num; } } internal static class CtfSpawns { private struct HotSpot { internal Vector3 Position; internal float At; } private struct Candidate { internal SpawnPoint Point; internal int Severity; internal float Score; } private struct Hazard { internal Vector3 Position; internal Bounds Bounds; internal bool HasBounds; internal float DistanceTo(Vector3 point) { //IL_0016: 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_001e: 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_000a: Unknown result type (might be due to invalid IL or missing references) return HasBounds ? Vector3.Distance(point, ((Bounds)(ref Bounds)).ClosestPoint(point)) : Vector3.Distance(point, Position); } } private const float OccupiedRadius = 1.6f; private const float HotSpotMemorySeconds = 12f; private const float HotSpotRadius = 9f; private const int MaxRememberedDeaths = 64; private const float OwnDeathEmphasis = 1.6f; private const float ReuseWindowSeconds = 5f; private const float ReuseVetoSeconds = 1.5f; private const int ShortlistSize = 3; private const float IdealTeammateDistance = 9f; private const float TeammateSpread = 7f; private const float DuplicateSpawnDistance = 1.5f; private const float HazardCacheSeconds = 0.25f; private const float WeightSide = 1f; private const float WeightEnemyClearance = 0.9f; private const float WeightTeammate = 0.35f; private const float WeightHotSpot = 0.8f; private const float WeightReuse = 0.9f; private const float WeightObjective = 0.45f; private const int SeverityClean = 0; private const int SeverityWatched = 1; private const int SeverityHazard = 2; private const int SeverityOccupied = 3; private const float EyeHeight = 1.6f; private const int MaxSightHits = 8; private static readonly string[] HazardTypeNames = new string[11] { "ProximityMine", "Claymore", "DamageZone", "PhysicsGrenade", "Obus", "Bubble", "PredictedProjectile", "ShrapnelBallistic", "HandGrenade", "HandGrenadeTwo", "RebondBalle" }; private static readonly List _deaths = new List(); private static readonly Dictionary _lastDeath = new Dictionary(); private static readonly Dictionary _usedAt = new Dictionary(); private static readonly List _candidates = new List(); private static readonly List _scored = new List(); private static readonly List _enemies = new List(); private static readonly List _friends = new List(); private static readonly List _hazards = new List(); private static readonly RaycastHit[] _sightBuffer = (RaycastHit[])(object)new RaycastHit[8]; private static float[] _scoreBuffer = new float[8]; private static Type[] _hazardTypes = Array.Empty(); private static bool _hazardTypesResolved; private static string _candidateScene = ""; private static float _hazardsSweptAt = float.MinValue; internal static void Reset() { _deaths.Clear(); _lastDeath.Clear(); _usedAt.Clear(); _candidates.Clear(); _candidateScene = ""; } internal static void RecordDeath(int playerId, Vector3 position) { //IL_0001: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (CtfGame.IsFinite(position)) { float unscaledTime = Time.unscaledTime; _deaths.Add(new HotSpot { Position = position, At = unscaledTime }); _lastDeath[playerId] = new HotSpot { Position = position, At = unscaledTime }; if (_deaths.Count > 64) { ForgetOldDeaths(unscaledTime); } } } internal static bool TryChoose(int playerId, CtfState state, CtfConfig config, out Transform chosen) { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: 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_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) chosen = null; try { if (state == null || !state.Active || config == null) { return false; } if (!config.SmartRespawns.Value) { return false; } int team = CtfGame.GetTeam(playerId); if (!state.IsPlayingTeam(team)) { return false; } CollectCandidates(playerId, config); if (_candidates.Count == 0) { return false; } if (_candidates.Count == 1) { chosen = ((Component)_candidates[0]).transform; MarkUsed(_candidates[0]); return true; } float unscaledTime = Time.unscaledTime; float clearance = Mathf.Max(1f, config.RespawnEnemyClearance.Value); float num = Mathf.Max(0f, config.RespawnHazardRadius.Value); ForgetOldDeaths(unscaledTime); CollectPlayers(playerId, team); CollectHazards(unscaledTime, num > 0f); Vector3 val = state.BaseForTeam(team); Vector3 val2 = state.BaseForTeam(state.OtherTeam(team)); float separation = Mathf.Max(1f, CtfGame.HorizontalDistance(val, val2)); Vector3 focus; bool hasObjective = TryFindObjectiveFocus(playerId, team, state, out focus); _scored.Clear(); int num2 = int.MaxValue; foreach (SpawnPoint candidate in _candidates) { Vector3 position = ((Component)candidate).transform.position; int num3 = RateSeverity(candidate, position, clearance, num, unscaledTime); if (num3 < num2) { num2 = num3; } _scored.Add(new Candidate { Point = candidate, Severity = num3, Score = ScorePosition(playerId, candidate, position, val, val2, separation, clearance, hasObjective, focus, unscaledTime) }); } SpawnPoint val3 = PickFromShortlist(num2, config); if ((Object)(object)val3 == (Object)null) { return false; } MarkUsed(val3); chosen = ((Component)val3).transform; if (CtfPlugin.VerboseLogging) { CtfLog.Debug("Respawning " + CtfGame.PlayerName(playerId) + " at " + CtfProtocol.FormatVector(((Component)val3).transform.position) + " (severity " + num2 + ", " + _candidates.Count + " candidates)."); } return true; } catch (Exception ex) { CtfLog.Debug("Spawn selection failed, falling back to the game's own choice: " + ex.Message); return false; } } private static SpawnPoint PickFromShortlist(int severity, CtfConfig config) { _scored.RemoveAll((Candidate c) => c.Severity != severity || (Object)(object)c.Point == (Object)null); if (_scored.Count == 0) { return null; } if (_scored.Count == 1) { return _scored[0].Point; } _scored.Sort((Candidate a, Candidate b) => b.Score.CompareTo(a.Score)); if (_scoreBuffer.Length < _scored.Count) { _scoreBuffer = new float[_scored.Count]; } for (int num = 0; num < _scored.Count; num++) { _scoreBuffer[num] = _scored[num].Score; } int num2 = CtfSpawnMath.Choose(_scoreBuffer, _scored.Count, 3, config.RespawnVariety.Value, Random.value); return (num2 >= 0) ? _scored[num2].Point : _scored[0].Point; } private static int RateSeverity(SpawnPoint point, Vector3 position, float clearance, float hazardRadius, float now) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (_usedAt.TryGetValue(((Object)point).GetInstanceID(), out var value) && now - value < 1.5f) { return 3; } foreach (Vector3 enemy in _enemies) { if (Vector3.Distance(position, enemy) <= 1.6f) { return 3; } } foreach (Vector3 friend in _friends) { if (Vector3.Distance(position, friend) <= 1.6f) { return 3; } } if (hazardRadius > 0f && HasLiveOrdnance(position, hazardRadius)) { return 2; } foreach (Vector3 enemy2 in _enemies) { if (Vector3.Distance(position, enemy2) > clearance || !HasLineOfSight(position, enemy2)) { continue; } return 1; } return 0; } private static bool HasLiveOrdnance(Vector3 position, float radius) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) foreach (Hazard hazard in _hazards) { if (hazard.DistanceTo(position) <= radius) { return true; } } return false; } private static void CollectHazards(float now, bool needed) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (!needed) { _hazards.Clear(); _hazardsSweptAt = float.MinValue; } else { if (now - _hazardsSweptAt < 0.25f) { return; } _hazardsSweptAt = now; EnsureHazardTypes(); _hazards.Clear(); if (_hazardTypes.Length == 0) { return; } try { Type[] hazardTypes = _hazardTypes; foreach (Type type in hazardTypes) { Object[] array = Object.FindObjectsOfType(type); foreach (Object val in array) { Component val2 = (Component)(object)((val is Component) ? val : null); if (val2 == null || (Object)(object)val2.transform == (Object)null || !val2.gameObject.activeInHierarchy) { continue; } Vector3 position = val2.transform.position; if (CtfGame.IsFinite(position)) { Collider val3 = val2.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = val2.GetComponentInChildren(); } bool flag = (Object)(object)val3 != (Object)null && val3.enabled; _hazards.Add(new Hazard { Position = position, Bounds = (Bounds)(flag ? val3.bounds : default(Bounds)), HasBounds = flag }); } } } } catch (Exception ex) { CtfLog.Debug("Could not sweep for live ordnance: " + ex.Message); _hazards.Clear(); } } } private static bool HasLineOfSight(Vector3 spawn, Vector3 enemy) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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_0017: 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) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) //IL_0054: 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) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = spawn + Vector3.up * 1.6f; Vector3 val2 = enemy + Vector3.up * 1.6f; Vector3 val3 = val2 - val; float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude <= 0.01f) { return true; } int num = Physics.RaycastNonAlloc(new Ray(val, val3 / magnitude), _sightBuffer, magnitude, -1, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider collider = ((RaycastHit)(ref _sightBuffer[i])).collider; if (!((Object)(object)collider == (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent() != (Object)null)) { return false; } } return true; } catch { return true; } } private static float ScorePosition(int playerId, SpawnPoint point, Vector3 position, Vector3 ownBase, Vector3 enemyBase, float separation, float clearance, bool hasObjective, Vector3 objective, float now) { //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_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_014e: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: 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) float num = CtfGame.HorizontalDistance(position, ownBase); float num2 = CtfGame.HorizontalDistance(position, enemyBase); float num3 = Mathf.Clamp((num2 - num) / separation, -1f, 1f); float num4 = 1f; float num5 = NearestDistance(position, _enemies); if (num5 < float.MaxValue) { num4 = Mathf.Clamp01(num5 / (clearance * 2f)); } float num6 = 0f; float num7 = NearestDistance(position, _friends); if (num7 < float.MaxValue) { float num8 = (num7 - 9f) / 7f; num6 = Mathf.Exp(-0.5f * num8 * num8); } float num9 = 0f; foreach (HotSpot death in _deaths) { float num10 = Vector3.Distance(position, death.Position); if (!(num10 >= 9f)) { float num11 = 1f - Mathf.Clamp01((now - death.At) / 12f); num9 = Mathf.Max(num9, (1f - num10 / 9f) * num11); } } if (_lastDeath.TryGetValue(playerId, out var value)) { float num12 = Vector3.Distance(position, value.Position); if (num12 < 9f) { float num13 = 1f - Mathf.Clamp01((now - value.At) / 12f); float num14 = (1f - num12 / 9f) * num13 * 1.6f; num9 = Mathf.Max(num9, Mathf.Clamp01(num14)); } } float num15 = 0f; if (_usedAt.TryGetValue(((Object)point).GetInstanceID(), out var value2)) { num15 = Mathf.Clamp01(1f - (now - value2) / 5f); } float num16 = 0f; if (hasObjective) { num16 = Mathf.Clamp01(1f - CtfGame.HorizontalDistance(position, objective) / separation); } return 1f * num3 + 0.9f * num4 + 0.35f * num6 - 0.8f * num9 - 0.9f * num15 + 0.45f * num16; } private static bool TryFindObjectiveFocus(int playerId, int team, CtfState state, out Vector3 focus) { //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_0040: 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_008e: 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) focus = Vector3.zero; FlagState flagState = state.FlagForTeam(team); if (flagState != null && flagState.IsCarried && flagState.CarrierPlayerId >= 0 && CtfGame.TryGetPosition(flagState.CarrierPlayerId, out var position)) { focus = position; return true; } FlagState flagState2 = state.FlagForTeam(state.OtherTeam(team)); if (flagState2 != null && flagState2.IsCarried && flagState2.CarrierPlayerId >= 0 && flagState2.CarrierPlayerId != playerId && CtfGame.TryGetPosition(flagState2.CarrierPlayerId, out var position2)) { focus = position2; return true; } return false; } private static void CollectCandidates(int playerId, CtfConfig config) { //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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) if (!config.RespawnUsesAllSpawnPoints.Value) { _candidateScene = ""; CtfGame.CollectAssignedSpawnPoints(playerId, _candidates); if (_candidates.Count > 0) { return; } } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name == _candidateScene && _candidates.Count > 0 && AllStillPresent()) { return; } _candidates.Clear(); _candidateScene = name; try { SpawnPoint[] array = Object.FindObjectsOfType(); foreach (SpawnPoint val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null) && ((Component)val).gameObject.activeInHierarchy && CtfGame.IsFinite(((Component)val).transform.position) && !IsDuplicateOf(((Component)val).transform.position)) { _candidates.Add(val); } } } catch (Exception ex) { CtfLog.Debug("Could not enumerate spawn points: " + ex.Message); _candidates.Clear(); } } private static bool IsDuplicateOf(Vector3 position) { //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) foreach (SpawnPoint candidate in _candidates) { if ((Object)(object)candidate == (Object)null || (Object)(object)((Component)candidate).transform == (Object)null || !(Vector3.Distance(((Component)candidate).transform.position, position) < 1.5f)) { continue; } return true; } return false; } private static bool AllStillPresent() { foreach (SpawnPoint candidate in _candidates) { if ((Object)(object)candidate == (Object)null || !((Component)candidate).gameObject.activeInHierarchy) { return false; } } return true; } private static void CollectPlayers(int playerId, int team) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) _enemies.Clear(); _friends.Clear(); foreach (int connectedPlayerId in CtfGame.GetConnectedPlayerIds()) { if (connectedPlayerId != playerId && CtfGame.HasLivingBody(connectedPlayerId) && CtfGame.TryGetPosition(connectedPlayerId, out var position)) { if (CtfGame.GetTeam(connectedPlayerId) == team) { _friends.Add(position); } else { _enemies.Add(position); } } } } private static void MarkUsed(SpawnPoint point) { if (!((Object)(object)point == (Object)null)) { _usedAt[((Object)point).GetInstanceID()] = Time.unscaledTime; } } private static void ForgetOldDeaths(float now) { for (int num = _deaths.Count - 1; num >= 0; num--) { if (now - _deaths[num].At > 12f) { _deaths.RemoveAt(num); } } } private static float NearestDistance(Vector3 position, List others) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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) float num = float.MaxValue; foreach (Vector3 other in others) { float num2 = Vector3.Distance(position, other); if (num2 < num) { num = num2; } } return num; } private static void EnsureHazardTypes() { if (_hazardTypesResolved) { return; } _hazardTypesResolved = true; List list = new List(); List list2 = new List(); Assembly assembly = typeof(PlayerHealth).Assembly; string[] hazardTypeNames = HazardTypeNames; foreach (string text in hazardTypeNames) { Type type = null; try { type = assembly.GetType(text, throwOnError: false); } catch { } if (type != null && typeof(Component).IsAssignableFrom(type)) { list.Add(type); } else { list2.Add(text); } } _hazardTypes = list.ToArray(); if (list2.Count > 0) { CtfLog.Debug("Spawn hazard avoidance could not resolve " + string.Join(", ", list2.ToArray()) + "; respawns will not steer around those."); } } } internal sealed class CtfVisuals { private sealed class FlagVisual { private const float PoleHeight = 1.55f; private readonly Transform _root; private readonly Transform _tilt; private readonly Transform _cloth; private readonly Transform _beam; private readonly Transform _glow; private readonly Transform _finial; private readonly Mesh _clothMesh; private readonly Vector3[] _clothRest; private readonly Vector3[] _clothScratch; private readonly Material _clothMaterial; private readonly Material _beamMaterial; private readonly Material _glowMaterial; private readonly Material _finialMaterial; private readonly Color _color; private Vector3 _smoothedPosition; private bool _positioned; internal FlagVisual(Transform parent, string name, Color color) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: 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_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) _color = color; _root = CtfArt.Node(name, parent).transform; _tilt = CtfArt.Node("Tilt", _root).transform; Material material = CtfArt.Solid(new Color(0.09f, 0.09f, 0.1f)); CtfArt.Renderer("Pole", _tilt, CtfArt.Tube(0.028f, 1.55f, 8), material); _finialMaterial = CtfArt.Solid(color, 2.2f); Transform transform = ((Component)CtfArt.Renderer("Finial", _tilt, CtfArt.Tube(0.055f, 0.12f, 8), _finialMaterial)).transform; transform.localPosition = new Vector3(0f, 1.55f, 0f); _finial = transform; _clothMaterial = CtfArt.Solid(color, 1.35f); _clothMesh = CtfArt.Cloth(9, 6, 0.62f, 0.4f, out _clothRest); _clothScratch = (Vector3[])(object)new Vector3[_clothRest.Length * 2]; Transform transform2 = ((Component)CtfArt.Renderer("Cloth", _tilt, _clothMesh, _clothMaterial)).transform; transform2.localPosition = new Vector3(0.028f, 1.29f, 0f); _cloth = transform2; _beamMaterial = CtfArt.Glow(new Color(color.r, color.g, color.b, 0.16f)); Transform transform3 = ((Component)CtfArt.Renderer("Beam", _root, CtfArt.Beam(0.24f, 22f, 12), _beamMaterial)).transform; _beam = transform3; _glowMaterial = CtfArt.Glow(new Color(color.r, color.g, color.b, 0.3f)); Transform transform4 = ((Component)CtfArt.Renderer("Glow", _root, CtfArt.Disc(0.85f, 24), _glowMaterial)).transform; transform4.localPosition = new Vector3(0f, 0.03f, 0f); _glow = transform4; } internal void Tick(CtfState state, FlagState flag, int teamId, float time, bool showBeam) { //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_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_004c: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) Vector3 val = flag.Position; bool isCarried = flag.IsCarried; if (isCarried && CtfGame.TryGetCarryPoint(flag.CarrierPlayerId, out var position)) { val = position; } if (!_positioned) { _smoothedPosition = val; _positioned = true; } else { Vector3 val2 = val - _smoothedPosition; float num = ((((Vector3)(ref val2)).sqrMagnitude > 25f) ? 1f : (1f - Mathf.Exp(-18f * Time.deltaTime))); _smoothedPosition = Vector3.Lerp(_smoothedPosition, val, num); } _root.position = _smoothedPosition; float num2 = (isCarried ? 0f : (Mathf.Sin(time * 1.7f) * 0.045f)); _tilt.localPosition = new Vector3(0f, num2, 0f); if (isCarried) { _root.rotation = Quaternion.Euler(0f, time * 40f, 0f); _tilt.localRotation = Quaternion.Euler(-12f, 0f, 6f); } else if (flag.IsDropped) { _root.rotation = Quaternion.Euler(0f, time * 18f, 0f); _tilt.localRotation = Quaternion.Euler(20f, 0f, 8f); } else { _root.rotation = Quaternion.Euler(0f, time * 28f, 0f); _tilt.localRotation = Quaternion.identity; } CtfArt.WaveCloth(_clothMesh, _clothRest, _clothScratch, time, isCarried ? 1.6f : 1f); float num3 = ((flag.IsDropped && flag.ReturnIn > 0f) ? Mathf.Clamp01(1f - flag.ReturnIn / 6f) : 0f); float num4 = Mathf.Lerp(1.6f, 9f, num3); float num5 = 0.65f + 0.35f * Mathf.Sin(time * num4); CtfArt.SetColor(_clothMaterial, _color, 0.8f + 0.9f * num5); CtfArt.SetColor(_finialMaterial, _color, 1.4f + 1.2f * num5); bool flag2 = showBeam && !isCarried; if (((Component)_beam).gameObject.activeSelf != flag2) { ((Component)_beam).gameObject.SetActive(flag2); } if (flag2) { _beamMaterial.color = new Color(_color.r, _color.g, _color.b, 0.1f + 0.12f * num5); _beam.localScale = new Vector3(1f, 1f, 1f); } _glowMaterial.color = new Color(_color.r, _color.g, _color.b, isCarried ? 0f : (0.18f + 0.16f * num5)); _glow.localScale = Vector3.one * (1f + 0.08f * num5); } } private sealed class BaseVisual { private readonly Transform _root; private readonly Transform _outerRing; private readonly Transform _innerRing; private readonly Transform _beam; private readonly Material _outerMaterial; private readonly Material _innerMaterial; private readonly Material _beamMaterial; private readonly Color _color; internal BaseVisual(Transform parent, string name, Color color) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: 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) _color = color; _root = CtfArt.Node(name, parent).transform; _outerMaterial = CtfArt.Glow(new Color(color.r, color.g, color.b, 0.5f)); _outerRing = ((Component)CtfArt.Renderer("Outer ring", _root, CtfArt.Ring(1.15f, 1.35f), _outerMaterial)).transform; _outerRing.localPosition = new Vector3(0f, 0.04f, 0f); _innerMaterial = CtfArt.Glow(new Color(color.r, color.g, color.b, 0.32f)); _innerRing = ((Component)CtfArt.Renderer("Inner ring", _root, CtfArt.Ring(0.55f, 0.72f, 36), _innerMaterial)).transform; _innerRing.localPosition = new Vector3(0f, 0.05f, 0f); Material material = CtfArt.Solid(new Color(0.08f, 0.08f, 0.09f)); for (int i = 0; i < 4; i++) { float num = (float)i * (float)Math.PI * 0.5f + (float)Math.PI / 4f; Transform transform = ((Component)CtfArt.Renderer("Post " + i, _root, CtfArt.Tube(0.05f, 0.5f, 6), material)).transform; transform.localPosition = new Vector3(Mathf.Cos(num) * 1.25f, 0f, Mathf.Sin(num) * 1.25f); } _beamMaterial = CtfArt.Glow(new Color(color.r, color.g, color.b, 0.09f)); _beam = ((Component)CtfArt.Renderer("Beam", _root, CtfArt.Beam(1.1f, 16f, 16), _beamMaterial)).transform; } internal void Tick(CtfState state, int teamId, Vector3 position, float time, bool showBeam) { //IL_0007: 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_0088: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) _root.position = position; FlagState flagState = state.FlagForTeam(teamId); bool flag = flagState != null && !flagState.IsHome; float num = (flag ? 6.5f : 2.2f); float num2 = 0.5f + 0.5f * Mathf.Sin(time * num); _outerRing.localRotation = Quaternion.Euler(0f, time * 22f, 0f); _innerRing.localRotation = Quaternion.Euler(0f, (0f - time) * 34f, 0f); _innerRing.localScale = Vector3.one * (0.94f + 0.1f * num2); Color val = (flag ? Color.Lerp(_color, Color.white, 0.35f * num2) : _color); _outerMaterial.color = new Color(val.r, val.g, val.b, 0.32f + 0.28f * num2); _innerMaterial.color = new Color(val.r, val.g, val.b, 0.18f + 0.22f * num2); if (((Component)_beam).gameObject.activeSelf != showBeam) { ((Component)_beam).gameObject.SetActive(showBeam); } if (showBeam) { _beamMaterial.color = new Color(val.r, val.g, val.b, 0.05f + 0.07f * num2); } } } private readonly CtfConfig _config; private FlagVisual _flagA; private FlagVisual _flagB; private BaseVisual _baseA; private BaseVisual _baseB; private GameObject _root; private string _builtSession = ""; private int _builtForTeam = int.MinValue; internal CtfVisuals(CtfConfig config) { _config = config; } internal void Apply(CtfState state) { if (state == null || !state.Active) { Clear(); } else if (!(_builtSession == state.SessionId) || !((Object)(object)_root != (Object)null) || _builtForTeam != LocalTeam()) { Clear(); Build(state); Tick(state); } } private static int LocalTeam() { return CtfGame.GetTeam(CtfGame.LocalPlayerId); } internal void Tick(CtfState state) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if (state == null || !state.Active) { if ((Object)(object)_root != (Object)null) { Clear(); } return; } if (_builtSession != state.SessionId || (Object)(object)_root == (Object)null || _builtForTeam != LocalTeam()) { Apply(state); return; } float time = Time.time; bool value = _config.ShowFlagBeacons.Value; _baseA?.Tick(state, state.TeamA, state.BaseA, time, value); _baseB?.Tick(state, state.TeamB, state.BaseB, time, value); _flagA?.Tick(state, state.FlagA, state.TeamA, time, value); _flagB?.Tick(state, state.FlagB, state.TeamB, time, value); } internal void Clear() { _flagA = null; _flagB = null; _baseA = null; _baseB = null; _builtSession = ""; _builtForTeam = int.MinValue; if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } CtfArt.DisposeAll(); } private void Build(CtfState state) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_00b6: Unknown result type (might be due to invalid IL or missing references) _root = CtfArt.Node("StraftatCTF Visuals", null); _builtSession = state.SessionId; _builtForTeam = LocalTeam(); _baseA = new BaseVisual(_root.transform, "Base A", CtfArt.ColorForTeam(state, state.TeamA)); _baseB = new BaseVisual(_root.transform, "Base B", CtfArt.ColorForTeam(state, state.TeamB)); _flagA = new FlagVisual(_root.transform, "Flag A", CtfArt.ColorForTeam(state, state.TeamA)); _flagB = new FlagVisual(_root.transform, "Flag B", CtfArt.ColorForTeam(state, state.TeamB)); } } [BepInPlugin("com.straftatmods.capturetheflag", "Capture the Flag", "1.0.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class CtfPlugin : BaseUnityPlugin { public const string PluginGuid = "com.straftatmods.capturetheflag"; public const string PluginName = "Capture the Flag"; public const string PluginVersion = "1.0.4"; private static ConfigEntry _verboseLogging; private static bool _sceneHooksInstalled; private CtfConfig _config; private CtfVisuals _visuals; private CtfHud _hud; private CtfAudio _audio; private bool _ready; internal static CtfPlugin Instance { get; private set; } internal static bool VerboseLogging => _verboseLogging != null && _verboseLogging.Value; internal CtfController Controller { get; private set; } internal CtfConfig Settings => _config; internal CtfAudio Audio => _audio; internal static bool ModeActive { get { CtfPlugin instance = Instance; return instance != null && instance._ready && instance.Controller != null && instance.Controller.Active; } } private void Awake() { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) Instance = this; CtfLog.Bind(((BaseUnityPlugin)this).Logger); _verboseLogging = ((BaseUnityPlugin)this).Config.Bind("General", "VerboseLogging", false, "Write extra diagnostic lines to the BepInEx log. Useful when reporting a problem."); _config = new CtfConfig(((BaseUnityPlugin)this).Config); _visuals = new CtfVisuals(_config); _hud = new CtfHud(_config); _audio = new CtfAudio(_config); Controller = new CtfController(this, _config); bool flag = CtfNetwork.EnsureRegistered(); bool flag2 = CtfPatches.Apply(); _ready = flag && flag2; InstallSceneHooks(); CtfRuntime.Ensure(); Controller.ResetForScene(_ready ? "Waiting for a two-team match..." : "Capture the Flag could not start. Check the BepInEx log."); if (_ready) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Capture the Flag 1.0.4 loaded. Host hotkeys: " + ((object)_config.SetBaseAKey.Value/*cast due to .constrained prefix*/).ToString() + " / " + ((object)_config.SetBaseBKey.Value/*cast due to .constrained prefix*/).ToString() + " set bases, " + ((object)_config.ClearBasesKey.Value/*cast due to .constrained prefix*/).ToString() + " clears them.")); } else { ((BaseUnityPlugin)this).Logger.LogError((object)"Capture the Flag 1.0.4 loaded but stayed inactive because a required dependency or game hook is missing."); } } private static void InstallSceneHooks() { if (!_sceneHooksInstalled) { _sceneHooksInstalled = true; SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; } } private unsafe static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) CtfRuntime.Ensure(); CtfPatches.OnSceneLoaded(); CtfLog.Debug("Scene loaded: " + ((Scene)(ref scene)).name + " (" + ((object)(*(LoadSceneMode*)(&mode))/*cast due to .constrained prefix*/).ToString() + ")"); Instance?.HandleSceneLoaded(scene); } private static void OnSceneUnloaded(Scene scene) { CtfLog.Debug("Scene unloaded: " + ((Scene)(ref scene)).name); Instance?.HandleSceneUnloaded(); } private void HandleSceneLoaded(Scene scene) { //IL_000e: 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) CtfLog.Guard("handling a scene load", delegate { _visuals.Clear(); _hud.OnSceneChanged(); _audio.OnSceneChanged(); Controller?.ResetForScene(_ready ? ("Entered " + ((Scene)(ref scene)).name + "; waiting for two teams...") : "Capture the Flag is inactive. Check the BepInEx log."); }); } private void HandleSceneUnloaded() { CtfLog.Guard("handling a scene unload", delegate { _visuals.Clear(); }); } internal void RuntimeUpdate() { if (_ready && Controller != null) { CtfLog.Guard("ticking networking", delegate { CtfNetwork.Instance?.Tick(); }); CtfLog.Guard("ticking CTF rules", Controller.Tick); if (ModeActive) { CtfLog.Guard("holding the CTF round live", CtfGame.EnforceRoundIsLive); } CtfLog.Guard("updating the CTF HUD", delegate { _hud.Tick(Controller); }); CtfLog.Guard("applying local CTF fixups", delegate { CtfLocal.Tick(Controller); }); CtfLog.Guard("reading CTF hotkeys", ReadHotkeys); } } internal void RuntimeLateUpdate() { if (_ready && Controller != null) { CtfLog.Guard("updating CTF visuals", delegate { _visuals.Tick(Controller.State); }); } } internal void OnRuntimeLost() { CtfLog.Guard("recovering from a lost CTF runtime", delegate { _visuals.Clear(); _hud.OnRuntimeLost(); _audio.OnRuntimeLost(); CtfLocal.Reset(); }); } internal void OnStateApplied() { CtfLog.Guard("applying CTF state", delegate { _visuals.Apply(Controller?.State); _hud.OnStateApplied(Controller); }); } internal void OnCtfEvent(int kind, string message) { CtfLog.Guard("presenting a CTF event", delegate { _hud.ShowAnnouncement(kind, message); _audio.Play(kind); }); } private void ReadHotkeys() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (Controller.IsAuthority) { if (Input.GetKeyDown(_config.SetBaseAKey.Value)) { Controller.TrySetHostBase(baseA: true); } else if (Input.GetKeyDown(_config.SetBaseBKey.Value)) { Controller.TrySetHostBase(baseA: false); } else if (Input.GetKeyDown(_config.ClearBasesKey.Value)) { Controller.ClearHostBases(); } } } private void OnDestroy() { CtfLog.Info("CTF plugin component destroyed by the game; networking, runtime and hooks stay active."); } private void OnApplicationQuit() { CtfLog.Guard("shutting down", delegate { CtfRuntime.Shutdown(); CtfPatches.Remove(); }); } }