using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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 InControl;
using Microsoft.CodeAnalysis;
using NineSolsAPI;
using NineSolsAPI.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DpsMeter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+66408d67671c7d3224d44dca51bf26a7171fc1ad")]
[assembly: AssemblyProduct("DpsMeter")]
[assembly: AssemblyTitle("DpsMeter")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[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 DpsMeter
{
public class DpsDisplay
{
[CompilerGenerated]
private DpsTracker <tracker>P;
[CompilerGenerated]
private ConfigEntry<bool> <configShowDamageNumbers>P;
[CompilerGenerated]
private ConfigEntry<bool> <updateOnHits>P;
[CompilerGenerated]
private TMP_Text <statsPanel>P;
private const float MoveAmount = 50f;
private const float FadeDuration = 2f;
private const float ScaleFactor = 1.2f;
private List<TMP_Text> objects;
public DpsDisplay(DpsTracker tracker, ConfigEntry<bool> configShowDamageNumbers, ConfigEntry<bool> updateOnHits, TMP_Text statsPanel)
{
<tracker>P = tracker;
<configShowDamageNumbers>P = configShowDamageNumbers;
<updateOnHits>P = updateOnHits;
<statsPanel>P = statsPanel;
objects = new List<TMP_Text>();
base..ctor();
}
public IEnumerator OnDamage(EffectHitData data, float value, bool internalDamage)
{
if (<configShowDamageNumbers>P.Value)
{
Color color = (Color)(internalDamage ? new Color(0.5f, 1f, 1f, 1f) : Color.white);
yield return SpawnText(data.hitPos, $"{value:0.##}", color);
}
}
public IEnumerator OnInaccurateParry(EffectHitData data, float parryTime, float requiredParryTime, float spamLevel)
{
if (<configShowDamageNumbers>P.Value)
{
float num = parryTime - requiredParryTime;
string text = $"Missed by {num * 1000f:F0}ms";
if (spamLevel > 0f)
{
text += $" (Spam {spamLevel})";
}
if (requiredParryTime == 0f)
{
text = "Parry Spam";
}
yield return SpawnText(data.hitPos, text, new Color(1f, 0f, 0.28f, 1f));
}
}
private IEnumerator SpawnText(Vector3 pos, string str, Color color)
{
//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)
//IL_001c: 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)
TMP_Text text = CreateDamageText(str, color);
objects.Add(text);
yield return AnimateText(text, pos);
objects.Remove(text);
}
public void Update()
{
List<HitData> recentHits = <tracker>P.RecentHits;
if (recentHits.Count == 0)
{
<statsPanel>P.text = "";
return;
}
float num = recentHits.Sum((HitData hit) => hit.Value);
float totalDamage = recentHits.Sum((HitData hit) => hit.Value);
IEnumerable<string> enumerable = from x in recentHits.GroupBy((HitData hit) => hit.Type, (HitData hit) => hit.Value, delegate(string type, IEnumerable<float> @group)
{
float num4 = @group.Sum();
int item = (int)(num4 / totalDamage * 100f);
return (type, num4, item);
})
orderby x.groupSum descending
select $"{x.type}: {x.groupSum:0.##} {x.percentage}%";
<statsPanel>P.text = GeneralExtensions.Join<string>(enumerable, (Func<string, string>)null, "\n");
float num2 = (<updateOnHits>P.Value ? <tracker>P.RunningTime : ((recentHits.Count <= 0) ? 0f : (ListExtensions.Last<HitData>(recentHits).Time - ListExtensions.First<HitData>(recentHits).Time)));
if (num2 != 0f)
{
float num3 = num / num2;
TMP_Text obj = <statsPanel>P;
obj.text += $"\nDPS: {num3:F1}";
}
}
public void OnDestroy()
{
foreach (TMP_Text @object in objects)
{
Object.Destroy((Object)(object)((Component)@object).gameObject);
}
}
private static Vector2 WorldToCanvas(Vector3 worldPosition)
{
//IL_0019: 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_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_0034: Unknown result type (might be due to invalid IL or missing references)
Transform transform = ((Component)NineSolsAPICore.FullscreenCanvas).transform;
Vector3 val = SingletonBehaviour<CameraManager>.Instance.camera2D.GameCamera.WorldToScreenPoint(worldPosition);
Vector2 result = default(Vector2);
RectTransformUtility.ScreenPointToLocalPointInRectangle(((Component)transform).GetComponent<RectTransform>(), Vector2.op_Implicit(val), (Camera)null, ref result);
return result;
}
private static TMP_Text CreateDamageText(string str, Color color)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: 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_0061: 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)
Transform transform = ((Component)NineSolsAPICore.FullscreenCanvas).transform;
GameObject val = new GameObject();
val.transform.SetParent(transform);
TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
((TMP_Text)obj).fontSize = 25f;
((TMP_Text)obj).alignment = (TextAlignmentOptions)1026;
((TMP_Text)obj).fontWeight = (FontWeight)700;
((TMP_Text)obj).text = str;
((Graphic)obj).color = color;
RectTransform component = ((Component)obj).GetComponent<RectTransform>();
component.pivot = new Vector2(0.5f, 0f);
component.sizeDelta = new Vector2(200f, 10f);
return (TMP_Text)(object)obj;
}
private static IEnumerator AnimateText(TMP_Text text, Vector3 worldPosition)
{
//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)
Vector2 anchoredPosition = WorldToCanvas(worldPosition);
RectTransform rectTransform = ((Component)text).GetComponent<RectTransform>();
rectTransform.anchoredPosition = anchoredPosition;
Vector3 targetPositionWorld = worldPosition + new Vector3(0f, 50f, 0f);
float elapsedTime = 0f;
Color originalColor = ((Graphic)text).color;
float originalScale = ((Transform)rectTransform).localScale.x;
while (elapsedTime < 2f)
{
rectTransform.anchoredPosition = WorldToCanvas(Vector3.Lerp(worldPosition, targetPositionWorld, elapsedTime / 2f));
Color color = ((Graphic)text).color;
color.a = Mathf.Lerp(1f, 0f, elapsedTime / 2f);
((Graphic)text).color = color;
float num = Mathf.Lerp(originalScale, 1.2f, elapsedTime / 1f);
if (elapsedTime > 1f)
{
num = Mathf.Lerp(1.2f, originalScale, (elapsedTime - 1f) / 1f);
}
((Transform)rectTransform).localScale = new Vector3(num, num, num);
elapsedTime += Time.deltaTime;
yield return null;
}
((Graphic)text).color = new Color(originalColor.r, originalColor.g, originalColor.b, 0f);
Object.Destroy((Object)(object)((Component)text).gameObject);
}
}
internal static class Extensions
{
public static string Name(this EffectType type)
{
//IL_0000: 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_000f: 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_001b: 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_002a: 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)
if ((type & 0x40000) != 0)
{
return "Heavy";
}
if ((type & 0x20) != 0)
{
return "Talisman Attach";
}
if ((type & 0x2000000) != 0)
{
return "Talisman Explode";
}
if ((type & 0x200) != 0)
{
return "Tai-Chi";
}
return ((object)(EffectType)(ref type)).ToString();
}
}
public enum DpsResetMode
{
OnEnemyChange,
Manual
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("DpsMeter", "DpsMeter", "1.0.0")]
public class DpsMeterMod : BaseUnityPlugin
{
private ConfigEntry<DpsResetMode> configResetMode;
private ConfigEntry<bool> configUpdateOnHits;
private ConfigEntry<bool> configShowDamageNumbers;
private ConfigEntry<KeyboardShortcut> configShortcutPause;
private ConfigEntry<KeyboardShortcut> configShortcutReset;
public static DpsMeterMod Instance;
private Harmony harmony;
private DpsTracker dpsTracker;
private DpsDisplay dpsDisplay;
private GameObject dummy;
public TMP_Text statsPanel;
private void Awake()
{
//IL_0084: 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)
Instance = this;
Log.Init(((BaseUnityPlugin)this).Logger);
RCGLifeCycle.DontDestroyForever(((Component)this).gameObject);
try
{
configResetMode = ((BaseUnityPlugin)this).Config.Bind<DpsResetMode>("General", "Reset Mode", DpsResetMode.OnEnemyChange, (ConfigDescription)null);
configUpdateOnHits = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Only update DPS on hits", false, (ConfigDescription)null);
configShowDamageNumbers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show damage numbers", true, (ConfigDescription)null);
configShortcutPause = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Shortcuts", "Pause DPS Tracking", KeyboardShortcut.Empty, (ConfigDescription)null);
configShortcutReset = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Shortcuts", "Reset DPS Tracking", KeyboardShortcut.Empty, (ConfigDescription)null);
statsPanel = CreateStatsPanel();
harmony = Harmony.CreateAndPatchAll(typeof(DpsMeterMod).Assembly, (string)null);
dpsTracker = new DpsTracker(configResetMode);
dpsDisplay = new DpsDisplay(dpsTracker, configShowDamageNumbers, configUpdateOnHits, statsPanel);
KeybindManager.Add((MonoBehaviour)(object)this, (Action)delegate
{
dpsTracker.Pause();
}, (Func<KeyboardShortcut>)(() => configShortcutPause.Value));
KeybindManager.Add((MonoBehaviour)(object)this, (Action)delegate
{
dpsTracker.Reset();
}, (Func<KeyboardShortcut>)(() => configShortcutReset.Value));
KeybindManager.Add((MonoBehaviour)(object)this, (Action)delegate
{
GameCore instance = SingletonBehaviour<GameCore>.Instance;
if (instance != null)
{
instance.ResetLevel(false);
}
}, (KeyCode[])(object)new KeyCode[1] { (KeyCode)116 });
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin DpsMeter is loaded!");
}
catch (Exception ex)
{
ToastManager.Toast((object)ex);
}
}
private static TMP_Text CreateStatsPanel()
{
//IL_0005: 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_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: 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_008b: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("Stats");
val.transform.SetParent(((Component)NineSolsAPICore.FullscreenCanvas).transform);
TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
((TMP_Text)obj).alignment = (TextAlignmentOptions)1028;
((TMP_Text)obj).fontSize = 20f;
((Graphic)obj).color = Color.white;
((TMP_Text)obj).text = "";
RectTransform component = ((Component)obj).GetComponent<RectTransform>();
component.anchorMin = new Vector2(1f, 0.5f);
component.anchorMax = new Vector2(1f, 0.5f);
component.pivot = new Vector2(1f, 0f);
component.anchoredPosition = new Vector2(-10f, 10f);
component.sizeDelta = new Vector2((float)Screen.width, 0f);
return (TMP_Text)(object)obj;
}
private void Update()
{
dpsTracker.Update();
dpsDisplay.Update();
}
private void SpawnTrainingDummy()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
Player i = Player.i;
if (i != null)
{
Object.Instantiate<GameObject>(dummy, ((Component)Instance).gameObject.transform).transform.position = ((Component)i).transform.position;
}
}
private void OnDestroy()
{
harmony.UnpatchSelf();
dpsDisplay.OnDestroy();
if (Object.op_Implicit((Object)(object)dummy))
{
Object.Destroy((Object)(object)dummy);
}
if (Object.op_Implicit((Object)(object)statsPanel))
{
Object.Destroy((Object)(object)((Component)statsPanel).gameObject);
}
}
public void OnDamage(EffectHitData hitData, float value, bool internalDamage)
{
dpsTracker.OnDamage(hitData, value, internalDamage);
((MonoBehaviour)this).StartCoroutine(dpsDisplay.OnDamage(hitData, value, internalDamage));
}
public void OnInaccurateParry(EffectHitData hitData, float parryTime, float requiredParryTime, float spamLevel)
{
((MonoBehaviour)this).StartCoroutine(dpsDisplay.OnInaccurateParry(hitData, parryTime, requiredParryTime, spamLevel));
}
private static GameObject LoadObjectFromResources(string resourceName, string scenePath, string objectName)
{
//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_0068: 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)
GameObject val = null;
AssetBundle embeddedAssetBundle = AssemblyUtils.GetEmbeddedAssetBundle(resourceName);
SceneManager.LoadScene(scenePath, (LoadSceneMode)1);
Scene sceneByPath = SceneManager.GetSceneByPath(scenePath);
GameObject[] rootGameObjects = ((Scene)(ref sceneByPath)).GetRootGameObjects();
foreach (GameObject val2 in rootGameObjects)
{
if (((Object)val2).name == objectName)
{
val = Object.Instantiate<GameObject>(val2);
((Object)val).name = "Training Dummy";
RCGLifeCycle.DontDestroyForever(val);
break;
}
Object.Destroy((Object)(object)val2);
}
SceneManager.UnloadSceneAsync(sceneByPath);
embeddedAssetBundle.Unload(true);
if (val == null)
{
ToastManager.Toast((object)embeddedAssetBundle);
ToastManager.Toast((object)sceneByPath);
ToastManager.Toast((object)((Scene)(ref sceneByPath)).GetRootGameObjects().Length);
throw new Exception(objectName + " not found in " + resourceName + "/" + scenePath);
}
return val;
}
}
public record struct HitData(float Time, float Value, string Type, GameObject Owner);
public class DpsTracker
{
[CompilerGenerated]
private ConfigEntry<DpsResetMode> <resetMode>P;
public readonly List<HitData> RecentHits;
public bool Running;
public float RunningTime;
private readonly Dictionary<string, string> dealerNames;
public DpsTracker(ConfigEntry<DpsResetMode> resetMode)
{
<resetMode>P = resetMode;
RecentHits = new List<HitData>();
Running = true;
dealerNames = new Dictionary<string, string>
{
{ "AttackFront", "Attack" },
{ "ChargedAttackFront", "Charge Attack" },
{ "Third Attack", "Heavy" },
{ "Foo", "Foo Attach" },
{ "FooExplode", "Foo" },
{ "JumpSpinKick", "Tai Chi" },
{ "[Dealer] Internal Damage", "UC" },
{ "--ReflectNode", "Reflect Projectile" },
{ "NormalArrow Shoot 穿雲 Lv1(Clone)", "Bow" },
{ "NormalArrow Shoot 穿雲 Lv2(Clone)", "Bow" },
{ "NormalArrow Shoot 穿雲 Lv3(Clone)", "Bow" }
};
base..ctor();
}
public void Pause()
{
Running = !Running;
ToastManager.Toast((object)$"DPS tracking enabled: {Running}");
}
public void Reset()
{
RecentHits.Clear();
RunningTime = 0f;
ToastManager.Toast((object)"DPS tracking reset");
}
public void Update()
{
if (Running)
{
RunningTime += Time.deltaTime;
}
}
public void OnDamage(EffectHitData data, float value, bool internalDamage)
{
if (Running)
{
GameObject gameObject = data.receiver.OwnerComponent.gameObject;
if (<resetMode>P.Value == DpsResetMode.OnEnemyChange && RecentHits.Count > 0 && (Object)(object)ListExtensions.Last<HitData>(RecentHits).Owner != (Object)(object)gameObject)
{
RecentHits.Clear();
ToastManager.Toast((object)"New enemy, resetting DPS stats");
}
string name = ((Object)data.dealer).name;
if ((name == "PostureDecrease" || name == "Posture Decrease") ? true : false)
{
name = ((Object)((Component)data.dealer).transform.parent).name;
}
string value2 = null;
Transform val = ((Component)data.dealer).transform;
while (Object.op_Implicit((Object)(object)val) && !dealerNames.TryGetValue(((Object)val).name, out value2))
{
val = val.parent;
}
if (value2 == null)
{
ToastManager.Toast((object)ObjectUtils.ObjectPath(((Component)data.dealer).gameObject));
value2 = ((Object)data.dealer).name;
}
RecentHits.Add(new HitData(Time.time, value, value2, gameObject));
}
}
}
internal static class Log
{
private static ManualLogSource logSource;
internal static void Init(ManualLogSource logSource)
{
Log.logSource = logSource;
}
internal static void Debug(object data)
{
logSource.LogDebug(data);
}
internal static void Error(object data)
{
logSource.LogError(data);
}
internal static void Fatal(object data)
{
logSource.LogFatal(data);
}
internal static void Info(object data)
{
logSource.LogInfo(data);
}
internal static void Message(object data)
{
logSource.LogMessage(data);
}
internal static void Warning(object data)
{
logSource.LogWarning(data);
}
}
[HarmonyPatch]
public class Patches
{
private static FieldRef<PlayerInputCommandQueue, Dictionary<PlayerAction, List<float>>> actionDict = AccessTools.FieldRefAccess<PlayerInputCommandQueue, Dictionary<PlayerAction, List<float>>>("actionDict");
private static FieldRef<PlayerParryState, float[]> spamDatas = AccessTools.FieldRefAccess<PlayerParryState, float[]>("spamDatas");
[HarmonyPatch(typeof(PlayerParryState), "Parried")]
[HarmonyPrefix]
public static void Parried(ref PlayerParryState __instance, EffectHitData hitData)
{
Player i = Player.i;
List<float> list = actionDict.Invoke(i.inputCommandQueue)[i.playerInput.gameplayActions.Parry];
float num;
if (list.Count <= 0)
{
num = -1f;
}
else
{
num = list[list.Count - 1];
}
float num2 = num;
float[] array = spamDatas.Invoke(__instance);
int spamLevel = __instance.spamLevel;
float num3 = Time.time - num2;
if (!__instance.IsAlwaysAccurate && !i.IsInQTETutorial && !(num3 < array[spamLevel]))
{
DpsMeterMod.Instance.OnInaccurateParry(hitData, num3, array[spamLevel], spamLevel);
}
}
[HarmonyPatch(typeof(MonsterBase), "HittedByPlayerDecreasePosture")]
[HarmonyPrefix]
public static void OnDamage(EffectHitData hitData)
{
DpsMeterMod.Instance.OnDamage(hitData, hitData.dealer.FinalValue, internalDamage: false);
}
[HarmonyPatch(typeof(MonsterBase), "InternalInjuryVirtual")]
[HarmonyPrefix]
public static void OnDamageInternal(EffectHitData data, float scale, float additionalDamage)
{
float value = (int)(data.dealer.FinalValue * scale + additionalDamage);
DpsMeterMod.Instance.OnDamage(data, value, internalDamage: true);
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "DpsMeter";
public const string PLUGIN_NAME = "DpsMeter";
public const string PLUGIN_VERSION = "1.0.0";
}
}