using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using H3MP;
using H3MP.Networking;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Sodalite;
using Sodalite.Api;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("Niko666")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Lap timing, checkpoints and a persistent leaderboard for the Mugello raceway map, with per-player H3MP sync.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Niko666.RaceLeaderboard")]
[assembly: AssemblyTitle("Mugello Race")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace Niko666.MugelloRace
{
public static class H3MPSync
{
[CompilerGenerated]
private static class <>O
{
public static CustomPacketHandlerReceivedDelegate <0>__OnRegistered;
public static CustomPacketHandler <1>__OnPacket;
}
private const string PacketId = "mugello_lap";
private static Leaderboard _board;
private static Action _onChange;
private static bool _subscribed;
private static bool _registered;
private static int _index = -1;
public static bool Active
{
get
{
try
{
return (Object)(object)Mod.managerObject != (Object)null;
}
catch
{
return false;
}
}
}
public static void Attach(Leaderboard board, Action onChange)
{
_board = board;
_onChange = onChange;
}
public static void Poll()
{
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected O, but got Unknown
if (!Active)
{
if (_registered)
{
MugelloRacePlugin.Log.LogInfo((object)"H3MP session closed, lap sync idle.");
_registered = false;
_index = -1;
}
}
else
{
if (_registered)
{
return;
}
try
{
if (!_subscribed)
{
object obj = <>O.<0>__OnRegistered;
if (obj == null)
{
CustomPacketHandlerReceivedDelegate val = OnRegistered;
<>O.<0>__OnRegistered = val;
obj = (object)val;
}
Mod.CustomPacketHandlerReceived += (CustomPacketHandlerReceivedDelegate)obj;
_subscribed = true;
}
if (Mod.registeredCustomPacketIDs != null && Mod.registeredCustomPacketIDs.TryGetValue("mugello_lap", out var value))
{
Bind(value);
}
else if (ThreadManager.host)
{
Bind(Server.RegisterCustomPacketType("mugello_lap", 0));
}
else
{
ClientSend.RegisterCustomPacketType("mugello_lap");
}
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("H3MP registration failed: " + ex.Message));
_registered = true;
_index = -1;
}
}
}
private static void OnRegistered(string id, int index)
{
if (id == "mugello_lap")
{
Bind(index);
}
}
private static void Bind(int index)
{
//IL_002d: 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_0038: Expected O, but got Unknown
if (index >= 0 && Mod.customPacketHandlers != null && index < Mod.customPacketHandlers.Length)
{
CustomPacketHandler[] customPacketHandlers = Mod.customPacketHandlers;
int num = index;
object obj = <>O.<1>__OnPacket;
if (obj == null)
{
CustomPacketHandler val = OnPacket;
<>O.<1>__OnPacket = val;
obj = (object)val;
}
customPacketHandlers[num] = (CustomPacketHandler)obj;
_index = index;
_registered = true;
MugelloRacePlugin.Log.LogInfo((object)("H3MP lap sync ready (packet index " + index + ", host=" + ThreadManager.host + ")"));
}
}
private static void OnPacket(int clientId, Packet packet)
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Expected O, but got Unknown
try
{
int num = packet.ReadInt(true);
string text = packet.ReadString(true);
float num2 = packet.ReadFloat(true);
long num3 = packet.ReadLong(true);
if (num == GameManager.ID || _board == null || !_board.Merge(text, num2, num3, local: false))
{
return;
}
_board.Save();
if (_onChange != null)
{
_onChange();
}
if (!ThreadManager.host || clientId == 0)
{
return;
}
Packet val = new Packet(_index);
try
{
val.Write(num);
val.Write(text);
val.Write(num2);
val.Write(num3);
ServerSend.SendTCPDataToAll(val, true);
}
finally
{
((IDisposable)val)?.Dispose();
}
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("lap packet failed: " + ex.Message));
}
}
public static void SendLap(string name, float seconds, long ticks)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
if (!Active || _index < 0)
{
return;
}
try
{
Packet val = new Packet(_index);
try
{
val.Write(GameManager.ID);
val.Write(name);
val.Write(seconds);
val.Write(ticks);
if (ThreadManager.host)
{
ServerSend.SendTCPDataToAll(val, true);
}
else
{
ClientSend.SendTCPData(val, true);
}
}
finally
{
((IDisposable)val)?.Dispose();
}
MugelloRacePlugin.Log.LogInfo((object)("lap sent: " + name + " " + seconds.ToString("0.00") + "s"));
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("lap send failed: " + ex.Message));
}
}
}
public class HeadBoard : MonoBehaviour
{
public static HeadBoard Instance;
private TextMesh _text;
private Font _asciiFont;
private Material _depthMat;
private Material _overlayMat;
private Transform _bg;
private Material _bgMatOverlay;
private Material _bgMatDepth;
private float _fitSx = -1f;
private float _fitSy = -1f;
private int _fitQueued;
private float _nextCfgPoll;
private float _fwd = 0.62f;
private float _right = 0.19f;
private float _down = 0.13f;
private bool _visible;
private bool _materialLogged;
private string _pending = "LEADERBOARD";
private static Texture2D _white;
public bool Visible => _visible;
private static bool OnTop
{
get
{
if (MugelloRacePlugin.CfgBoardOnTop != null)
{
return MugelloRacePlugin.CfgBoardOnTop.Value;
}
return true;
}
}
public static HeadBoard Create()
{
//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_0023: Expected O, but got Unknown
if ((Object)(object)Instance != (Object)null)
{
return Instance;
}
GameObject val = new GameObject("LB_HEAD_BOARD");
Object.DontDestroyOnLoad((Object)val);
HeadBoard headBoard = val.AddComponent<HeadBoard>();
headBoard.Build();
Instance = headBoard;
return headBoard;
}
private void Build()
{
//IL_00ec: 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_015e: Expected O, but got Unknown
_text = ((Component)this).gameObject.AddComponent<TextMesh>();
float characterSize = ((MugelloRacePlugin.CfgBoardScale != null) ? MugelloRacePlugin.CfgBoardScale.Value : 0.0026f);
Font val = null;
GameObject val2 = MugelloRacePlugin.FindByName("MR_BOARD_TEXT");
if ((Object)(object)val2 != (Object)null)
{
TextMesh component = val2.GetComponent<TextMesh>();
if ((Object)(object)component != (Object)null)
{
val = component.font;
}
}
if ((Object)(object)val == (Object)null)
{
val = Resources.GetBuiltinResource<Font>("Arial.ttf");
}
Debug.Log((object)("[MugelloRace] head board font: " + (((Object)(object)val != (Object)null) ? ((Object)val).name : "none")));
_asciiFont = val;
_text.font = val;
_text.fontSize = 64;
_text.characterSize = characterSize;
_text.anchor = (TextAnchor)0;
_text.alignment = (TextAlignment)0;
_text.color = new Color(1f, 0.78f, 0.28f);
_text.richText = false;
_text.text = _pending;
MeshRenderer component2 = ((Component)this).gameObject.GetComponent<MeshRenderer>();
if ((Object)(object)component2 != (Object)null && (Object)(object)val != (Object)null)
{
Shader val3 = Shader.Find("Niko666/MugelloText");
if ((Object)(object)val3 != (Object)null)
{
_depthMat = new Material(val.material);
_depthMat.shader = val3;
_depthMat.renderQueue = 2450;
}
_overlayMat = val.material;
((Renderer)component2).sharedMaterial = (((Object)(object)_depthMat != (Object)null) ? _depthMat : _overlayMat);
((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
((Renderer)component2).receiveShadows = false;
}
BuildBackground();
((Component)this).gameObject.SetActive(false);
}
private void BuildBackground()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Expected O, but got Unknown
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Expected O, but got Unknown
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Expected O, but got Unknown
GameObject val = new GameObject("LB_BG");
val.transform.SetParent(((Component)this).transform, false);
val.AddComponent<MeshFilter>().sharedMesh = QuadMesh();
Shader val2 = (((Object)(object)_overlayMat != (Object)null) ? _overlayMat.shader : null);
if ((Object)(object)val2 != (Object)null)
{
_bgMatOverlay = new Material(val2);
_bgMatOverlay.SetTexture("_MainTex", (Texture)(object)WhiteTexture());
_bgMatOverlay.renderQueue = 2440;
}
Shader val3 = Shader.Find("Sprites/Default");
if ((Object)(object)val3 == (Object)null)
{
val3 = Shader.Find("UI/Default");
}
if ((Object)(object)val3 != (Object)null)
{
_bgMatDepth = new Material(val3);
if (_bgMatDepth.HasProperty("_MainTex"))
{
_bgMatDepth.SetTexture("_MainTex", (Texture)(object)WhiteTexture());
}
_bgMatDepth.renderQueue = 2440;
}
if ((Object)(object)_bgMatOverlay == (Object)null && (Object)(object)_bgMatDepth == (Object)null)
{
Debug.LogError((object)"[MugelloRace] board backdrop: no usable shader, plate skipped");
}
MeshRenderer obj = val.AddComponent<MeshRenderer>();
((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
((Renderer)obj).receiveShadows = false;
_bg = val.transform;
ApplyBackground();
}
private static Texture2D WhiteTexture()
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Expected O, but got Unknown
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_white != (Object)null)
{
return _white;
}
_white = new Texture2D(1, 1, (TextureFormat)5, false);
_white.SetPixel(0, 0, Color.white);
_white.Apply();
((Object)_white).hideFlags = (HideFlags)61;
return _white;
}
private static Mesh QuadMesh()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Expected O, but got Unknown
//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)
//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_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)
//IL_007a: 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_009c: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: 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_011f: Unknown result type (might be due to invalid IL or missing references)
Mesh val = new Mesh();
((Object)val).name = "LB_BG_Quad";
val.vertices = (Vector3[])(object)new Vector3[4]
{
new Vector3(-0.5f, -0.5f, 0f),
new Vector3(0.5f, -0.5f, 0f),
new Vector3(0.5f, 0.5f, 0f),
new Vector3(-0.5f, 0.5f, 0f)
};
val.uv = (Vector2[])(object)new Vector2[4]
{
new Vector2(0f, 0f),
new Vector2(1f, 0f),
new Vector2(1f, 1f),
new Vector2(0f, 1f)
};
val.colors = (Color[])(object)new Color[4]
{
Color.white,
Color.white,
Color.white,
Color.white
};
val.triangles = new int[6] { 0, 1, 2, 0, 2, 3 };
val.RecalculateNormals();
val.RecalculateBounds();
return val;
}
private void ApplyBackground()
{
//IL_00d1: 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)
if ((Object)(object)_bg == (Object)null)
{
return;
}
float num = ((MugelloRacePlugin.CfgBoardBackground != null) ? MugelloRacePlugin.CfgBoardBackground.Value : 0.55f);
Material val = (OnTop ? _bgMatOverlay : (((Object)(object)_bgMatDepth != (Object)null) ? _bgMatDepth : _bgMatOverlay));
if (!((Object)(object)val == (Object)null))
{
MeshRenderer component = ((Component)_bg).GetComponent<MeshRenderer>();
if ((Object)(object)component != (Object)null && (Object)(object)((Renderer)component).sharedMaterial != (Object)(object)val)
{
((Renderer)component).sharedMaterial = val;
}
((Component)_bg).gameObject.SetActive(num > 0.001f);
Color val2 = default(Color);
((Color)(ref val2))..ctor(0.03f, 0.03f, 0.03f, num);
if (val.HasProperty("_Color"))
{
val.SetColor("_Color", val2);
}
val.color = val2;
}
}
private void FitBackground()
{
//IL_002f: 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_0037: 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_0057: 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_0070: 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_00da: 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_00f2: 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_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_012d: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_bg == (Object)null || (Object)(object)_text == (Object)null)
{
return;
}
MeshRenderer component = ((Component)this).GetComponent<MeshRenderer>();
if ((Object)(object)component == (Object)null)
{
return;
}
Bounds bounds = ((Renderer)component).bounds;
if (!(((Bounds)(ref bounds)).size.y < 0.0001f))
{
float num = Mathf.Sqrt(((Bounds)(ref bounds)).size.x * ((Bounds)(ref bounds)).size.x + ((Bounds)(ref bounds)).size.z * ((Bounds)(ref bounds)).size.z);
if (!(num < 0.0001f))
{
float num2 = ((MugelloRacePlugin.CfgBoardBgScaleX != null) ? MugelloRacePlugin.CfgBoardBgScaleX.Value : 1f);
float num3 = ((MugelloRacePlugin.CfgBoardBgScaleY != null) ? MugelloRacePlugin.CfgBoardBgScaleY.Value : 1f);
_fitSx = num2;
_fitSy = num3;
Vector3 val = ((Component)this).transform.InverseTransformPoint(((Bounds)(ref bounds)).center);
_bg.localScale = new Vector3(num * num2 + 0.012f, ((Bounds)(ref bounds)).size.y * num3 + 0.004f, 1f);
_bg.localPosition = new Vector3(val.x, val.y, 0.003f);
}
}
}
private void RefreshAsciiFont()
{
GameObject val = MugelloRacePlugin.FindByName("MR_BOARD_TEXT");
if (!((Object)(object)val == (Object)null))
{
TextMesh component = val.GetComponent<TextMesh>();
if (!((Object)(object)component == (Object)null) && !((Object)(object)component.font == (Object)null) && !((Object)(object)component.font == (Object)(object)_asciiFont))
{
_asciiFont = component.font;
ApplyFont(BoardFonts.For(_pending, _asciiFont));
}
}
}
public void SetText(string text)
{
_pending = text;
RefreshAsciiFont();
if (_visible && (Object)(object)_text != (Object)null)
{
ApplyFont(BoardFonts.For(text, _asciiFont));
_text.text = text;
_fitQueued = 2;
}
}
private void ApplyFont(Font f)
{
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)f == (Object)null) && !((Object)(object)_text == (Object)null) && !((Object)(object)_text.font == (Object)(object)f))
{
_text.font = f;
Shader val = Shader.Find("Niko666/MugelloText");
_depthMat = ((!((Object)(object)val != (Object)null) || !((Object)(object)f.material != (Object)null)) ? ((Material)null) : new Material(f.material));
if ((Object)(object)_depthMat != (Object)null)
{
_depthMat.shader = val;
_depthMat.renderQueue = 2450;
}
_overlayMat = f.material;
MeshRenderer component = ((Component)this).GetComponent<MeshRenderer>();
if ((Object)(object)component != (Object)null && (Object)(object)_overlayMat != (Object)null)
{
((Renderer)component).sharedMaterial = (OnTop ? _overlayMat : (((Object)(object)_depthMat != (Object)null) ? _depthMat : _overlayMat));
}
_fitQueued = 2;
}
}
public void Toggle()
{
_visible = !_visible;
((Component)this).gameObject.SetActive(_visible);
RefreshAsciiFont();
if (_visible && (Object)(object)_text != (Object)null)
{
ApplyFont(BoardFonts.For(_pending, _asciiFont));
_text.text = _pending;
PollConfig();
_fitQueued = 2;
Place();
}
}
private void LateUpdate()
{
if (_visible)
{
Place();
if (_fitQueued > 0 && --_fitQueued == 0)
{
FitBackground();
}
if (!(Time.unscaledTime < _nextCfgPoll))
{
_nextCfgPoll = Time.unscaledTime + 1f;
PollConfig();
}
}
}
private void PollConfig()
{
ConfigEntry<float> cfgBoardScale = MugelloRacePlugin.CfgBoardScale;
if (cfgBoardScale != null && (Object)(object)_text != (Object)null && Mathf.Abs(_text.characterSize - cfgBoardScale.Value) > 1E-05f)
{
_text.characterSize = cfgBoardScale.Value;
_fitQueued = 2;
}
float num = ((MugelloRacePlugin.CfgBoardBgScaleX != null) ? MugelloRacePlugin.CfgBoardBgScaleX.Value : 1f);
float num2 = ((MugelloRacePlugin.CfgBoardBgScaleY != null) ? MugelloRacePlugin.CfgBoardBgScaleY.Value : 1f);
if (Mathf.Abs(num - _fitSx) > 0.001f || Mathf.Abs(num2 - _fitSy) > 0.001f)
{
_fitQueued = 2;
}
ConfigEntry<float> cfgBoardForward = MugelloRacePlugin.CfgBoardForward;
ConfigEntry<float> cfgBoardRight = MugelloRacePlugin.CfgBoardRight;
ConfigEntry<float> cfgBoardDown = MugelloRacePlugin.CfgBoardDown;
if (cfgBoardForward != null)
{
_fwd = cfgBoardForward.Value;
}
if (cfgBoardRight != null)
{
_right = cfgBoardRight.Value;
}
if (cfgBoardDown != null)
{
_down = cfgBoardDown.Value;
}
ApplyBackground();
ApplyDepthTest();
}
private void Place()
{
//IL_0044: 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_0055: 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_0060: 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_0070: 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_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_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_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
Transform val;
if ((Object)(object)currentPlayerBody != (Object)null && (Object)(object)currentPlayerBody.Head != (Object)null)
{
val = currentPlayerBody.Head;
}
else
{
Camera main = Camera.main;
if ((Object)(object)main == (Object)null)
{
return;
}
val = ((Component)main).transform;
}
((Component)this).transform.position = val.position + val.forward * _fwd + val.right * _right - val.up * _down;
((Component)this).transform.rotation = Quaternion.LookRotation(((Component)this).transform.position - val.position, val.up);
}
private void ApplyDepthTest()
{
MeshRenderer component = ((Component)this).GetComponent<MeshRenderer>();
if ((Object)(object)component == (Object)null)
{
return;
}
Material val = ((OnTop || (MugelloRacePlugin.CfgBoardDepthTested != null && !MugelloRacePlugin.CfgBoardDepthTested.Value)) ? _overlayMat : (((Object)(object)_depthMat != (Object)null) ? _depthMat : _overlayMat));
if ((Object)(object)val != (Object)null && (Object)(object)((Renderer)component).sharedMaterial != (Object)(object)val)
{
((Renderer)component).sharedMaterial = val;
if (!_materialLogged)
{
_materialLogged = true;
Debug.Log((object)("[MugelloRace] head board material: " + (((Object)(object)val.shader != (Object)null) ? ((Object)val.shader).name : "?") + " onTop=" + OnTop));
}
}
}
private void OnDestroy()
{
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
}
public class LapEntry
{
public string Name;
public float Seconds;
public long Ticks;
public bool Local;
}
public class Leaderboard
{
private readonly string _path;
private readonly List<LapEntry> _entries = new List<LapEntry>();
public string Path_ => _path;
public string FilePath => _path;
public List<LapEntry> Entries => _entries;
public Leaderboard(string dir, string trackId)
{
_path = Path.Combine(dir, (string.IsNullOrEmpty(trackId) || trackId == "mugello") ? "leaderboard.txt" : ("leaderboard-" + Safe(trackId) + ".txt"));
Load();
}
private static string Safe(string s)
{
char[] array = s.ToCharArray();
for (int i = 0; i < array.Length; i++)
{
if (!char.IsLetterOrDigit(array[i]) && array[i] != '-' && array[i] != '_')
{
array[i] = '_';
}
}
return new string(array);
}
private void Load()
{
try
{
if (!File.Exists(_path))
{
return;
}
string[] array = File.ReadAllLines(_path);
foreach (string text in array)
{
if (!string.IsNullOrEmpty(text) && text[0] != '#')
{
string[] array2 = text.Split(new char[1] { '\t' });
if (array2.Length >= 3 && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && long.TryParse(array2[2], out var result2))
{
_entries.Add(new LapEntry
{
Name = array2[0],
Seconds = result,
Ticks = result2
});
}
}
}
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("leaderboard load failed: " + ex.Message));
}
Sort();
}
public void Save()
{
try
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("# name\tseconds\tutcTicks");
foreach (LapEntry entry in _entries)
{
stringBuilder.AppendLine(entry.Name + "\t" + entry.Seconds.ToString("0.000", CultureInfo.InvariantCulture) + "\t" + entry.Ticks);
}
File.WriteAllText(_path, stringBuilder.ToString());
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("leaderboard save failed: " + ex.Message));
}
}
public bool Merge(string name, float seconds, long ticks, bool local)
{
for (int i = 0; i < _entries.Count; i++)
{
if (_entries[i].Ticks == ticks && _entries[i].Name == name)
{
return false;
}
}
_entries.Add(new LapEntry
{
Name = name,
Seconds = seconds,
Ticks = ticks,
Local = local
});
Sort();
int num = ((MugelloRacePlugin.CfgMaxEntries != null) ? MugelloRacePlugin.CfgMaxEntries.Value : 20);
if (_entries.Count > num)
{
_entries.RemoveRange(num, _entries.Count - num);
}
return true;
}
private void Sort()
{
_entries.Sort((LapEntry a, LapEntry b) => a.Seconds.CompareTo(b.Seconds));
}
public float? PersonalBest(string name)
{
float? result = null;
foreach (LapEntry entry in _entries)
{
if (entry.Name == name && (!result.HasValue || entry.Seconds < result.Value))
{
result = entry.Seconds;
}
}
return result;
}
public static string Format(float seconds)
{
if (seconds < 0f)
{
return "--:--.--";
}
int num = (int)(seconds / 60f);
float num2 = seconds - (float)num * 60f;
return num.ToString("00") + ":" + num2.ToString("00.00", CultureInfo.InvariantCulture);
}
}
[BepInPlugin("Niko666.RaceLeaderboard", "Race Leaderboard", "1.0.0")]
[BepInProcess("h3vr.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class MugelloRacePlugin : BaseUnityPlugin
{
private static class WristMenuPatch
{
private const string ButtonText = "Leaderboard";
[HarmonyPatch(typeof(FVRWristMenu2), "Awake")]
[HarmonyPostfix]
public static void AddButton(FVRWristMenu2 __instance)
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Expected O, but got Unknown
try
{
ICollection<WristMenuButton> buttons = WristMenuAPI.CustomButtonsSection.Buttons;
foreach (WristMenuButton item in buttons)
{
if (item != null && item.Text == "Leaderboard")
{
return;
}
}
buttons.Add(new WristMenuButton("Leaderboard", new ButtonClickEvent(OnClick)));
}
catch (Exception ex)
{
Log.LogWarning((object)("wrist menu button failed: " + ex.Message));
}
}
private static void OnClick(object sender, ButtonClickEventArgs args)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
RaceDirector.ToggleHeadBoard();
try
{
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
if ((Object)(object)currentPlayerBody != (Object)null)
{
SM.PlayGlobalUISound((GlobalUISound)1, ((Component)currentPlayerBody).transform.position);
}
}
catch
{
}
}
}
public const string Guid = "Niko666.RaceLeaderboard";
private const string LegacyGuid = "Niko666.MugelloRace";
public static MugelloRacePlugin Instance;
public static ManualLogSource Log;
public static string PluginDir;
public static string DataDir;
public static ConfigEntry<bool> CfgEnabled;
public static ConfigEntry<int> CfgMaxEntries;
public static ConfigEntry<string> CfgName;
public static ConfigEntry<bool> CfgDebugGates;
public static ConfigEntry<float> CfgBoardRight;
public static ConfigEntry<float> CfgBoardForward;
public static ConfigEntry<float> CfgBoardDown;
public static ConfigEntry<float> CfgBoardScale;
public static ConfigEntry<bool> CfgBoardStartVisible;
public static ConfigEntry<bool> CfgBoardDepthTested;
public static ConfigEntry<float> CfgBoardBackground;
public static ConfigEntry<float> CfgBoardBgScaleX;
public static ConfigEntry<float> CfgBoardBgScaleY;
public static ConfigEntry<bool> CfgBoardOnTop;
public static ConfigEntry<bool> CfgCjkFallback;
public static ConfigEntry<string> CfgCjkFont;
public static ConfigEntry<bool> CfgAutoFitPanel;
public static ConfigEntry<float> CfgPanelMargin;
private static string _pendingScene;
private static void SetupDataDir()
{
try
{
string text = Path.Combine(Paths.ConfigPath, "Niko666.RaceLeaderboard");
Directory.CreateDirectory(text);
DataDir = text;
string text2 = Path.Combine(PluginDir, "leaderboard.txt");
string text3 = Path.Combine(text, "leaderboard.txt");
if (File.Exists(text2) && !File.Exists(text3))
{
File.Copy(text2, text3);
}
Log.LogInfo((object)("leaderboard data: " + text));
}
catch (Exception ex)
{
Log.LogWarning((object)("data dir failed, falling back to the plugin folder: " + ex.Message));
DataDir = PluginDir;
}
}
private static void MigrateLegacyConfig()
{
try
{
string configPath = Paths.ConfigPath;
string text = Path.Combine(configPath, "Niko666.MugelloRace.cfg");
string text2 = Path.Combine(configPath, "Niko666.RaceLeaderboard.cfg");
if (File.Exists(text) && !File.Exists(text2))
{
File.Copy(text, text2);
File.AppendAllText(text2, Environment.NewLine + "# migrated from Niko666.MugelloRace.cfg (plugin renamed, GUID unchanged in spirit)" + Environment.NewLine);
}
}
catch
{
}
}
private void Awake()
{
//IL_021d: Unknown result type (might be due to invalid IL or missing references)
//IL_0227: Expected O, but got Unknown
//IL_025b: Unknown result type (might be due to invalid IL or missing references)
//IL_0265: Expected O, but got Unknown
//IL_0299: Unknown result type (might be due to invalid IL or missing references)
//IL_02a3: Expected O, but got Unknown
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
MigrateLegacyConfig();
try
{
PluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
catch (Exception ex)
{
Log.LogWarning((object)("plugin dir unresolved: " + ex.Message));
PluginDir = ".";
}
SetupDataDir();
CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable lap timing on the Mugello map.");
CfgMaxEntries = ((BaseUnityPlugin)this).Config.Bind<int>("Leaderboard", "MaxEntries", 20, "How many leaderboard rows to keep and show.");
CfgName = ((BaseUnityPlugin)this).Config.Bind<string>("Race", "PlayerName", "Niko", "Name shown on the leaderboard.");
CfgDebugGates = ((BaseUnityPlugin)this).Config.Bind<bool>("Race", "DebugGates", false, "Show the nearest gate and your offset from it on the board (for diagnosing checkpoints).");
SceneManager.sceneLoaded += OnSceneLoaded;
try
{
Harmony.CreateAndPatchAll(typeof(WristMenuPatch), (string)null);
}
catch (Exception ex2)
{
Log.LogWarning((object)("wrist menu patch failed (Sodalite missing?): " + ex2.Message));
}
CfgBoardRight = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "OffsetRight", 0.19f, "Head board offset to the player's right, in metres.");
CfgBoardForward = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "OffsetForward", 0.62f, "Head board distance in front of the eyes, in metres.");
CfgBoardDown = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "OffsetDown", 0.13f, "Head board offset below the eye line, in metres.");
CfgBoardScale = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "CharacterSize", 0.0026f, "Text size of the head board (0.0026 = ~1.7 cm glyphs).");
CfgBoardStartVisible = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "StartVisible", false, "Show the head board as soon as a race scene loads (otherwise use the wrist menu button).");
CfgBoardDepthTested = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "DepthTested", true, "Only used when AlwaysOnTop = false: true = hidden by walls/terrain, false = drawn on top.");
CfgBoardBackground = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "BackgroundOpacity", 0.55f, new ConfigDescription("Opacity of the dark plate behind the text (0 = no plate, 1 = solid black).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
CfgBoardBgScaleX = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "BackgroundScaleX", 1f, new ConfigDescription("Backdrop width multiplier (1 = snug around the text).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 3f), new object[0]));
CfgBoardBgScaleY = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "BackgroundScaleY", 1f, new ConfigDescription("Backdrop height multiplier (1 = snug around the text).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 3f), new object[0]));
CfgAutoFitPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "AutoFitBoardPanel", true, "Resize the in-map backing panel (MR_BOARD_BACK) to the rendered text at runtime. false = keep the panel exactly as authored in Unity.");
CfgPanelMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "BoardPanelMargin", 0.25f, "Margin in metres left around the text when AutoFitBoardPanel is on.");
CfgCjkFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "CJKFallback", true, "If the board text contains non-ASCII characters (Chinese etc.), render it with an OS font instead of the LED face - the LED font is ASCII only.");
CfgCjkFont = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "CJKFontName", "Microsoft YaHei UI", "OS font used for the CJK fallback (falls back to Microsoft YaHei / SimHei / SimSun / Segoe UI).");
CfgBoardOnTop = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "AlwaysOnTop", true, "true = the board and its backdrop draw in front of walls and terrain, false = they get occluded.");
Log.LogInfo((object)"Race leaderboard loaded, watching for race scenes.");
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (CfgEnabled.Value)
{
_pendingScene = ((Scene)(ref scene)).name;
((MonoBehaviour)this).StartCoroutine(AttachWhenReady());
}
}
private IEnumerator AttachWhenReady()
{
TrackProfile profile = TrackProfile.LoadForScene(_pendingScene);
for (int i = 0; i < 120; i++)
{
if ((Object)(object)FindByName(profile.StartGate) != (Object)null)
{
if ((Object)(object)Object.FindObjectOfType<RaceDirector>() == (Object)null)
{
GameObject val = new GameObject("RaceDirector");
Object.DontDestroyOnLoad((Object)val);
val.AddComponent<RaceDirector>().Begin(profile);
}
break;
}
yield return (object)new WaitForSeconds(0.25f);
}
}
public static GameObject FindByName(string name)
{
//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)
Transform[] array = Resources.FindObjectsOfTypeAll<Transform>();
foreach (Transform val in array)
{
if (!((Object)(object)val == (Object)null) && !(((Object)val).name != name))
{
Scene scene = ((Component)val).gameObject.scene;
if (((Scene)(ref scene)).IsValid() && ((Component)val).gameObject.activeInHierarchy)
{
return ((Component)val).gameObject;
}
}
}
return null;
}
}
public static class BoardFonts
{
private static Font _cjk;
private static bool _tried;
public static bool NeedsCjk(string text)
{
if (string.IsNullOrEmpty(text))
{
return false;
}
for (int i = 0; i < text.Length; i++)
{
if (text[i] > '~')
{
return true;
}
}
return false;
}
public static Font Cjk()
{
if (_tried)
{
return _cjk;
}
_tried = true;
string text = ((MugelloRacePlugin.CfgCjkFont != null) ? MugelloRacePlugin.CfgCjkFont.Value : "Microsoft YaHei UI");
string[] array = new string[6] { text, "Microsoft YaHei UI", "Microsoft YaHei", "SimHei", "SimSun", "Segoe UI" };
try
{
_cjk = Font.CreateDynamicFontFromOSFont(array, 72);
if (MugelloRacePlugin.Log != null)
{
MugelloRacePlugin.Log.LogInfo((object)("CJK fallback font: " + (((Object)(object)_cjk != (Object)null) ? ((Object)_cjk).name : "none found")));
}
}
catch (Exception ex)
{
if (MugelloRacePlugin.Log != null)
{
MugelloRacePlugin.Log.LogWarning((object)("CJK font failed: " + ex.Message));
}
}
return _cjk;
}
public static Font For(string text, Font asciiFont)
{
if ((MugelloRacePlugin.CfgCjkFallback == null || MugelloRacePlugin.CfgCjkFallback.Value) && NeedsCjk(text))
{
Font val = Cjk();
if ((Object)(object)val != (Object)null)
{
return val;
}
}
return asciiFont;
}
}
public class Gate
{
public Transform T;
public string Label;
public float HalfWidth = 8f;
public bool WasInside;
public float PrevFwd;
public bool HasFwd;
public bool Armed = true;
}
public class RaceDirector : MonoBehaviour
{
private readonly List<Gate> _cps = new List<Gate>();
private Gate _start;
private Gate _finish;
private TextMesh _boardText;
private Font _boardAsciiFont;
private Shader _boardDepthShader;
private Material _boardMat;
private Transform _boardPanel;
private int _panelFitAt;
private Leaderboard _board;
private TrackProfile _profile = new TrackProfile();
private string _name;
private int _nextCp;
private bool _running;
private bool _finished;
private float _lapStart;
private float _lastTime = -1f;
private float _nextBoardRefresh;
private string _status = "";
public static RaceDirector Current;
private const float GateCenterHeight = 1.5f;
private const string ItemTextName = "LB_ITEM_TEXT";
public TrackProfile Profile => _profile;
public static void ToggleHeadBoard()
{
HeadBoard.Create().Toggle();
if ((Object)(object)Current != (Object)null)
{
Current.RefreshBoards();
}
}
public void Begin(TrackProfile profile)
{
Current = this;
_profile = profile ?? new TrackProfile();
GameObject val = MugelloRacePlugin.FindByName(_profile.StartGate);
if ((Object)(object)val == (Object)null)
{
MugelloRacePlugin.Log.LogWarning((object)("start gate '" + _profile.StartGate + "' not found"));
return;
}
_start = MakeGate(val, "START");
GameObject val2 = MugelloRacePlugin.FindByName(_profile.EffectiveFinish);
_finish = (((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)val) ? _start : MakeGate(val2, "FINISH"));
for (int i = 0; i < _profile.MaxCheckpoints; i++)
{
GameObject val3 = MugelloRacePlugin.FindByName(_profile.CheckpointName(i));
if ((Object)(object)val3 == (Object)null)
{
break;
}
_cps.Add(MakeGate(val3, "CP" + i));
}
_board = new Leaderboard(MugelloRacePlugin.DataDir ?? MugelloRacePlugin.PluginDir, _profile.Id);
_name = MugelloRacePlugin.CfgName.Value;
BindBoard();
HeadBoard headBoard = HeadBoard.Create();
if (MugelloRacePlugin.CfgBoardStartVisible.Value && !headBoard.Visible)
{
headBoard.Toggle();
}
MugelloRacePlugin.Log.LogInfo((object)("race ready: " + _profile.DisplayName + " " + _profile.Mode.ToString() + ", " + _cps.Count + " checkpoints, board " + _board.FilePath));
H3MPSync.Attach(_board, RefreshBoards);
}
private Gate MakeGate(GameObject go, string label)
{
return new Gate
{
T = go.transform,
Label = label,
HalfWidth = HalfWidthOf(go.transform),
Armed = true
};
}
private void BindBoard()
{
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
//IL_0185: Expected O, but got Unknown
if (string.IsNullOrEmpty(_profile.BoardText))
{
return;
}
GameObject val = MugelloRacePlugin.FindByName(_profile.BoardText);
if ((Object)(object)val == (Object)null)
{
MugelloRacePlugin.Log.LogInfo((object)("no in-map board '" + _profile.BoardText + "', portable only."));
return;
}
_boardText = val.GetComponent<TextMesh>();
if ((Object)(object)_boardText == (Object)null)
{
return;
}
GameObject val2 = MugelloRacePlugin.FindByName("MR_BOARD_BACK");
_boardPanel = (((Object)(object)val2 != (Object)null) ? val2.transform : null);
Font val3 = (((Object)(object)_boardText.font != (Object)null) ? _boardText.font : Resources.GetBuiltinResource<Font>("Arial.ttf"));
if ((Object)(object)val3 == (Object)null)
{
return;
}
_boardText.font = val3;
_boardAsciiFont = val3;
MeshRenderer component = ((Component)_boardText).GetComponent<MeshRenderer>();
Shader val4 = null;
if ((Object)(object)component != (Object)null && (Object)(object)((Renderer)component).sharedMaterial != (Object)null)
{
val4 = ((Renderer)component).sharedMaterial.shader;
}
if ((Object)(object)val4 == (Object)null || ((Object)val4).name == "Hidden/InternalErrorShader")
{
val4 = Shader.Find("Niko666/MugelloText");
}
if ((Object)(object)val4 != (Object)null && ((Object)val4).name == "Hidden/InternalErrorShader")
{
val4 = null;
}
if ((Object)(object)val4 != (Object)null && (Object)(object)val3.material != (Object)null)
{
_boardDepthShader = val4;
_boardMat = new Material(val3.material);
((Object)_boardMat).name = "MugelloBoardFont_Runtime";
_boardMat.shader = val4;
if ((Object)(object)component != (Object)null)
{
((Renderer)component).sharedMaterial = _boardMat;
}
MugelloRacePlugin.Log.LogInfo((object)("board shader: " + ((Object)val4).name));
}
else
{
MugelloRacePlugin.Log.LogWarning((object)"depth-tested text shader missing - board may draw through walls.");
}
RefreshBoards();
}
private void FitPanel()
{
//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_004e: 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_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: 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_00e6: 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_0133: Unknown result type (might be due to invalid IL or missing references)
//IL_0146: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: 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_0176: 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_0183: Unknown result type (might be due to invalid IL or missing references)
//IL_018a: Unknown result type (might be due to invalid IL or missing references)
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_boardText == (Object)null || (Object)(object)_boardPanel == (Object)null)
{
return;
}
ConfigEntry<bool> cfgAutoFitPanel = MugelloRacePlugin.CfgAutoFitPanel;
if (cfgAutoFitPanel != null && !cfgAutoFitPanel.Value)
{
return;
}
MeshRenderer component = ((Component)_boardText).GetComponent<MeshRenderer>();
if ((Object)(object)component == (Object)null)
{
return;
}
Bounds bounds = ((Renderer)component).bounds;
if (((Bounds)(ref bounds)).size.y < 0.01f)
{
return;
}
float num = Mathf.Sqrt(((Bounds)(ref bounds)).size.x * ((Bounds)(ref bounds)).size.x + ((Bounds)(ref bounds)).size.z * ((Bounds)(ref bounds)).size.z);
if (!(num < 0.01f))
{
Transform val = (((Object)(object)_boardPanel.parent != (Object)null) ? _boardPanel.parent : _boardPanel);
Vector3 lossyScale = val.lossyScale;
if (!(Mathf.Abs(lossyScale.x) < 0.0001f) && !(Mathf.Abs(lossyScale.y) < 0.0001f))
{
float num2 = ((MugelloRacePlugin.CfgPanelMargin != null) ? MugelloRacePlugin.CfgPanelMargin.Value : 0.25f);
_boardPanel.localScale = new Vector3((num + num2 * 2f) / Mathf.Abs(lossyScale.x), (((Bounds)(ref bounds)).size.y + num2 * 2f) / Mathf.Abs(lossyScale.y), _boardPanel.localScale.z);
Vector3 val2 = val.InverseTransformPoint(((Bounds)(ref bounds)).center);
_boardPanel.localPosition = new Vector3(val2.x, val2.y, _boardPanel.localPosition.z);
}
}
}
private void ApplyBoardFont(string text)
{
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Expected O, but got Unknown
if ((Object)(object)_boardText == (Object)null)
{
return;
}
Font val = BoardFonts.For(text, _boardAsciiFont);
if ((Object)(object)val == (Object)null || (Object)(object)_boardText.font == (Object)(object)val)
{
return;
}
_boardText.font = val;
MeshRenderer component = ((Component)_boardText).GetComponent<MeshRenderer>();
if ((Object)(object)component != (Object)null && (Object)(object)val.material != (Object)null)
{
_boardMat = new Material(val.material);
((Object)_boardMat).name = "MugelloBoardFont_Runtime";
if ((Object)(object)_boardDepthShader != (Object)null)
{
_boardMat.shader = _boardDepthShader;
}
((Renderer)component).sharedMaterial = _boardMat;
}
}
private void Update()
{
//IL_009a: 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_00a7: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
try
{
H3MPSync.Poll();
if (_panelFitAt > 0 && Time.frameCount >= _panelFitAt)
{
_panelFitAt = 0;
try
{
FitPanel();
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("panel fit: " + ex.Message));
}
}
if (_start == null || (Object)(object)_start.T == (Object)null)
{
Object.Destroy((Object)(object)((Component)this).gameObject);
return;
}
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.Head == (Object)null)
{
return;
}
Vector3 position = currentPlayerBody.Head.position;
if (Crossed(_start, position))
{
OnStartCrossed();
}
if (_finish != _start && Crossed(_finish, position))
{
OnFinishCrossed();
}
for (int i = 0; i < _cps.Count; i++)
{
if (Crossed(_cps[i], position))
{
OnCheckpoint(i);
}
}
if (Time.unscaledTime >= _nextBoardRefresh)
{
_nextBoardRefresh = Time.unscaledTime + 0.2f;
RefreshBoards();
}
}
catch (Exception ex2)
{
MugelloRacePlugin.Log.LogError((object)("race update: " + ex2));
((Behaviour)this).enabled = false;
}
}
private static float HalfWidthOf(Transform t)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
float num = Mathf.Abs(t.lossyScale.x) * 0.5f;
if (!(num > 1f))
{
return 12f;
}
return num;
}
private bool Crossed(Gate g, Vector3 head)
{
//IL_0013: 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_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: 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_003b: 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)
if (g == null || (Object)(object)g.T == (Object)null)
{
return false;
}
Vector3 val = head - g.T.position;
float num = Mathf.Abs(Vector3.Dot(val, g.T.right));
float num2 = Mathf.Abs(val.y - 1.5f);
float num3 = Vector3.Dot(val, g.T.forward);
bool flag = num <= g.HalfWidth && num2 <= 3f && Mathf.Abs(num3) <= 1.6f;
bool flag2 = g.Armed && g.HasFwd && g.PrevFwd < 0f && num3 >= 0f && num <= g.HalfWidth && num2 <= 3f;
bool num4 = (flag && !g.WasInside) || flag2;
if (num4)
{
g.Armed = false;
}
else if (num3 < -0.3f)
{
g.Armed = true;
}
g.WasInside = flag;
g.PrevFwd = num3;
g.HasFwd = true;
return num4;
}
private void OnStartCrossed()
{
float realtimeSinceStartup = Time.realtimeSinceStartup;
if (!_running)
{
StartRun(realtimeSinceStartup, (_profile.IsSprint ? "Run started - " : "Lap started - ") + _cps.Count + " checkpoints");
return;
}
if (_finish == _start)
{
if (_nextCp >= _cps.Count)
{
Record(realtimeSinceStartup);
}
else
{
StartRun(realtimeSinceStartup, "Restarted: " + _nextCp + "/" + _cps.Count);
}
}
else
{
StartRun(realtimeSinceStartup, "Restarted: " + _nextCp + "/" + _cps.Count);
}
RefreshBoards();
}
private void OnFinishCrossed()
{
if (_running)
{
if (_nextCp < _cps.Count)
{
_status = "Finish ignored: " + _nextCp + "/" + _cps.Count + " checkpoints";
RefreshBoards();
}
else
{
Record(Time.realtimeSinceStartup);
RefreshBoards();
}
}
}
private void Record(float now)
{
float seconds = (_lastTime = now - _lapStart);
long ticks = DateTime.UtcNow.Ticks;
if (_board.Merge(_name, seconds, ticks, local: true))
{
_board.Save();
H3MPSync.SendLap(_name, seconds, ticks);
_status = _profile.DisplayName + " " + Leaderboard.Format(seconds) + " recorded";
}
else
{
_status = "duplicate ignored";
}
if (_profile.IsSprint)
{
_running = false;
_finished = true;
}
else
{
StartRun(now, _status);
}
}
private void StartRun(float now, string status)
{
_running = true;
_finished = false;
_lapStart = now;
_nextCp = 0;
_status = status;
}
private void OnCheckpoint(int index)
{
if (_running && index == _nextCp)
{
_nextCp++;
MugelloRacePlugin.Log.LogInfo((object)("checkpoint " + index + " ok (" + _nextCp + "/" + _cps.Count + ")"));
if (_nextCp == _cps.Count)
{
_status = "All checkpoints - cross the finish!";
}
}
}
private static string Fit(string line, int width)
{
if (line.Length > width)
{
return line.Substring(0, width - 3) + "...";
}
return line;
}
public void RefreshBoards()
{
if ((Object)(object)_boardText != (Object)null)
{
string text = ComposeBoard();
ApplyBoardFont(text);
_boardText.text = text;
if ((Object)(object)_boardMat != (Object)null && (Object)(object)_boardText.font != (Object)null && (Object)(object)_boardText.font.material != (Object)null)
{
_boardMat.SetTexture("_MainTex", _boardText.font.material.mainTexture);
}
_panelFitAt = Time.frameCount + 2;
}
HeadBoard instance = HeadBoard.Instance;
if ((Object)(object)instance != (Object)null)
{
instance.SetText(ComposePortable());
}
RefreshHandheldItems();
}
private void RefreshHandheldItems()
{
//IL_0035: 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_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)
string text = null;
TextMesh[] array = Resources.FindObjectsOfTypeAll<TextMesh>();
Scene scene;
foreach (TextMesh val in array)
{
if ((Object)(object)val == (Object)null || ((Object)val).name != "LB_ITEM_TEXT")
{
continue;
}
scene = ((Component)val).gameObject.scene;
if (((Scene)(ref scene)).IsValid())
{
if (text == null)
{
text = ComposePortable();
}
val.text = text;
}
}
Text[] array2 = Resources.FindObjectsOfTypeAll<Text>();
foreach (Text val2 in array2)
{
if ((Object)(object)val2 == (Object)null || ((Object)val2).name != "LB_ITEM_TEXT")
{
continue;
}
scene = ((Component)val2).gameObject.scene;
if (((Scene)(ref scene)).IsValid())
{
if (text == null)
{
text = ComposePortable();
}
val2.text = text;
}
}
}
private string ModeTitle()
{
return _profile.DisplayName + (_profile.IsSprint ? " - SPRINT" : " - LAP BOARD");
}
private string ComposeBoard()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(ModeTitle());
stringBuilder.AppendLine();
int num = 0;
List<LapEntry> list = ((_board != null) ? _board.Entries : new List<LapEntry>());
int num2 = 0;
while (num2 < list.Count && num < 10)
{
stringBuilder.AppendLine(Fit((num2 + 1).ToString().PadLeft(2) + " " + Leaderboard.Format(list[num2].Seconds) + " " + list[num2].Name, 34));
num2++;
num++;
}
if (num == 0)
{
stringBuilder.AppendLine(" no times recorded yet");
}
stringBuilder.AppendLine();
float? num3 = ((_board != null) ? _board.PersonalBest(_name) : ((float?)null));
stringBuilder.AppendLine(Fit("YOU " + _name + " BEST " + (num3.HasValue ? Leaderboard.Format(num3.Value) : "--:--.--"), 34));
if (_running)
{
stringBuilder.AppendLine((_profile.IsSprint ? "RUN " : "LAP ") + Leaderboard.Format(Time.realtimeSinceStartup - _lapStart) + " CP " + _nextCp + "/" + _cps.Count);
}
else if (_finished)
{
stringBuilder.AppendLine("finished " + Leaderboard.Format(_lastTime) + " - cross the start again");
}
else
{
stringBuilder.AppendLine("cross the start gate to begin");
}
if (!string.IsNullOrEmpty(_status))
{
stringBuilder.AppendLine(Fit(_status, 40));
}
if (H3MPSync.Active)
{
stringBuilder.AppendLine("H3MP: per-player timing on");
}
if (MugelloRacePlugin.CfgDebugGates.Value)
{
stringBuilder.AppendLine(DebugLine());
}
return stringBuilder.ToString();
}
private string ComposePortable()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(_profile.DisplayName + (_profile.IsSprint ? " SPRINT" : " LAP"));
int num = 0;
List<LapEntry> list = ((_board != null) ? _board.Entries : new List<LapEntry>());
int num2 = 0;
while (num2 < list.Count && num < 5)
{
stringBuilder.AppendLine(num2 + 1 + " " + Leaderboard.Format(list[num2].Seconds) + " " + Fit(list[num2].Name, 8));
num2++;
num++;
}
if (num == 0)
{
stringBuilder.AppendLine("no times yet");
}
if (_running)
{
stringBuilder.AppendLine("CP " + _nextCp + "/" + _cps.Count + " " + Leaderboard.Format(Time.realtimeSinceStartup - _lapStart));
}
else if (_finished)
{
stringBuilder.AppendLine("done " + Leaderboard.Format(_lastTime));
}
else
{
stringBuilder.AppendLine("cross start");
}
return stringBuilder.ToString();
}
private void OnDestroy()
{
if ((Object)(object)Current == (Object)(object)this)
{
Current = null;
}
}
private string DebugLine()
{
//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_0082: 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_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
if (_cps.Count == 0)
{
return "dbg no checkpoints";
}
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.Head == (Object)null)
{
return "dbg no player";
}
Vector3 position = currentPlayerBody.Head.position;
int num = -1;
float num2 = float.MaxValue;
float num3 = 0f;
float num4 = 0f;
float num5 = 0f;
for (int i = 0; i < _cps.Count; i++)
{
Transform t = _cps[i].T;
if (!((Object)(object)t == (Object)null))
{
Vector3 val = position - t.position;
float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
if (!(sqrMagnitude >= num2))
{
num2 = sqrMagnitude;
num = i;
num3 = Vector3.Dot(val, t.right);
num4 = val.y - 1.5f;
num5 = Vector3.Dot(val, t.forward);
}
}
}
if (num < 0)
{
return "dbg no gate";
}
return "dbg CP" + num + " lat" + num3.ToString("0.0") + " up" + num4.ToString("0.0") + " fwd" + num5.ToString("0.0") + " hw" + _cps[num].HalfWidth.ToString("0.0");
}
}
public enum RaceMode
{
Circuit,
Sprint
}
public class TrackProfile
{
public string Id = "mugello";
public string DisplayName = "MUGELLO";
public RaceMode Mode;
public string StartGate = "MR_START";
public string FinishGate = "";
public string CheckpointFormat = "MR_CP_{0:00}";
public string BoardText = "MR_BOARD_TEXT";
public string PortableAnchor = "";
public int MaxCheckpoints = 200;
public bool PortableBoard = true;
public bool FromFile;
public string EffectiveFinish
{
get
{
if (!string.IsNullOrEmpty(FinishGate))
{
return FinishGate;
}
return StartGate;
}
}
public bool IsSprint => Mode == RaceMode.Sprint;
public static string TracksDir => Path.Combine(MugelloRacePlugin.PluginDir, "tracks");
public string CheckpointName(int index)
{
try
{
return string.Format(CultureInfo.InvariantCulture, CheckpointFormat, new object[1] { index });
}
catch
{
return "CP" + index;
}
}
public static List<string> SearchDirs()
{
List<string> dirs = new List<string>();
Add(TracksDir);
try
{
string directoryName = Path.GetDirectoryName(MugelloRacePlugin.PluginDir);
if (!string.IsNullOrEmpty(directoryName) && Directory.Exists(directoryName))
{
string[] directories = Directory.GetDirectories(directoryName);
foreach (string obj in directories)
{
Add(Path.Combine(obj, "tracks"));
Add(obj);
}
}
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("track search failed: " + ex.Message));
}
return dirs;
void Add(string d)
{
if (!string.IsNullOrEmpty(d) && Directory.Exists(d) && !dirs.Contains(d))
{
dirs.Add(d);
}
}
}
public static TrackProfile LoadForScene(string sceneName)
{
TrackProfile trackProfile = Auto();
List<string> list = new List<string>();
if (!string.IsNullOrEmpty(sceneName))
{
foreach (string item in SearchDirs())
{
list.Add(Path.Combine(item, sceneName + ".json"));
}
}
list.Add(Path.Combine(TracksDir, "default.json"));
foreach (string item2 in list)
{
if (File.Exists(item2))
{
try
{
Parse(File.ReadAllText(item2), trackProfile);
trackProfile.FromFile = true;
MugelloRacePlugin.Log.LogInfo((object)("track profile: " + item2));
return trackProfile;
}
catch (Exception ex)
{
MugelloRacePlugin.Log.LogWarning((object)("track profile " + item2 + " failed: " + ex.Message));
}
}
}
MugelloRacePlugin.Log.LogInfo((object)("track profile: built-in defaults for '" + sceneName + "'"));
return trackProfile;
}
private static TrackProfile Auto()
{
return new TrackProfile();
}
private static void Parse(string text, TrackProfile p)
{
int num = 0;
while (num < text.Length)
{
int num2 = text.IndexOf('"', num);
if (num2 < 0)
{
break;
}
int num3 = text.IndexOf('"', num2 + 1);
if (num3 < 0)
{
break;
}
string key = text.Substring(num2 + 1, num3 - num2 - 1);
int num4 = text.IndexOf(':', num3 + 1);
if (num4 < 0)
{
break;
}
int i;
for (i = num4 + 1; i < text.Length && char.IsWhiteSpace(text[i]); i++)
{
}
string value;
if (i < text.Length && text[i] == '"')
{
int num5 = text.IndexOf('"', i + 1);
if (num5 < 0)
{
break;
}
value = text.Substring(i + 1, num5 - i - 1);
num = num5 + 1;
}
else
{
int j;
for (j = i; j < text.Length && text[j] != ',' && text[j] != '}' && text[j] != '\n'; j++)
{
}
value = text.Substring(i, j - i).Trim();
num = j;
}
Apply(key, value, p);
}
}
private static void Apply(string key, string value, TrackProfile p)
{
if (key == null)
{
return;
}
switch (key.Length)
{
case 9:
switch (key[0])
{
case 's':
if (key == "startGate")
{
p.StartGate = value;
}
break;
case 'b':
if (key == "boardText")
{
p.BoardText = value;
}
break;
}
break;
case 14:
switch (key[0])
{
case 'p':
if (key == "portableAnchor")
{
p.PortableAnchor = value;
}
break;
case 'm':
if (key == "maxCheckpoints")
{
p.MaxCheckpoints = ParseInt(value, p.MaxCheckpoints);
}
break;
}
break;
case 2:
if (key == "id")
{
p.Id = value;
}
break;
case 11:
if (key == "displayName")
{
p.DisplayName = value;
}
break;
case 4:
if (key == "mode")
{
p.Mode = (string.Equals(value, "sprint", StringComparison.OrdinalIgnoreCase) ? RaceMode.Sprint : RaceMode.Circuit);
}
break;
case 10:
if (key == "finishGate")
{
p.FinishGate = value;
}
break;
case 16:
if (key == "checkpointFormat")
{
p.CheckpointFormat = value;
}
break;
case 13:
if (key == "portableBoard")
{
p.PortableBoard = value == "true" || value == "1";
}
break;
case 3:
case 5:
case 6:
case 7:
case 8:
case 12:
case 15:
break;
}
}
private static int ParseInt(string s, int fallback)
{
if (!int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
{
return fallback;
}
return result;
}
private static float ParseFloat(string s, float fallback)
{
if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
return fallback;
}
return result;
}
public void WriteTemplate(string path)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("{");
stringBuilder.AppendLine(" \"id\": \"" + Id + "\",");
stringBuilder.AppendLine(" \"displayName\": \"" + DisplayName + "\",");
stringBuilder.AppendLine(" \"mode\": \"" + (IsSprint ? "sprint" : "circuit") + "\",");
stringBuilder.AppendLine(" \"startGate\": \"" + StartGate + "\",");
stringBuilder.AppendLine(" \"finishGate\": \"" + FinishGate + "\",");
stringBuilder.AppendLine(" \"checkpointFormat\": \"" + CheckpointFormat + "\",");
stringBuilder.AppendLine(" \"boardText\": \"" + BoardText + "\",");
stringBuilder.AppendLine(" \"portableAnchor\": \"" + PortableAnchor + "\",");
stringBuilder.AppendLine(" \"portableBoard\": " + (PortableBoard ? "true" : "false"));
stringBuilder.AppendLine("}");
File.WriteAllText(path, stringBuilder.ToString());
}
}
}