using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Speedometer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Adds a speedometer to the HUD while driving")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2+e5e5718e0d4ae6ee6fcf6d42d06b740971850bf3")]
[assembly: AssemblyProduct("Speedometer")]
[assembly: AssemblyTitle("Speedometer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 Speedometer
{
[BepInPlugin("Speedometer", "Speedometer", "1.1.2")]
public class Plugin : BaseUnityPlugin
{
internal enum Align
{
Start,
Center,
End
}
internal enum SpeedoType
{
Analog,
Digital
}
internal struct DigitalConfig
{
public ConfigEntry<string> format;
public ConfigEntry<Align> textAlign;
}
internal struct AnalogConfig
{
public ConfigEntry<string> background;
public ConfigEntry<float> maxSpeed;
public ConfigEntry<float> maxPointX;
public ConfigEntry<float> maxPointY;
public ConfigEntry<float> zeroPointX;
public ConfigEntry<float> zeroPointY;
public ConfigEntry<float> needleX;
public ConfigEntry<float> needleY;
public ConfigEntry<float> needleWidth;
public ConfigEntry<float> lengthFactor;
public ConfigEntry<float> backLength;
}
internal struct SpeedoConfig
{
public ConfigEntry<int> x;
public ConfigEntry<int> y;
public ConfigEntry<Align> alignX;
public ConfigEntry<Align> alignY;
public ConfigEntry<float> factor;
public ConfigEntry<SpeedoType> type;
public DigitalConfig digital;
public AnalogConfig analog;
}
internal class SpeedoGauge
{
private Texture2D atlas;
private Texture2D needleOverlay;
private Texture2D clearTexture;
private Vector2Int dimensions;
private Vector2 center;
private Vector2 needleStart;
private Vector2 needleEnd;
private float needleWidth = 1.3f;
private float length;
private float back;
public Vector2Int Dimensions => dimensions;
private static void SetPixel(Texture2D t, int x, int y, Color c)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
t.SetPixel(x, ((Texture)t).height - y, c);
}
private static Color GetPixel(Texture2D t, int x, int y)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
return t.GetPixel(x, ((Texture)t).height - y);
}
public SpeedoGauge(string atlasImage, Vector2 needleStart, Vector2 needleEnd, Vector2 center, float width, float length, float back)
{
//IL_0036: 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_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Expected O, but got Unknown
//IL_00aa: 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_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)
//IL_00b4: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: 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_00cd: 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_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Expected O, but got Unknown
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
atlas = LoadImage(atlasImage);
dimensions = new Vector2Int(((Texture)atlas).width, ((Texture)atlas).height);
clearTexture = new Texture2D(((Vector2Int)(ref dimensions)).x, ((Vector2Int)(ref dimensions)).y);
for (int i = 0; i < ((Vector2Int)(ref dimensions)).x; i++)
{
for (int j = 0; j < ((Vector2Int)(ref dimensions)).y; j++)
{
SetPixel(clearTexture, i, j, Color.clear);
}
}
needleStart -= center;
needleEnd -= center;
this.needleEnd = needleEnd;
this.needleStart = needleStart;
this.center = center;
needleWidth = width;
this.length = length;
this.back = back;
needleOverlay = new Texture2D(((Vector2Int)(ref dimensions)).x, ((Vector2Int)(ref dimensions)).y);
}
public void Draw(MiniRenderer R, Vector2Int position, float speedFactor)
{
//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_0066: 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_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_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_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
R.spr((Texture)(object)atlas, 0f, 0f, (float)((Vector2Int)(ref position)).x, (float)((Vector2Int)(ref position)).y, (float)((Vector2Int)(ref dimensions)).x, (float)((Vector2Int)(ref dimensions)).y, false, (float)((Vector2Int)(ref dimensions)).x, (float)((Vector2Int)(ref dimensions)).y);
Vector2 val = ClockwiseLerp(needleStart, needleEnd, speedFactor);
Vector2 w = center + val * length;
needleOverlay.CopyPixels((Texture)(object)clearTexture);
for (int i = 0; i < ((Vector2Int)(ref dimensions)).x; i++)
{
for (int j = 0; j < ((Vector2Int)(ref dimensions)).y; j++)
{
float num = minimum_distance(center - ((Vector2)(ref val)).normalized * back, w, new Vector2((float)i, (float)j));
if (num < needleWidth)
{
SetPixel(needleOverlay, i, j, Color.white);
}
}
}
needleOverlay.Apply();
R.spr((Texture)(object)needleOverlay, 0f, 0f, (float)((Vector2Int)(ref position)).x, (float)((Vector2Int)(ref position)).y, (float)((Vector2Int)(ref dimensions)).x, (float)((Vector2Int)(ref dimensions)).y, false, (float)((Vector2Int)(ref dimensions)).x, (float)((Vector2Int)(ref dimensions)).y);
}
private static float minimum_distance(Vector2 v, Vector2 w, Vector2 p)
{
//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_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_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_003b: 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_0042: 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_005b: 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)
//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)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: 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_0024: 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)
Vector2 val = w - v;
float sqrMagnitude = ((Vector2)(ref val)).sqrMagnitude;
if ((double)sqrMagnitude == 0.0)
{
return Vector2.Distance(p, v);
}
float num = Mathf.Max(0f, Mathf.Min(1f, Vector2.Dot(p - v, w - v) / sqrMagnitude));
Vector2 val2 = v + num * (w - v);
return Vector2.Distance(p, val2);
}
private static Vector2 ClockwiseLerp(Vector2 start, Vector2 end, float t)
{
//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_0013: 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_004b: 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_006a: 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_006f: Unknown result type (might be due to invalid IL or missing references)
float num = Mathf.Atan2(start.y, start.x);
float num2 = Mathf.Atan2(end.y, end.x);
if (num > num2)
{
num -= MathF.PI * 2f;
}
float num3 = num + (num2 - num) * t;
return new Vector2(Mathf.Cos(num3), Mathf.Sin(num3)) * Mathf.Lerp(((Vector2)(ref start)).magnitude, ((Vector2)(ref end)).magnitude, t);
}
}
internal static ManualLogSource Logger;
private static SpeedoGauge gauge;
private static ConfigEntry<bool> useWheelSpeed;
private static ConfigEntry<string> speedoConfigName;
private static ConfigFile config;
private static string configPath = Utility.CombinePaths(new string[2]
{
Paths.ConfigPath,
"Speedometer"
});
private static SpeedoConfig sConfig;
private static List<string> configs = new List<string>();
private static void LoadConfig(string name)
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected O, but got Unknown
//IL_0355: Unknown result type (might be due to invalid IL or missing references)
//IL_037a: Unknown result type (might be due to invalid IL or missing references)
//IL_039f: Unknown result type (might be due to invalid IL or missing references)
ConfigFile val = new ConfigFile(Utility.CombinePaths(new string[2]
{
configPath,
name + ".spdo.cfg"
}), false);
Logger.LogInfo((object)("Loading config " + val.ConfigFilePath));
SpeedoConfig speedoConfig = default(SpeedoConfig);
speedoConfig.x = val.Bind<int>("general", "x", 16, "X position of the speedometer");
speedoConfig.y = val.Bind<int>("general", "y", -54, "Y position of the speedometer");
speedoConfig.alignX = val.Bind<Align>("general", "alignX", Align.Center, "X alignment, Start = from left side, Center = from center, End = from right side");
speedoConfig.alignY = val.Bind<Align>("general", "alignY", Align.End, "Y alignment, Start = from top, Center = from center, End = from bottom");
speedoConfig.factor = val.Bind<float>("general", "factor", 3.6f, "Multiplication factor for the displayed speed, 1 = meters per second");
speedoConfig.type = val.Bind<SpeedoType>("general", "type", SpeedoType.Digital, "Speedometer type");
switch (speedoConfig.type.Value)
{
case SpeedoType.Digital:
speedoConfig.digital = default(DigitalConfig);
speedoConfig.digital.format = val.Bind<string>("digital", "format", "{0:f0} km/h", "Format string for the display, check C# String.Format docs for options");
speedoConfig.digital.textAlign = val.Bind<Align>("digital", "textAlign", Align.End, "Text alignment, Start = Text starts at position, Center = Center of text is at position, End = End of text is at position");
break;
case SpeedoType.Analog:
speedoConfig.analog = default(AnalogConfig);
speedoConfig.analog.background = val.Bind<string>("analog", "background", "", "Image to use for the speedometer");
speedoConfig.analog.maxSpeed = val.Bind<float>("analog", "maxSpeed", 36f, "Speed where the needle should be at the maxPoint");
speedoConfig.analog.maxPointX = val.Bind<float>("analog", "maxPointX", 0f, "X coordinate where the needle ends up at max speed");
speedoConfig.analog.maxPointY = val.Bind<float>("analog", "maxPointY", 0f, "Y coordinate where the needle ends up at max speed");
speedoConfig.analog.zeroPointX = val.Bind<float>("analog", "zeroPointX", 0f, "X coordinate where the needle is at 0 speed");
speedoConfig.analog.zeroPointY = val.Bind<float>("analog", "zeroPointY", 0f, "Y coordinate where the needle is at 0 speed");
speedoConfig.analog.needleX = val.Bind<float>("analog", "needleX", 0f, "X coordinate of the needle start");
speedoConfig.analog.needleY = val.Bind<float>("analog", "needleY", 0f, "Y coordinate of the needle start");
speedoConfig.analog.needleWidth = val.Bind<float>("analog", "needleWidth", 1.3f, "Width of the needle in hud pixels");
speedoConfig.analog.lengthFactor = val.Bind<float>("analog", "lengthFactor", 0.8f, "Scales the needle length by this amount, 1 reaches exactly from base to start/end position");
speedoConfig.analog.backLength = val.Bind<float>("analog", "backLength", 4f, "How many pixels the needle should poke out the back");
gauge = new SpeedoGauge(speedoConfig.analog.background.Value, new Vector2(speedoConfig.analog.zeroPointX.Value, speedoConfig.analog.zeroPointY.Value), new Vector2(speedoConfig.analog.maxPointX.Value, speedoConfig.analog.maxPointY.Value), new Vector2(speedoConfig.analog.needleX.Value, speedoConfig.analog.needleY.Value), speedoConfig.analog.needleWidth.Value, speedoConfig.analog.lengthFactor.Value, speedoConfig.analog.backLength.Value);
break;
}
sConfig = speedoConfig;
}
private void Awake()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Expected O, but got Unknown
Logger = ((BaseUnityPlugin)this).Logger;
Harmony.CreateAndPatchAll(typeof(Plugin), (string)null);
config = new ConfigFile(Utility.CombinePaths(new string[2]
{
Paths.ConfigPath,
"Speedometer.cfg"
}), true);
config.SaveOnConfigSet = true;
useWheelSpeed = config.Bind<bool>("indicator", "wheelSpeed", true, "Use front wheel rotation speed for calculating speed. Otherwise use car's linear velocity");
speedoConfigName = config.Bind<string>("indicator", "configName", "digital-center", "Name of speedometer config to use. Configs are located in BepInEx/config/Speedometer/");
config.SettingChanged += delegate(object _, SettingChangedEventArgs args)
{
if (args.ChangedSetting.Definition.Key == "configName")
{
LoadConfig(speedoConfigName.Value);
}
};
LoadConfig(speedoConfigName.Value);
ScanConfigs();
}
private static void ScanConfigs()
{
configs.Clear();
foreach (string item in Directory.EnumerateFiles(configPath))
{
if (item.EndsWith(".spdo.cfg"))
{
ReadOnlySpan<char> readOnlySpan = Path.GetFileName(item).AsSpan().TrimEnd(".spdo.cfg".AsSpan());
configs.Add(readOnlySpan.ToString());
Logger.LogInfo((object)("Found config " + readOnlySpan));
}
}
}
private static void Reload()
{
ScanConfigs();
config.Reload();
LoadConfig(speedoConfigName.Value);
}
private static Texture2D LoadImage(string name)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
byte[] array = File.ReadAllBytes(Utility.CombinePaths(new string[2] { configPath, name }));
Texture2D val = new Texture2D(2, 2);
ImageConversion.LoadImage(val, array);
((Texture)val).wrapMode = (TextureWrapMode)1;
((Texture)val).filterMode = (FilterMode)0;
return val;
}
private void Update()
{
if (Input.GetKeyDown((KeyCode)46))
{
int index = (configs.IndexOf(speedoConfigName.Value) + 1) % configs.Count;
speedoConfigName.Value = configs[index];
}
if (Input.GetKeyDown((KeyCode)44))
{
Reload();
}
}
[HarmonyPatch(typeof(sHUD), "RadioDisplay")]
[HarmonyPostfix]
private static void ShowSpeed(sHUD __instance)
{
//IL_0086: 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_028d: Unknown result type (might be due to invalid IL or missing references)
//IL_0292: Unknown result type (might be due to invalid IL or missing references)
//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
//IL_02b3: 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_033d: Unknown result type (might be due to invalid IL or missing references)
//IL_0320: Unknown result type (might be due to invalid IL or missing references)
//IL_0325: Unknown result type (might be due to invalid IL or missing references)
object? value = AccessTools.Field(typeof(sHUD), "R").GetValue(__instance);
MiniRenderer val = (MiniRenderer)((value is MiniRenderer) ? value : null);
sCarController val2 = Object.FindFirstObjectByType<sCarController>();
if (val2.GuyActive)
{
return;
}
bool value2 = useWheelSpeed.Value;
if (1 == 0)
{
}
float num;
if (value2)
{
num = Math.Max(Math.Abs(val2.wheels[0].viewRotationVelocity * 1.092f / 3.6f) - 1f, 0f);
}
else
{
Vector3 linearVelocity = val2.rb.linearVelocity;
num = ((Vector3)(ref linearVelocity)).magnitude;
}
if (1 == 0)
{
}
float num2 = num;
num2 *= sConfig.factor.Value;
Vector2Int position = default(Vector2Int);
((Vector2Int)(ref position))..ctor(sConfig.x.Value, sConfig.y.Value);
switch (sConfig.alignX.Value)
{
case Align.Center:
((Vector2Int)(ref position)).x = ((Vector2Int)(ref position)).x + val.width / 2;
break;
case Align.End:
((Vector2Int)(ref position)).x = ((Vector2Int)(ref position)).x + val.width;
break;
}
switch (sConfig.alignY.Value)
{
case Align.Center:
((Vector2Int)(ref position)).y = ((Vector2Int)(ref position)).y + val.height / 2;
break;
case Align.End:
((Vector2Int)(ref position)).y = ((Vector2Int)(ref position)).y + val.height;
break;
}
switch (sConfig.type.Value)
{
case SpeedoType.Digital:
{
string text;
try
{
text = string.Format(sConfig.digital.format.Value, num2);
}
catch (FormatException ex)
{
text = "Invalid format specifier";
Logger.LogError((object)ex);
}
int num3 = text.Length * 8;
switch (sConfig.digital.textAlign.Value)
{
case Align.End:
((Vector2Int)(ref position)).x = ((Vector2Int)(ref position)).x - num3;
break;
case Align.Center:
((Vector2Int)(ref position)).x = ((Vector2Int)(ref position)).x - num3 / 2;
break;
}
val.put(text, (float)((Vector2Int)(ref position)).x, (float)((Vector2Int)(ref position)).y);
break;
}
case SpeedoType.Analog:
{
Vector2Int dimensions;
switch (sConfig.alignX.Value)
{
case Align.Center:
{
int x2 = ((Vector2Int)(ref position)).x;
dimensions = gauge.Dimensions;
((Vector2Int)(ref position)).x = x2 - ((Vector2Int)(ref dimensions)).x / 2;
break;
}
case Align.End:
{
int x = ((Vector2Int)(ref position)).x;
dimensions = gauge.Dimensions;
((Vector2Int)(ref position)).x = x - ((Vector2Int)(ref dimensions)).x;
break;
}
}
switch (sConfig.alignY.Value)
{
case Align.Center:
{
int y2 = ((Vector2Int)(ref position)).y;
dimensions = gauge.Dimensions;
((Vector2Int)(ref position)).y = y2 - ((Vector2Int)(ref dimensions)).y / 2;
break;
}
case Align.End:
{
int y = ((Vector2Int)(ref position)).y;
dimensions = gauge.Dimensions;
((Vector2Int)(ref position)).y = y - ((Vector2Int)(ref dimensions)).y;
break;
}
}
gauge.Draw(val, position, num2 / sConfig.analog.maxSpeed.Value);
break;
}
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "Speedometer";
public const string PLUGIN_NAME = "Speedometer";
public const string PLUGIN_VERSION = "1.1.2";
}
}