using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Agents;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using EWC.API;
using EWC.API.Accuracy;
using EWC.CustomWeapon;
using EWC.CustomWeapon.Properties;
using EWC.CustomWeapon.Properties.Traits.CustomProjectile.Components;
using EWC.CustomWeapon.Structs;
using Enemies;
using GTFO.API;
using GTFO.API.Utilities;
using Gear;
using HarmonyLib;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using LevelGeneration;
using Localization;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using StatDisplay.Attributes;
using StatDisplay.Config;
using StatDisplay.Config.Archive;
using StatDisplay.Config.Vanilla;
using StatDisplay.Data;
using StatDisplay.Dependencies;
using StatDisplay.Handler;
using StatDisplay.Networking;
using StatDisplay.Networking.Events;
using StatDisplay.Utils.Extensions;
using TMPro;
using TheArchive;
using TheArchive.Core;
using TheArchive.Core.Attributes.Feature;
using TheArchive.Core.Attributes.Feature.Members;
using TheArchive.Core.Attributes.Feature.Settings;
using TheArchive.Core.FeaturesAPI;
using TheArchive.Core.FeaturesAPI.Settings;
using TheArchive.Core.Localization;
using TheArchive.Core.Models;
using TheArchive.Interfaces;
using TheArchive.Utilities;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("StatDisplay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+bb549e3583571ce3f7196f9b4ac06eb92714f259")]
[assembly: AssemblyProduct("StatDisplay")]
[assembly: AssemblyTitle("StatDisplay")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[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]
[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;
}
}
}
namespace StatDisplay
{
internal static class DinoLogger
{
private static ManualLogSource logger = Logger.CreateLogSource("StatDisplay");
public static void Log(string format, params object[] args)
{
Log(string.Format(format, args));
}
public static void Log(string str)
{
if (logger != null)
{
logger.Log((LogLevel)8, (object)str);
}
}
public static void Warning(string format, params object[] args)
{
Warning(string.Format(format, args));
}
public static void Warning(string str)
{
if (logger != null)
{
logger.Log((LogLevel)4, (object)str);
}
}
public static void Error(string format, params object[] args)
{
Error(string.Format(format, args));
}
public static void Error(string str)
{
if (logger != null)
{
logger.Log((LogLevel)2, (object)str);
}
}
public static void Debug(string format, params object[] args)
{
Debug(string.Format(format, args));
}
public static void Debug(string str)
{
if (logger != null)
{
logger.Log((LogLevel)32, (object)str);
}
}
}
[BepInPlugin("Dinorush.StatDisplay", "StatDisplay", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
internal sealed class EntryPoint : BasePlugin
{
public const string MODNAME = "StatDisplay";
private IEnumerable<MethodInfo> _cleanupCallbacks;
private IEnumerable<MethodInfo> _enterCallbacks;
private IEnumerable<MethodInfo> _buildStartCallbacks;
private Type[] _allTypes;
public override void Load()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Expected O, but got Unknown
_allTypes = GetTypesSafe();
Harmony val = new Harmony("StatDisplay");
Type[] allTypes = _allTypes;
foreach (Type type in allTypes)
{
if (((MemberInfo)type).GetCustomAttribute<HarmonyPatch>() != null)
{
val.PatchAll(type);
}
}
CacheFrequentCallbacks();
InvokeCallbacks<InvokeOnLoadAttribute>();
LevelAPI.OnLevelCleanup += RunFrequentCallback(_cleanupCallbacks);
LevelAPI.OnEnterLevel += RunFrequentCallback(_enterCallbacks);
LevelAPI.OnBuildStart += RunFrequentCallback(_buildStartCallbacks);
AssetAPI.OnStartupAssetsLoaded += InvokeCallbacks<InvokeOnAssetLoadAttribute>;
((BasePlugin)this).Log.LogMessage((object)"Loaded StatDisplay");
}
private Type[] GetTypesSafe()
{
IEnumerable<Type> source;
try
{
source = ((object)this).GetType().Assembly.DefinedTypes;
}
catch (ReflectionTypeLoadException ex)
{
source = ex.Types.Where((Type type) => type != null);
}
if (!ArchiveWrapper.HasArchive)
{
return source.Where((Type type) => !type.FullName.Contains("Archive")).ToArray();
}
return source.ToArray();
}
private static Action RunFrequentCallback(IEnumerable<MethodInfo> callbacks)
{
return delegate
{
foreach (MethodInfo callback in callbacks)
{
callback.Invoke(null, null);
}
};
}
private void CacheFrequentCallbacks()
{
IEnumerable<MethodInfo> source = from method in ((IEnumerable<Type>)_allTypes).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods)
where method.IsStatic
select method;
_cleanupCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnCleanupAttribute>() != null);
_enterCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnEnterAttribute>() != null);
_buildStartCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnBuildStartAttribute>() != null);
}
private void InvokeCallbacks<T>() where T : Attribute
{
foreach (MethodInfo item in from method in ((IEnumerable<Type>)_allTypes).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods)
where method.GetCustomAttribute<T>() != null
where method.IsStatic
select method)
{
item.Invoke(null, null);
}
}
}
public static class StatManager
{
private static readonly Dictionary<ulong, SNet_Player> _remotePlayers = new Dictionary<ulong, SNet_Player>();
private static bool _masterHasMod = false;
public static IReadOnlyCollection<SNet_Player> RemotePlayers => _remotePlayers.Values;
public static bool MasterHasMod
{
get
{
return _masterHasMod;
}
private set
{
if (_masterHasMod == value)
{
return;
}
_masterHasMod = value;
foreach (SNet_Slot item in (Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots)
{
if ((Object)(object)item.player != (Object)null && item.player.IsBot)
{
StatHandler.AddPlayer(item.player, MasterHasMod, onlyIfExists: true);
}
}
}
}
public static bool PlayerHasMod(SNet_Player player)
{
if (player.IsLocal || (MasterHasMod && player.IsBot))
{
return true;
}
return _remotePlayers.ContainsKey(player.Lookup);
}
[InvokeOnLoad]
private static void Init()
{
SNet_Events.OnMasterChanged += Action.op_Implicit((Action)OnMasterSet);
}
internal static void AddPlayer(SNet_Player player, bool hasMod)
{
if (hasMod)
{
if (player.IsMaster)
{
MasterHasMod = true;
}
if (!player.IsLocal && !player.IsBot)
{
_remotePlayers.TryAdd(player.Lookup, player);
}
}
StatHandler.AddPlayer(player, hasMod);
}
internal static void RemovePlayer(SNet_Player player)
{
StatHandler.RemovePlayer(player);
_remotePlayers.Remove(player.Lookup);
}
internal static void OnMasterSet()
{
MasterHasMod = SNet.IsMaster || _remotePlayers.ContainsKey(SNet.Master.Lookup);
StatHandler.OnMasterSet();
}
internal static void Clear()
{
_masterHasMod = false;
_remotePlayers.Clear();
StatHandler.Clear();
}
}
}
namespace StatDisplay.Utils.Extensions
{
internal static class SlotExtensions
{
public static AccSlotType ToAccSlotType(this AmmoType ammo)
{
//IL_0000: 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_0005: Invalid comparison between Unknown and I4
if ((int)ammo != 0)
{
if ((int)ammo == 1)
{
return AccSlotType.Secondary;
}
return AccSlotType.All;
}
return AccSlotType.Primary;
}
public static DamSlotType ToDamSlotType(this AmmoType slot)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Expected I4, but got Unknown
return (int)slot switch
{
0 => DamSlotType.Primary,
1 => DamSlotType.Secondary,
2 => DamSlotType.Tool,
4 => DamSlotType.Melee,
_ => DamSlotType.All,
};
}
public static AccSlotType ToAccSlotType(this InventorySlot slot)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
if ((int)slot != 1)
{
if ((int)slot == 2)
{
return AccSlotType.Secondary;
}
return AccSlotType.All;
}
return AccSlotType.Primary;
}
public static DamSlotType ToDamSlotType(this InventorySlot slot)
{
//IL_0000: 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_0014: Expected I4, but got Unknown
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Invalid comparison between Unknown and I4
switch (slot - 1)
{
default:
if ((int)slot == 10)
{
return DamSlotType.Melee;
}
return DamSlotType.All;
case 0:
return DamSlotType.Primary;
case 1:
return DamSlotType.Secondary;
case 2:
return DamSlotType.Tool;
}
}
public static InventorySlot ToInventorySlot(this AmmoType ammo)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Expected I4, but got Unknown
//IL_0019: 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_0021: 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_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
return (InventorySlot)((int)ammo switch
{
0 => 1,
1 => 2,
2 => 3,
3 => 4,
_ => 0,
});
}
public static AmmoType ToAmmoType(this InventorySlot slot)
{
//IL_0000: 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_0018: Expected I4, but got Unknown
//IL_001b: 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_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: 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)
return (AmmoType)((slot - 1) switch
{
0 => 0,
1 => 1,
2 => 2,
3 => 3,
_ => 4,
});
}
}
internal static class StringExtensions
{
public static T ToEnum<T>(this string? value, T defaultValue) where T : struct
{
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
if (!Enum.TryParse<T>(value.Replace(" ", null), ignoreCase: true, out var result))
{
return defaultValue;
}
return result;
}
public static bool TryToEnum<T>(this string? value, out T enumValue) where T : struct
{
enumValue = default(T);
if (string.IsNullOrEmpty(value))
{
return false;
}
return Enum.TryParse<T>(value.Replace(" ", null), ignoreCase: true, out enumValue);
}
}
}
namespace StatDisplay.Patches
{
[HarmonyPatch]
internal static class AccuracyPatches
{
private struct CacheInfo
{
public AccSlotType slot;
public ulong lookup;
public readonly bool[,] history;
public readonly short[,] delta;
public readonly bool Valid => slot != AccSlotType.All;
public CacheInfo()
{
slot = AccSlotType.All;
lookup = 0uL;
history = new bool[2, 2];
delta = new short[3, 3];
}
}
private static CacheInfo _cache = new CacheInfo();
private static void CacheInc(AccShotType shotType, AccStatType statType, bool checkHistory = false)
{
if (checkHistory)
{
if (_cache.history[(uint)shotType, (uint)statType])
{
return;
}
_cache.history[(uint)shotType, (uint)statType] = true;
}
_cache.delta[(uint)shotType, (uint)statType]++;
}
private static void ResetHistory(AccShotType shotType)
{
for (int i = 0; i < 2; i++)
{
_cache.history[(uint)shotType, i] = false;
}
}
[HarmonyPatch(typeof(ShotgunSynced), "Fire")]
[HarmonyPatch(typeof(BulletWeaponSynced), "Fire")]
[HarmonyPatch(typeof(Shotgun), "Fire")]
[HarmonyPatch(typeof(BulletWeapon), "Fire")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_Fire(BulletWeapon __instance)
{
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)((Item)__instance).Owner == (Object)null))
{
SNet_Player owner = ((Item)__instance).Owner.Owner;
if ((owner.IsLocal || (SNet.IsMaster && owner.IsBot)) && !EWCWrapper.HasCWC(__instance))
{
_cache.slot = ((ItemEquippable)__instance).AmmoType.ToAccSlotType();
_cache.lookup = owner.Lookup;
CacheInc(AccShotType.Group, AccStatType.Fired);
}
}
}
[HarmonyPatch(typeof(ShotgunSynced), "Fire")]
[HarmonyPatch(typeof(BulletWeaponSynced), "Fire")]
[HarmonyPatch(typeof(Shotgun), "Fire")]
[HarmonyPatch(typeof(BulletWeapon), "Fire")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_Fire()
{
if (!_cache.Valid)
{
return;
}
StatHandler.AddAccuracyDelta(_cache.lookup, _cache.slot, _cache.delta);
_cache.slot = AccSlotType.All;
_cache.lookup = 0uL;
short[,] delta = _cache.delta;
for (int i = 0; i < delta.GetLength(0); i++)
{
for (int j = 0; j < delta.GetLength(1); j++)
{
delta[i, j] = 0;
}
}
bool[,] history = _cache.history;
for (int k = 0; k < history.GetLength(0); k++)
{
for (int l = 0; l < history.GetLength(1); l++)
{
history[k, l] = false;
}
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_CastWeaponRay(WeaponHitData weaponRayData)
{
if (_cache.Valid && !(weaponRayData.maxRayDist < 100f))
{
ResetHistory(AccShotType.Shot);
CacheInc(AccShotType.Shot, AccStatType.Fired);
CacheInc(AccShotType.Full, AccStatType.Fired);
}
}
[HarmonyPatch(typeof(Dam_EnemyDamageLimb), "BulletDamage")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_EnemyDamage(Dam_EnemyDamageLimb __instance)
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Invalid comparison between Unknown and I4
if (_cache.Valid)
{
CacheInc(AccShotType.Shot, AccStatType.Hit, checkHistory: true);
CacheInc(AccShotType.Group, AccStatType.Hit, checkHistory: true);
CacheInc(AccShotType.Full, AccStatType.Hit);
if ((int)__instance.m_type == 1)
{
CacheInc(AccShotType.Shot, AccStatType.Crit, checkHistory: true);
CacheInc(AccShotType.Group, AccStatType.Crit, checkHistory: true);
CacheInc(AccShotType.Full, AccStatType.Crit);
}
}
}
}
[HarmonyPatch]
internal static class DamagePatches
{
private struct CacheInfo
{
public DamSlotType slot;
public ulong lookup;
public readonly bool Valid
{
get
{
if (slot != DamSlotType.All)
{
return lookup != 0;
}
return false;
}
}
public CacheInfo()
{
slot = DamSlotType.All;
lookup = 0uL;
}
}
private static CacheInfo _cache = new CacheInfo();
private static readonly Random _random = new Random();
[InvokeOnLoad]
private static void Init()
{
EWCWrapper.AddProjectileHitCallback(delegate(ulong lookup, DamSlotType slot)
{
_cache.slot = slot;
_cache.lookup = lookup;
});
}
private static void ClearCache()
{
_cache.slot = DamSlotType.All;
_cache.lookup = 0uL;
}
[HarmonyPatch(typeof(ShotgunSynced), "Fire")]
[HarmonyPatch(typeof(BulletWeaponSynced), "Fire")]
[HarmonyPatch(typeof(Shotgun), "Fire")]
[HarmonyPatch(typeof(BulletWeapon), "Fire")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_Fire(BulletWeapon __instance)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
_cache.slot = ((ItemEquippable)__instance).AmmoType.ToDamSlotType();
_cache.lookup = ((Item)__instance).Owner.Owner.Lookup;
}
[HarmonyPatch(typeof(ShotgunSynced), "Fire")]
[HarmonyPatch(typeof(BulletWeaponSynced), "Fire")]
[HarmonyPatch(typeof(Shotgun), "Fire")]
[HarmonyPatch(typeof(BulletWeapon), "Fire")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_Fire()
{
ClearCache();
}
[HarmonyPatch(typeof(MeleeWeaponFirstPerson), "DoAttackDamage")]
[HarmonyPatch(typeof(MeleeWeaponThirdPerson), "DoAttackDamage")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_Attack(ItemEquippable __instance)
{
_cache.slot = DamSlotType.Melee;
_cache.lookup = ((Item)__instance).Owner.Owner.Lookup;
}
[HarmonyPatch(typeof(MeleeWeaponFirstPerson), "DoAttackDamage")]
[HarmonyPatch(typeof(MeleeWeaponThirdPerson), "DoAttackDamage")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_Attack()
{
ClearCache();
}
[HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireMaster")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_SentryFire(SentryGunInstance_Firing_Bullets __instance)
{
CacheTool(__instance.m_core.Owner);
}
[HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireMaster")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_SentryFire()
{
ClearCache();
}
[HarmonyPatch(typeof(MineDeployerInstance_Detonate_Explosive), "DoExplode")]
[HarmonyWrapSafe]
[HarmonyPrefix]
public static void ExplodePrefix(MineDeployerInstance_Detonate_Explosive __instance)
{
CacheTool(__instance.m_core.Owner);
}
[HarmonyPatch(typeof(MineDeployerInstance_Detonate_Explosive), "DoExplode")]
[HarmonyWrapSafe]
[HarmonyPostfix]
public static void ExplodePostfix()
{
ClearCache();
}
private static void CacheTool(PlayerAgent? owner)
{
_cache.slot = DamSlotType.Tool;
_cache.lookup = ((owner != null) ? owner.Owner.Lookup : 0);
}
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "BulletDamage")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_BulletDamage(Dam_EnemyDamageBase __instance, int limbID, float dam, ref uint gearCategoryId)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Invalid comparison between Unknown and I4
if (_cache.Valid && limbID != -1)
{
gearCategoryId = (uint)(_cache.slot + 1);
if (!StatManager.MasterHasMod)
{
DamStatType stat = (((int)((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)__instance.DamageLimbs)[limbID].m_type == 1) ? DamStatType.Crit : DamStatType.Any);
StatHandler.AddDamage(_cache.lookup, _cache.slot, stat, Math.Min(dam, ((Dam_SyncedDamageBase)__instance).Health));
}
}
}
[HarmonyPatch(typeof(Dam_EnemyDamageLimb), "MeleeDamage")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_MeleeDamage(Dam_EnemyDamageLimb __instance, Agent sourceAgent, float dam)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Invalid comparison between Unknown and I4
if (!StatManager.MasterHasMod)
{
DamStatType stat = (((int)__instance.m_type == 1) ? DamStatType.Crit : DamStatType.Any);
StatHandler.AddDamage(((Il2CppObjectBase)sourceAgent).Cast<PlayerAgent>().Owner.Lookup, DamSlotType.Melee, stat, Math.Min(dam, ((Dam_SyncedDamageBase)__instance.m_base).Health));
}
}
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveBulletDamage")]
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveMeleeDamage")]
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveExplosionDamage")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_RecvEnemyDamage(Dam_EnemyDamageBase __instance, ref float __state)
{
__state = ((Dam_SyncedDamageBase)__instance).Health;
}
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveBulletDamage")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_RecvBulletDamage(Dam_EnemyDamageBase __instance, pBulletDamageData data, float __state)
{
//IL_0001: 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_0026: 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_0061: Invalid comparison between Unknown and I4
//IL_00bd: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Invalid comparison between Unknown and I4
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
if (CacheDamageHost(__instance, data.limbID, __state) || _cache.slot == DamSlotType.Tool)
{
return;
}
DamSlotType damSlotType;
if (data.gearCategoryId != 0)
{
damSlotType = (DamSlotType)(data.gearCategoryId - 1);
if ((int)damSlotType >= 4)
{
return;
}
}
else
{
Agent val = default(Agent);
if (!((pAgent)(ref data.source)).TryGet(ref val))
{
return;
}
PlayerInventorySynced val2 = ((Il2CppObjectBase)((Il2CppObjectBase)val).Cast<PlayerAgent>().Inventory).Cast<PlayerInventorySynced>();
if ((int)val2.m_equipStatus == 1)
{
AmmoType ammoType = val2.m_queuedEquipItem.AmmoType;
DamSlotType damSlotType2 = (((int)ammoType != 0) ? (((int)ammoType == 1) ? DamSlotType.Secondary : ((PlayerInventoryBase)val2).WieldedSlot.ToDamSlotType()) : DamSlotType.Primary);
damSlotType = damSlotType2;
}
else
{
damSlotType = ((PlayerInventoryBase)val2).WieldedSlot.ToDamSlotType();
}
if (damSlotType != DamSlotType.Primary && damSlotType != DamSlotType.Secondary)
{
damSlotType = ((!(_random.NextSingle() < 0.5f)) ? DamSlotType.Secondary : DamSlotType.Primary);
}
}
CacheDamage(__instance, data.limbID, __state, data.source, damSlotType);
}
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveMeleeDamage")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_RecvMeleeDamage(Dam_EnemyDamageBase __instance, pFullDamageData data, float __state)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
if (!CacheDamageHost(__instance, data.limbID, __state))
{
CacheDamage(__instance, data.limbID, __state, data.source, DamSlotType.Melee);
}
}
[HarmonyPatch(typeof(Dam_EnemyDamageBase), "ReceiveExplosionDamage")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_RecvExplosionDamage(Dam_EnemyDamageBase __instance, pExplosionDamageData data, float __state)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
CacheDamageHost(__instance, data.limbID, __state);
}
private static bool CacheDamageHost(Dam_EnemyDamageBase damBase, byte limbID, float prevHealth)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Invalid comparison between Unknown and I4
if (!_cache.Valid)
{
return false;
}
DamStatType stat = (((int)((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)damBase.DamageLimbs)[(int)limbID].m_type == 1) ? DamStatType.Crit : DamStatType.Any);
StatHandler.AddDamage(_cache.lookup, _cache.slot, stat, prevHealth - ((Dam_SyncedDamageBase)damBase).Health);
return true;
}
private static void CacheDamage(Dam_EnemyDamageBase damBase, byte limbID, float prevHealth, pAgent agent, DamSlotType slot)
{
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Invalid comparison between Unknown and I4
Agent val = default(Agent);
if (((pAgent)(ref agent)).TryGet(ref val))
{
ulong lookup = ((Il2CppObjectBase)val).Cast<PlayerAgent>().Owner.Lookup;
DamStatType stat = (((int)((Il2CppArrayBase<Dam_EnemyDamageLimb>)(object)damBase.DamageLimbs)[(int)limbID].m_type == 1) ? DamStatType.Crit : DamStatType.Any);
StatHandler.AddDamage(lookup, slot, stat, prevHealth - ((Dam_SyncedDamageBase)damBase).Health);
}
}
}
[HarmonyPatch]
internal static class NetworkPatches
{
[HarmonyPatch(typeof(PlayerSync), "OnSpawn")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_PlayerSpawn(PlayerSync __instance)
{
ModSyncManager.OnAddPlayer(__instance.Replicator.OwningPlayer);
}
[HarmonyPatch(typeof(PlayerAgent), "OnDespawn")]
[HarmonyWrapSafe]
[HarmonyPrefix]
private static void Pre_PlayerDespawn(PlayerAgent __instance)
{
ModSyncManager.OnRemovePlayer(__instance.Owner);
}
[HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_LeaveHub()
{
StatManager.Clear();
}
}
[HarmonyPatch]
internal static class PagePatches
{
private static bool _isReady = false;
private static readonly CM_PageSuccess_PrisonerEvaluation[] _failReports = (CM_PageSuccess_PrisonerEvaluation[])(object)new CM_PageSuccess_PrisonerEvaluation[4];
private static IntPtr _lastFailPage = IntPtr.Zero;
[InvokeOnAssetLoad]
private static void OnAssetLoad()
{
_isReady = true;
}
[HarmonyPatch(typeof(CM_PageExpeditionSuccess), "Setup")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_PageSetup(CM_PageExpeditionSuccess __instance)
{
//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_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
foreach (CM_PageSuccess_PrisonerEvaluation item in (Il2CppArrayBase<CM_PageSuccess_PrisonerEvaluation>)(object)__instance.m_playerReports)
{
Vector2 sizeDelta = ((TMP_Text)item.m_gear).rectTransform.sizeDelta;
sizeDelta.y += 20f;
((TMP_Text)item.m_gear).rectTransform.sizeDelta = sizeDelta;
sizeDelta = ((TMP_Text)item.m_eval).rectTransform.sizeDelta;
sizeDelta.y += 20f;
((TMP_Text)item.m_eval).rectTransform.sizeDelta = sizeDelta;
}
}
[HarmonyPatch(typeof(CM_PageExpeditionSuccess), "OnEnable")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_PageEnable(CM_PageExpeditionSuccess __instance)
{
if (!_isReady || SNet.Slots.PlayerSlots == null)
{
return;
}
for (int i = 0; i < ((Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots).Length; i++)
{
SNet_Slot val = ((Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots)[i];
if (val != null && !((Object)(object)val.player == (Object)null) && val.player.IsInSlot)
{
SNet_Player player = val.player;
PopulateReport(__instance, ((Il2CppArrayBase<CM_PageSuccess_PrisonerEvaluation>)(object)__instance.m_playerReports)[i], player, isFail: false, i);
}
}
}
[HarmonyPatch(typeof(CM_PageExpeditionFail), "OnEnable")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_PageEnable(CM_PageExpeditionFail __instance)
{
if (!_isReady || SNet.Slots.PlayerSlots == null)
{
return;
}
CreateFailReports(__instance);
CM_PageExpeditionSuccess pageExpeditionSuccess = MainMenuGuiLayer.Current.PageExpeditionSuccess;
Random.InitState(Builder.SessionSeedRandom.Seed);
for (int i = 0; i < ((Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots).Length; i++)
{
SNet_Slot val = ((Il2CppArrayBase<SNet_Slot>)(object)SNet.Slots.PlayerSlots)[i];
CM_PageSuccess_PrisonerEvaluation val2 = _failReports[i];
val2.SetVisible(false);
if (val != null && !((Object)(object)val.player == (Object)null) && val.player.IsInSlot)
{
SNet_Player player = val.player;
PopulateReport(pageExpeditionSuccess, val2, player, isFail: true, i);
}
}
}
private static void CreateFailReports(CM_PageExpeditionFail __instance)
{
//IL_0057: 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_009a: 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_00c7: Expected O, but got Unknown
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_0172: Unknown result type (might be due to invalid IL or missing references)
//IL_0198: Unknown result type (might be due to invalid IL or missing references)
//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
if (_lastFailPage == ((Il2CppObjectBase)__instance).Pointer)
{
return;
}
_lastFailPage = ((Il2CppObjectBase)__instance).Pointer;
Transform transform = ((Component)((CM_PageBase)__instance).m_staticContentHolder).transform;
if (Object.op_Implicit((Object)(object)transform.FindChild("StatDisplay_Align0")))
{
return;
}
__instance.m_artifactInfo_text.transform.localPosition = new Vector3(0f, 2000f, 0f);
((Transform)((CM_PageBase)__instance).m_staticContentHolder).localPosition = new Vector3(0f, 100f, 0f);
((Component)__instance.m_ArtifactInventoryDisplay).transform.localPosition = new Vector3(0f, 100f, 0f);
Transform[] array = (Transform[])(object)new Transform[4];
for (int i = 0; i < array.Length; i++)
{
GameObject val = new GameObject("StatDisplay_Align" + i);
Object.DontDestroyOnLoad((Object)(object)val);
array[i] = val.transform;
array[i].SetParent(transform, false);
array[i].localPosition = new Vector3((float)(-900 + 600 * i), -250f, 0f);
}
GameObject playerReportPrefab = MainMenuGuiLayer.Current.PageExpeditionSuccess.m_playerReportPrefab;
for (int j = 0; j < _failReports.Length; j++)
{
Transform val2 = ((Component)__instance).transform;
if (j < array.Length)
{
val2 = array[j];
}
_failReports[j] = GOUtil.SpawnChildAndGetComp<CM_PageSuccess_PrisonerEvaluation>(playerReportPrefab, val2);
((Component)_failReports[j]).transform.localPosition = Vector3.zero;
((Component)_failReports[j]).transform.localRotation = Quaternion.identity;
((Component)_failReports[j]).transform.localScale = new Vector3(0.8f, 0.8f, 0.8f);
Vector2 sizeDelta = ((TMP_Text)_failReports[j].m_gear).rectTransform.sizeDelta;
sizeDelta.y += 20f;
((TMP_Text)_failReports[j].m_gear).rectTransform.sizeDelta = sizeDelta;
_failReports[j].SetVisible(false);
}
}
private static void PopulateReport(CM_PageExpeditionSuccess successPage, CM_PageSuccess_PrisonerEvaluation report, SNet_Player player, bool isFail, int index)
{
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
StatData data;
bool flag = StatHandler.TryGetData(player, out data);
PlayerBackpack val = default(PlayerBackpack);
if ((!flag && !isFail) || !PlayerBackpackManager.TryGetBackpack(player, ref val))
{
return;
}
if (isFail)
{
report.SetColor(new Color(1f, 1f, 1f, 0.65f));
((TMP_Text)report.m_name).text = "<color=red>" + (player.IsBot ? ("[" + Text.Get(1372u) + "]") : "") + player.GetName() + "</color>";
((TMP_Text)report.m_gearHeader).text = "<size=150%><nobr>" + Text.Get(888u) + "</nobr></size>";
}
bool flag2 = false;
bool flag3 = false;
StringBuilder stringBuilder = new StringBuilder("<color=white>");
string text = default(string);
if (successPage.TryGetArchetypeName(val, (InventorySlot)1, ref text))
{
flag2 = true;
stringBuilder.AppendLine(text + GetDamageText(data, DamSlotType.Primary));
}
string text2 = default(string);
if (successPage.TryGetArchetypeName(val, (InventorySlot)2, ref text2))
{
flag3 = true;
stringBuilder.AppendLine(text2 + GetDamageText(data, DamSlotType.Secondary));
}
string text3 = default(string);
if (successPage.TryGetArchetypeName(val, (InventorySlot)3, ref text3))
{
stringBuilder.AppendLine(text3 + GetDamageText(data, DamSlotType.Tool));
}
if (successPage.TryGetArchetypeName(val, (InventorySlot)10, ref text3))
{
stringBuilder.AppendLine(text3 + GetDamageText(data, DamSlotType.Melee));
}
if (flag)
{
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2);
handler.AppendLiteral("Total: ");
handler.AppendFormatted(GetDamageText(data, DamSlotType.All, includePipe: false));
stringBuilder3.Append(ref handler);
}
stringBuilder.Append("</color>");
((TMP_Text)report.m_gear).text = stringBuilder.ToString();
stringBuilder.Clear();
if (isFail)
{
((TMP_Text)report.m_evalHeader).text = "<size=150%><nobr>" + Text.Get(898u) + "</nobr></size>";
}
else
{
PlayerAgent obj = ((Il2CppObjectBase)player.PlayerAgent).Cast<PlayerAgent>();
float healthRel = ((Dam_SyncedDamageBase)obj.Damage).GetHealthRel();
string text4 = ((healthRel > 0.7f) ? Text.Get(889u) : ((healthRel > 0.5f) ? Text.Get(890u) : ((healthRel > 0.3f) ? Text.Get(891u) : ((!(healthRel > 0f)) ? ("<color=red>" + Text.Get(893u) + "</color>") : ("<color=red>" + Text.Get(892u) + "</color>")))));
float infection = obj.Damage.Infection;
string text5 = ((infection > 0.85f) ? Text.Get(894u) : ((infection > 0.5f) ? Text.Get(895u) : ((!(infection > 0f)) ? Text.Get(897u) : Text.Get(896u))));
stringBuilder.AppendLine(Text.Format(899u, (Object[])(object)new Object[1] { Object.op_Implicit("<color=white>" + text4 + "</color>") }));
stringBuilder.AppendLine(Text.Format(900u, (Object[])(object)new Object[1] { Object.op_Implicit("<color=white>" + text5 + "</color>") }));
}
if (flag && data.HasMod)
{
if (flag2)
{
stringBuilder.AppendLine(text + "<color=white>: " + data.StatText.GetEndscreenText((InventorySlot)1) + "</color>");
}
if (flag3)
{
stringBuilder.AppendLine(text2 + "<color=white>: " + data.StatText.GetEndscreenText((InventorySlot)2) + "</color>");
}
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(15, 1, stringBuilder2);
handler.AppendLiteral("Total: ");
handler.AppendFormatted(data.StatText.GetEndscreenText((InventorySlot)4));
handler.AppendLiteral("</color>");
stringBuilder4.Append(ref handler);
}
else
{
stringBuilder.Append("Missing StatDisplay");
}
((TMP_Text)report.m_eval).text = stringBuilder.ToString();
if (isFail)
{
CoroutineManager.BlinkIn(((Component)report).gameObject, 3f + (float)index * 0.2f);
}
}
private static string GetDamageText(StatData? data, DamSlotType slot, bool includePipe = true)
{
if (data == null)
{
return "";
}
string text = (includePipe ? " | " : "");
if (!data.HasMod && !StatManager.MasterHasMod)
{
return text + "N/A";
}
return text + $"<#a0a0a0>{(ulong)data.GetDamage(slot, DamStatType.Any)}</color> (<#ffff00>{(ulong)data.GetDamage(slot, DamStatType.Crit)}</color>)";
}
}
[HarmonyPatch]
internal static class WOPatches
{
[HarmonyPatch(typeof(WardenObjectiveManager), "OnWinConditionSolved")]
[HarmonyWrapSafe]
[HarmonyPostfix]
private static void Post_WinSolved()
{
StatHandler.ForceSync();
}
}
}
namespace StatDisplay.Networking
{
internal static class ModSyncManager
{
private static readonly NotifySync _notifySync = new NotifySync();
[InvokeOnLoad]
private static void Init()
{
_notifySync.Setup();
}
internal static void OnAddPlayer(SNet_Player player)
{
if (player.IsLocal)
{
StatManager.AddPlayer(player, hasMod: true);
}
else if (!player.IsBot)
{
_notifySync.Send(packetData: true, player, (SNet_ChannelType)0);
StatManager.AddPlayer(player, hasMod: false);
}
else
{
StatManager.AddPlayer(player, StatManager.MasterHasMod);
}
}
internal static void ReceiveNotify(SNet_Player player)
{
StatManager.AddPlayer(player, hasMod: true);
}
internal static void OnRemovePlayer(SNet_Player player)
{
StatManager.RemovePlayer(player);
}
}
internal static class StatSyncManager
{
private static readonly AccDeltaSync _accSync = new AccDeltaSync();
private static readonly DamDeltaSync _damSync = new DamDeltaSync();
[InvokeOnLoad]
private static void Init()
{
_accSync.Setup();
_damSync.Setup();
}
internal static void BroadcastAccData(ulong lookup, StatData data)
{
if (!data.PopRemoteAccDelta(out short[,,] deltaAccuracy))
{
return;
}
foreach (SNet_Player remotePlayer in StatManager.RemotePlayers)
{
_accSync.Send(pAccDeltaData.Create(lookup, deltaAccuracy), remotePlayer, (SNet_ChannelType)4);
}
}
internal static void BroadcastDamData(ulong lookup, StatData data)
{
if (!data.PopRemoteDamDelta(out float[,] deltaDamage))
{
return;
}
foreach (SNet_Player remotePlayer in StatManager.RemotePlayers)
{
_damSync.Send(pDamDeltaData.Create(lookup, deltaDamage), remotePlayer, (SNet_ChannelType)4);
}
}
internal static void ReceiveData(pAccDeltaData data)
{
StatHandler.AddRemoteAccDelta(data.Lookup, data.DeltaData);
}
internal static void ReceiveData(pDamDeltaData data)
{
StatHandler.AddRemoteDamDelta(data.Lookup, data.DeltaData);
}
}
public abstract class SyncedEvent<T> where T : struct
{
public delegate void ReceiveHandler(T packet);
private bool _isSetup;
public abstract string GUID { get; }
public bool IsSetup => _isSetup;
public string EventName { get; private set; } = string.Empty;
public void Setup()
{
if (!_isSetup)
{
EventName = "STAT" + GUID;
NetworkAPI.RegisterEvent<T>(EventName, (Action<ulong, T>)ReceiveClient_Callback);
_isSetup = true;
}
}
public void Send(T packetData, SNet_Player? target = null, SNet_ChannelType priority = (SNet_ChannelType)4)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)target != (Object)null)
{
NetworkAPI.InvokeEvent<T>(EventName, packetData, target, priority);
}
else
{
NetworkAPI.InvokeEvent<T>(EventName, packetData, priority);
}
ReceiveLocal_Callback(packetData);
}
private void ReceiveLocal_Callback(T packet)
{
ReceiveLocal(packet);
}
private void ReceiveClient_Callback(ulong sender, T packet)
{
Receive(sender, packet);
}
protected virtual void ReceiveLocal(T packet)
{
}
protected virtual void Receive(ulong sender, T packet)
{
Receive(packet);
}
protected virtual void Receive(T packet)
{
}
}
}
namespace StatDisplay.Networking.Events
{
internal sealed class AccDeltaSync : SyncedEvent<pAccDeltaData>
{
public override string GUID => "ACCSYN";
protected override void Receive(ulong lookup, pAccDeltaData packet)
{
StatSyncManager.ReceiveData(packet);
}
}
internal sealed class DamDeltaSync : SyncedEvent<pDamDeltaData>
{
public override string GUID => "DAMSYN";
protected override void Receive(ulong lookup, pDamDeltaData packet)
{
StatSyncManager.ReceiveData(packet);
}
}
internal struct pAccDeltaData
{
private const int Count = 18;
public ulong Lookup;
private unsafe fixed short _deltaData[18];
public unsafe short[,,] DeltaData
{
get
{
short[,,] array = new short[2, 3, 3];
int num = 0;
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
for (int k = 0; k < array.GetLength(2); k++)
{
array[i, j, k] = _deltaData[num++];
}
}
}
return array;
}
set
{
int num = 0;
for (int i = 0; i < value.GetLength(0); i++)
{
for (int j = 0; j < value.GetLength(1); j++)
{
for (int k = 0; k < value.GetLength(2); k++)
{
_deltaData[num++] = value[i, j, k];
}
}
}
}
}
public static pAccDeltaData Create(ulong lookup, short[,,] deltaData)
{
return new pAccDeltaData
{
Lookup = lookup,
DeltaData = deltaData
};
}
}
internal struct pDamDeltaData
{
private const int Count = 8;
public ulong Lookup;
private unsafe fixed float _deltaData[8];
public unsafe float[,] DeltaData
{
get
{
float[,] array = new float[4, 2];
int num = 0;
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
array[i, j] = _deltaData[num++];
}
}
return array;
}
set
{
int num = 0;
for (int i = 0; i < value.GetLength(0); i++)
{
for (int j = 0; j < value.GetLength(1); j++)
{
_deltaData[num++] = value[i, j];
}
}
}
}
public static pDamDeltaData Create(ulong lookup, float[,] deltaData)
{
return new pDamDeltaData
{
Lookup = lookup,
DeltaData = deltaData
};
}
}
internal sealed class NotifySync : SyncedEvent<bool>
{
public override string GUID => "NOTIF";
protected override void Receive(ulong lookup, bool _)
{
SNet_Player player = default(SNet_Player);
if (SNet.TryGetPlayer(lookup, ref player))
{
ModSyncManager.ReceiveNotify(player);
}
}
}
}
namespace StatDisplay.Handler
{
public sealed class StatHandler : MonoBehaviour
{
private static readonly (float x, float y) BaseOffset = (x: -70f, y: -62f);
private static readonly float TextMargin = 35f;
private const float SyncInterval = 2f;
private static RectTransform? s_baseRectTransform;
private static TextMeshPro? s_baseTextMesh;
private readonly List<TextMeshPro> _textMeshList = new List<TextMeshPro>();
private readonly List<GameObject> _meshGOList = new List<GameObject>();
private readonly Dictionary<ulong, StatData> _data = new Dictionary<ulong, StatData>();
private float _nextSyncTime;
private bool _visible = true;
[HideFromIl2Cpp]
public static StatHandler Instance { get; private set; } = null;
public StatHandler(IntPtr ptr)
: base(ptr)
{
}
[InvokeOnAssetLoad]
private static void Init()
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
ClassInjector.RegisterTypeInIl2Cpp<StatHandler>();
GameObject val = new GameObject("StatHandler");
Object.DontDestroyOnLoad((Object)val);
Instance = val.AddComponent<StatHandler>();
}
[InvokeOnBuildStart]
private static void ResetValues()
{
foreach (StatData value in Instance._data.Values)
{
value.Reset();
}
}
private void Awake()
{
Instance = this;
EnsureMeshCapacity(4);
Configuration.Config.OnChanged += ResetMeshOffsets;
}
[HideFromIl2Cpp]
public static bool TryGetData(SNet_Player player, [MaybeNullWhen(false)] out StatData data)
{
return Instance._data.TryGetValue(player.Lookup, out data);
}
[HideFromIl2Cpp]
public static void AddAccuracyDelta(ulong lookup, AccSlotType slot, short[,] delta)
{
if (Instance._data.TryGetValue(lookup, out StatData value))
{
value.AddAccuracy(slot, delta);
}
}
[HideFromIl2Cpp]
public static void AddDamage(ulong lookup, DamSlotType slot, DamStatType stat, float value)
{
if (Instance._data.TryGetValue(lookup, out StatData value2))
{
value2.AddDamage(slot, stat, value);
}
}
public static void AddRemoteAccDelta(ulong lookup, short[,,] deltaAccuracy)
{
if (Instance._data.TryGetValue(lookup, out StatData value))
{
value.AddRemoteAccDelta(deltaAccuracy);
}
}
public static void AddRemoteDamDelta(ulong lookup, float[,] deltaDamage)
{
if (Instance._data.TryGetValue(lookup, out StatData value))
{
value.AddRemoteDamDelta(deltaDamage);
}
}
internal static void AddPlayer(SNet_Player player, bool hasMod = true, bool onlyIfExists = false)
{
Instance.AddPlayer_Internal(player, hasMod, onlyIfExists);
}
private void AddPlayer_Internal(SNet_Player player, bool hasMod = true, bool onlyIfExists = false)
{
if (_data.TryGetValue(player.Lookup, out StatData value))
{
if (value.HasMod == hasMod)
{
return;
}
value.HasMod = hasMod;
}
else
{
if (onlyIfExists)
{
return;
}
_data.Add(player.Lookup, new StatData(player, hasMod));
}
RefreshMeshList();
}
internal static void RemovePlayer(SNet_Player player)
{
Instance.RemovePlayer_Internal(player);
}
private void RemovePlayer_Internal(SNet_Player player)
{
if (_data.Remove(player.Lookup, out StatData value) && (value.HasMod || StatManager.MasterHasMod))
{
RefreshMeshList();
}
}
internal static void Clear()
{
Instance.Clear_Internal();
}
private void Clear_Internal()
{
_data.Clear();
for (int i = 0; i < 4; i++)
{
_meshGOList[i].SetActive(false);
}
for (int j = 4; j < _meshGOList.Count; j++)
{
Object.Destroy((Object)(object)_meshGOList[j]);
}
if (_meshGOList.Count > 4)
{
_textMeshList.RemoveRange(4, _meshGOList.Count - 4);
_meshGOList.RemoveRange(4, _meshGOList.Count - 4);
}
}
internal static void OnMasterSet()
{
Instance.RefreshMeshList();
}
internal static void SetVisible(bool visible)
{
Instance.SetVisible_Internal(visible);
}
private void SetVisible_Internal(bool visible)
{
if (visible == _visible)
{
return;
}
_visible = visible;
if (visible)
{
RefreshMeshList();
return;
}
foreach (GameObject meshGO in _meshGOList)
{
meshGO.SetActive(false);
}
}
private void RefreshMeshList()
{
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
if (!_visible)
{
return;
}
foreach (GameObject meshGO in _meshGOList)
{
meshGO.SetActive(false);
}
int num = 0;
Il2CppReferenceArray<SNet_Slot> playerSlots = SNet.Slots.PlayerSlots;
for (int i = 0; i < ((Il2CppArrayBase<SNet_Slot>)(object)playerSlots).Count; i++)
{
if ((Object)(object)((Il2CppArrayBase<SNet_Slot>)(object)playerSlots)[i].player == (Object)null)
{
continue;
}
ulong lookup = ((Il2CppArrayBase<SNet_Slot>)(object)playerSlots)[i].player.Lookup;
if (_data.TryGetValue(lookup, out StatData value))
{
if (value.HasMod || StatManager.MasterHasMod)
{
EnsureMeshCapacity(num + 1);
value.StatText.TextMesh = _textMeshList[num];
_meshGOList[num].transform.localPosition = GetMeshPos(num);
_meshGOList[num].SetActive(true);
num++;
}
else
{
value.StatText.TextMesh = null;
}
}
}
}
internal static void ForceSync()
{
Instance.DoSync();
}
private void DoSync()
{
_nextSyncTime = Clock.Time + 2f;
foreach (var (lookup, data) in _data)
{
StatSyncManager.BroadcastAccData(lookup, data);
StatSyncManager.BroadcastDamData(lookup, data);
}
}
private void Update()
{
if (_data.Count == 0)
{
return;
}
foreach (StatData value in _data.Values)
{
value.StatText.UpdateMesh();
}
if (!(Clock.Time < _nextSyncTime))
{
DoSync();
}
}
private void EnsureMeshCapacity(int num)
{
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_0149: Unknown result type (might be due to invalid IL or missing references)
//IL_0153: Expected O, but got Unknown
//IL_019d: Unknown result type (might be due to invalid IL or missing references)
num -= _textMeshList.Count;
if (num <= 0)
{
return;
}
if ((Object)(object)s_baseRectTransform == (Object)null)
{
PUI_Inventory inventory = GuiManager.Current.m_playerLayer.Inventory;
s_baseTextMesh = inventory.m_inventorySlots[(InventorySlot)10].m_slim_archetypeName;
foreach (RectTransform componentsInChild in ((Component)inventory.m_iconDisplay).GetComponentsInChildren<RectTransform>(true))
{
if (((Object)componentsInChild).name == "Background Fade")
{
s_baseRectTransform = componentsInChild;
break;
}
}
if ((Object)(object)s_baseRectTransform == (Object)null)
{
return;
}
}
for (int i = 0; i < num; i++)
{
GameObject obj = Object.Instantiate<GameObject>(((Component)s_baseRectTransform).gameObject, ((Transform)s_baseRectTransform).parent);
RectTransform component = obj.GetComponent<RectTransform>();
obj.gameObject.SetActive(true);
foreach (Transform componentsInChild2 in obj.GetComponentsInChildren<Transform>(true))
{
if (((Object)componentsInChild2).name == "TimerShowObject")
{
((Component)componentsInChild2).gameObject.active = false;
}
}
GameObject val = new GameObject("StatDisplay" + _textMeshList.Count)
{
layer = 5,
hideFlags = (HideFlags)61
};
val.transform.SetParent(((Component)component).transform, false);
TextMeshPro val2 = Object.Instantiate<TextMeshPro>(s_baseTextMesh, val.transform);
((TMP_Text)val2).m_width = ((TMP_Text)val2).m_width * 2f;
((Component)val2).GetComponent<RectTransform>().anchoredPosition = new Vector2(-5f, 9f);
((TMP_Text)val2).SetText("-", true);
((TMP_Text)val2).ForceMeshUpdate(false, false);
_textMeshList.Add(val2);
_meshGOList.Add(((Component)val2.transform.parent.parent).gameObject);
List<GameObject> meshGOList = _meshGOList;
meshGOList[meshGOList.Count - 1].SetActive(false);
}
}
private static Vector3 GetMeshPos(int i)
{
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
return new Vector3(BaseOffset.x + Configuration.Config.Offset.X, BaseOffset.y + Configuration.Config.Offset.Y - (float)i * TextMargin, 0f);
}
private void ResetMeshOffsets()
{
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < _textMeshList.Count; i++)
{
_meshGOList[i].transform.localPosition = new Vector3(BaseOffset.x + Configuration.Config.Offset.X, BaseOffset.y + Configuration.Config.Offset.Y - (float)i * TextMargin, 0f);
}
}
}
}
namespace StatDisplay.Dependencies
{
internal static class ArchiveWrapper
{
public const string PLUGIN_GUID = "dev.AuriRex.gtfo.TheArchive";
public static readonly bool HasArchive;
static ArchiveWrapper()
{
HasArchive = ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("dev.AuriRex.gtfo.TheArchive");
}
}
internal static class EWCWrapper
{
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static AccuracyCallback <>9__6_0;
internal void <AddCallbacks_Unsafe>b__6_0(WeaponAccuracy accuracy, WeaponDelta delta)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//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_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
if ((int)accuracy.Slot != 3)
{
<>c__DisplayClass6_0 <>c__DisplayClass6_ = default(<>c__DisplayClass6_0);
<>c__DisplayClass6_.deltaArr = new short[3, 3];
<AddCallbacks_Unsafe>g__Copy|6_1(AccShotType.Shot, delta.Shots, ref <>c__DisplayClass6_);
<AddCallbacks_Unsafe>g__Copy|6_1(AccShotType.Group, delta.Groups, ref <>c__DisplayClass6_);
<AddCallbacks_Unsafe>g__Copy|6_1(AccShotType.Full, delta.FullShots, ref <>c__DisplayClass6_);
StatHandler.AddAccuracyDelta(accuracy.ParentStats.Owner.Owner.Lookup, accuracy.Slot.ToAccSlotType(), <>c__DisplayClass6_.deltaArr);
}
}
}
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <>c__DisplayClass6_0
{
public short[,] deltaArr;
}
public const string PLUGIN_GUID = "Dinorush.ExtraWeaponCustomization";
public static readonly bool HasEWC;
private static float _healthState;
static EWCWrapper()
{
HasEWC = ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("Dinorush.ExtraWeaponCustomization");
if (HasEWC)
{
AddCallbacks_Unsafe();
}
}
public static bool HasCWC(BulletWeapon weapon)
{
if (HasEWC)
{
return HasCWC_Unsafe(weapon);
}
return false;
}
public static void AddProjectileHitCallback(Action<ulong, DamSlotType> hitCallback)
{
if (HasEWC)
{
AddHitCallback_Unsafe(hitCallback);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool HasCWC_Unsafe(BulletWeapon weapon)
{
return (Object)(object)((Component)weapon).GetComponent<CustomWeaponComponent>() != (Object)null;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void AddCallbacks_Unsafe()
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Expected O, but got Unknown
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Expected O, but got Unknown
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Expected O, but got Unknown
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Expected O, but got Unknown
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Expected O, but got Unknown
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Expected O, but got Unknown
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Expected O, but got Unknown
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Expected O, but got Unknown
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
object obj = <>c.<>9__6_0;
if (obj == null)
{
AccuracyCallback val = delegate(WeaponAccuracy accuracy, WeaponDelta delta)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//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_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
short[,] deltaArr;
if ((int)accuracy.Slot != 3)
{
deltaArr = new short[3, 3];
Copy(AccShotType.Shot, delta.Shots);
Copy(AccShotType.Group, delta.Groups);
Copy(AccShotType.Full, delta.FullShots);
StatHandler.AddAccuracyDelta(accuracy.ParentStats.Owner.Owner.Lookup, accuracy.Slot.ToAccSlotType(), deltaArr);
}
void Copy(AccShotType shotType, StatDelta stat)
{
//IL_0008: 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_0030: Unknown result type (might be due to invalid IL or missing references)
deltaArr[(uint)shotType, 0] = (short)stat.Hits;
deltaArr[(uint)shotType, 1] = (short)stat.Crits;
deltaArr[(uint)shotType, 2] = (short)stat.Count;
}
};
<>c.<>9__6_0 = val;
obj = (object)val;
}
AccuracyAPI.OnAccuracyUpdate += (AccuracyCallback)obj;
DamageAPI.PreLocalDOTDamage += new DamageCallback(RecordDamageLocal);
DamageAPI.PreLocalExplosiveDamage += new DamageCallback(RecordDamageLocal);
DamageAPI.PreLocalShrapnelDamage += new DamageCallback(RecordDamageLocal);
DamageAPI.PreDOTDamage += new DamageCallback(RecordPreDamageHost);
DamageAPI.PreExplosiveDamage += new DamageCallback(RecordPreDamageHost);
DamageAPI.PreShrapnelDamage += new DamageCallback(RecordPreDamageHost);
DamageAPI.PostDOTDamage += new DamageCallback(RecordPostDamageHost);
DamageAPI.PostExplosiveDamage += new DamageCallback(RecordPostDamageHost);
DamageAPI.PostShrapnelDamage += new DamageCallback(RecordPostDamageHost);
}
private static void RecordDamageLocal(float damage, EnemyAgent enemy, Dam_EnemyDamageLimb limb, PlayerAgent? source, pCWC cwc)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Invalid comparison between Unknown and I4
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
if (!StatManager.MasterHasMod)
{
DamStatType stat = (((int)limb.m_type == 1) ? DamStatType.Crit : DamStatType.Any);
StatHandler.AddDamage(source.Owner.Lookup, cwc.slot.ToDamSlotType(), stat, Math.Min(damage, ((Dam_SyncedDamageBase)limb.m_base).Health));
}
}
private static void RecordPreDamageHost(float damage, EnemyAgent enemy, Dam_EnemyDamageLimb limb, PlayerAgent? source, pCWC cwc)
{
_healthState = ((Dam_SyncedDamageBase)limb.m_base).Health;
}
private static void RecordPostDamageHost(float damage, EnemyAgent enemy, Dam_EnemyDamageLimb limb, PlayerAgent? source, pCWC cwc)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_0019: 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)
DamStatType stat = (((int)limb.m_type == 1) ? DamStatType.Crit : DamStatType.Any);
StatHandler.AddDamage(source.Owner.Lookup, cwc.slot.ToDamSlotType(), stat, _healthState - ((Dam_SyncedDamageBase)limb.m_base).Health);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void AddHitCallback_Unsafe(Action<ulong, DamSlotType> hitCallback)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
ProjectileAPI.OnProjectileHit += (ProjectileHitCallback)delegate(EWCProjectileComponentBase comp, IDamageable? damageable)
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
CustomWeaponComponent cWC = ((WeaponPropertyBase)comp.Settings).CWC;
if (!((Object)(object)cWC.Owner.Player == (Object)null))
{
hitCallback(cWC.Owner.Player.Owner.Lookup, cWC.Weapon.AmmoType.ToDamSlotType());
}
};
}
[CompilerGenerated]
internal static void <AddCallbacks_Unsafe>g__Copy|6_1(AccShotType shotType, StatDelta stat, ref <>c__DisplayClass6_0 P_2)
{
//IL_0008: 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_0030: Unknown result type (might be due to invalid IL or missing references)
P_2.deltaArr[(uint)shotType, 0] = (short)stat.Hits;
P_2.deltaArr[(uint)shotType, 1] = (short)stat.Crits;
P_2.deltaArr[(uint)shotType, 2] = (short)stat.Count;
}
}
}
namespace StatDisplay.Data
{
public enum AccSlotType : byte
{
Primary = 0,
Main = 0,
Secondary = 1,
Special = 1,
Count = 2,
All = 3
}
public enum AccShotType : byte
{
Shot,
Group,
Full,
Count
}
public enum AccStatType : byte
{
Hit,
Crit,
Fired,
Count
}
public enum DamSlotType : byte
{
Primary = 0,
Main = 0,
Secondary = 1,
Special = 1,
Tool = 2,
Class = 2,
Melee = 3,
Count = 4,
All = 5
}
public enum DamStatType : byte
{
Any,
Crit,
Count
}
public sealed class StatData
{
public abstract class Retriever
{
protected readonly StatData _data;
public abstract long Value { get; }
public Retriever(StatData data)
{
_data = data;
}
}
private class AccuracyRetriever : Retriever
{
private readonly int _slotType;
private readonly int _shotType;
private readonly int _statType;
private readonly bool _isAllSlot;
public override long Value
{
get
{
if (_isAllSlot)
{
return _data._accData[0, _shotType, _statType] + _data._accData[1, _shotType, _statType];
}
return _data._accData[_slotType, _shotType, _statType];
}
}
public AccuracyRetriever(StatData data, AccSlotType slotType, AccShotType shotType, AccStatType statType)
: base(data)
{
_slotType = (int)slotType;
_shotType = (int)shotType;
_statType = (int)statType;
_isAllSlot = slotType == AccSlotType.All;
}
}
private class DamageRetriever : Retriever
{
private readonly int _slotType;
private readonly int _statType;
private readonly bool _isAllSlot;
public override long Value
{
get
{
double num = 0.0;
if (_isAllSlot)
{
for (int i = 0; i < 4; i++)
{
num += _data._damData[i, _statType];
}
}
else
{
num += _data._damData[_slotType, _statType];
}
return (long)num;
}
}
public DamageRetriever(StatData data, DamSlotType slotType, DamStatType statType)
: base(data)
{
_slotType = (int)slotType;
_statType = (int)statType;
_isAllSlot = slotType == DamSlotType.All;
}
}
private readonly long[,,] _accData;
private readonly short[,,] _deltaAccData;
private readonly double[,] _damData;
private readonly float[,] _deltaDamData;
private bool _needsAccSync;
private bool _needsDamSync;
public readonly StatParser StatText;
public bool HasMod
{
get
{
return StatText.HasMod;
}
set
{
StatText.HasMod = value;
}
}
public StatData(SNet_Player player, bool hasMod)
{
_accData = new long[2, 3, 3];
_deltaAccData = new short[2, 3, 3];
_damData = new double[4, 2];
_deltaDamData = new float[4, 2];
StatText = new StatParser(this, player, hasMod);
}
public long GetAccuracy(AccSlotType slot, AccShotType shot, AccStatType stat)
{
if (slot == AccSlotType.All)
{
return _accData[0, (uint)shot, (uint)stat] + _accData[1, (uint)shot, (uint)stat];
}
return _accData[(uint)slot, (uint)shot, (uint)stat];
}
public double GetDamage(DamSlotType slot, DamStatType stat)
{
double num = 0.0;
if (slot == DamSlotType.All)
{
for (int i = 0; i < 4; i++)
{
num += _damData[i, (uint)stat];
}
}
else
{
num += _damData[(uint)slot, (uint)stat];
}
return num;
}
public void Reset()
{
for (int i = 0; i < _accData.GetLength(0); i++)
{
for (int j = 0; j < _accData.GetLength(1); j++)
{
for (int k = 0; k < _accData.GetLength(2); k++)
{
_accData[i, j, k] = 0L;
_deltaAccData[i, j, k] = 0;
}
}
}
_needsAccSync = false;
_needsDamSync = false;
StatText.Update(updateMesh: true);
}
public void AddAccuracy(AccSlotType slot, short[,] deltaData)
{
_needsAccSync = true;
for (int i = 0; i < deltaData.GetLength(0); i++)
{
for (int j = 0; j < deltaData.GetLength(1); j++)
{
short num = deltaData[i, j];
_accData[(uint)slot, i, j] += num;
_deltaAccData[(uint)slot, i, j] += num;
}
}
StatText.Update();
}
public void AddDamage(DamSlotType slot, DamStatType stat, float value)
{
_needsDamSync = true;
_damData[(uint)slot, (uint)stat] += value;
_deltaDamData[(uint)slot, (uint)stat] += value;
if (stat != DamStatType.Any)
{
_damData[(uint)slot, 0] += value;
_deltaDamData[(uint)slot, 0] += value;
}
StatText.Update();
}
public bool PopRemoteAccDelta([MaybeNullWhen(false)] out short[,,] deltaAccuracy)
{
if (!_needsAccSync)
{
deltaAccuracy = null;
return false;
}
deltaAccuracy = (short[,,])_deltaAccData.Clone();
for (int i = 0; i < _deltaAccData.GetLength(0); i++)
{
for (int j = 0; j < _deltaAccData.GetLength(1); j++)
{
for (int k = 0; k < _deltaAccData.GetLength(2); k++)
{
_deltaAccData[i, j, k] = 0;
}
}
}
_needsAccSync = false;
return true;
}
public bool PopRemoteDamDelta([MaybeNullWhen(false)] out float[,] deltaDamage)
{
if (!_needsDamSync)
{
deltaDamage = null;
return false;
}
deltaDamage = (float[,])_deltaDamData.Clone();
for (int i = 0; i < _deltaDamData.GetLength(0); i++)
{
for (int j = 0; j < _deltaDamData.GetLength(1); j++)
{
_deltaDamData[i, j] = 0f;
}
}
_needsDamSync = false;
return true;
}
public void AddRemoteAccDelta(short[,,] deltaData)
{
for (int i = 0; i < deltaData.GetLength(0); i++)
{
for (int j = 0; j < deltaData.GetLength(1); j++)
{
for (int k = 0; k < deltaData.GetLength(2); k++)
{
_accData[i, j, k] += deltaData[i, j, k];
}
}
}
StatText.Update(updateMesh: true);
}
public void AddRemoteDamDelta(float[,] deltaDamage)
{
for (int i = 0; i < deltaDamage.GetLength(0); i++)
{
for (int j = 0; j < deltaDamage.GetLength(1); j++)
{
_damData[i, j] += deltaDamage[i, j];
}
}
StatText.Update(updateMesh: true);
}
public static Retriever CreateAccuracyRetriever(StatData data, AccSlotType slotType, AccShotType shotType, AccStatType statType)
{
return new AccuracyRetriever(data, slotType, shotType, statType);
}
public static Retriever CreateDamageRetriever(StatData data, DamSlotType slotType, DamStatType statType)
{
return new DamageRetriever(data, slotType, statType);
}
}
public sealed class StatParser
{
private class Token
{
private static readonly string[] s_accSlotTypes = Array.ConvertAll(Enum.GetValues<AccSlotType>()[..2], (AccSlotType slot) => slot.ToString());
private static readonly string[] s_accShotTypes = Array.ConvertAll(Enum.GetValues<AccShotType>()[..3], (AccShotType shot) => shot.ToString());
private static readonly string[] s_accStatTypes = Array.ConvertAll(Enum.GetValues<AccStatType>()[..3], (AccStatType stat) => stat.ToString());
private static readonly string[] s_damSlotTypes = Array.ConvertAll(Enum.GetValues<DamSlotType>()[..4], (DamSlotType slot) => slot.ToString());
private static readonly string[] s_damStatTypes = Array.ConvertAll(Enum.GetValues<DamStatType>()[..2], (DamStatType stat) => stat.ToString());
private readonly StatData.Retriever _base;
private readonly StatData.Retriever? _divider;
public Token(StatData data, string text, InventorySlot slot)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
int num = text.IndexOf('/');
if (num != -1)
{
_divider = ParseRetriever(data, text.Substring(num + 1), slot);
text = text.Substring(0, num);
}
else
{
_divider = null;
}
_base = ParseRetriever(data, text, slot);
}
public string GetString()
{
long value = _base.Value;
if (_divider != null)
{
long value2 = _divider.Value;
if (value2 == 0L)
{
if (value <= 0)
{
return "-";
}
return "100%";
}
return ((double)value / (double)value2).ToString("P0");
}
return value.ToString();
}
private static StatData.Retriever ParseRetriever(StatData data, string text, InventorySlot slot)
{
//IL_0019: 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)
if (!text.Contains("Damage", StringComparison.CurrentCultureIgnoreCase))
{
return ParseAccuracyRetriever(data, text, slot);
}
return ParseDamageRetriever(data, text, slot);
}
private static StatData.Retriever ParseDamageRetriever(StatData data, string text, InventorySlot slot)
{
//IL_0000: 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)
DamSlotType value;
if ((int)slot != 0)
{
value = slot.ToDamSlotType();
}
else if (!TryGetEnumInText<DamSlotType>(text, s_damSlotTypes, out value))
{
value = DamSlotType.All;
}
if (!TryGetEnumInText<DamStatType>(text, s_damStatTypes, out var value2))
{
value2 = DamStatType.Any;
}
return StatData.CreateDamageRetriever(data, value, value2);
}
private static StatData.Retriever ParseAccuracyRetriever(StatData data, string text, InventorySlot slot)
{
//IL_0000: 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)
AccSlotType value;
if ((int)slot != 0)
{
value = slot.ToAccSlotType();
}
else if (!TryGetEnumInText<AccSlotType>(text, s_accSlotTypes, out value))
{
value = AccSlotType.All;
}
if (!TryGetEnumInText<AccShotType>(text, s_accShotTypes, out var value2))
{
value2 = AccShotType.Shot;
}
if (!TryGetEnumInText<AccStatType>(text, s_accStatTypes, out var value3))
{
value3 = AccStatType.Hit;
}
return StatData.CreateAccuracyRetriever(data, value, value2, value3);
}
private static bool TryGetEnumInText<T>(string text, string[] enumStrings, out T value) where T : struct
{
int num = -1;
int length = 0;
foreach (string text2 in enumStrings)
{
if ((num = text.IndexOf(text2)) > -1)
{
length = text2.Length;
break;
}
}
if (num != -1)
{
if (text.Substring(num, length).TryToEnum<T>(out value))
{
return true;
}
DinoLogger.Error($"Unable to parse {typeof(T).Name} in token \"{text}\"");
return false;
}
value = default(T);
return false;
}
}
private const char StartToken = '{';
private const char EndToken = '}';
private const string NoModFormat = "N/A (<#a0a0a0>{Damage}</color>)";
public static readonly string[] SlotNames = new string[5] { "RED", "GRE", "BLU", "PUR", "EX" };
private List<string> _strings;
private List<Token> _tokens;
private StringBuilder _builder;
private readonly StatData _parent;
private readonly int _characterSlot;
private readonly string _characterName;
private string _text = "";
private TextMeshPro? _textMesh;
private bool _hasMod;
private bool _needsUpdate;
private bool _meshNeedsUpdate;
private string FormattedSlotName
{
get
{
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
string value = ((!Configuration.Config.FormatFullName) ? ((_characterSlot < 4) ? SlotNames[_characterSlot] : (SlotNames[^1] + (_characterSlot - 3))) : _characterName);
return $"<#{ColorUtility.ToHtmlStringRGB(((Il2CppArrayBase<Color>)(object)PlayerManager.Current.m_playerColors)[_characterSlot])}>{value}</color>: ";
}
}
public string Text
{
get
{
if (_needsUpdate)
{
UpdateInner();
}
return _text;
}
}
public TextMeshPro? TextMesh
{
get
{
return _textMesh;
}
set
{
_textMesh = value;
Update(updateMesh: true);
}
}
public bool HasMod
{
get
{
return _hasMod;
}
set
{
_hasMod = value;
SetFormat();
}
}
public StatParser(StatData data, SNet_Player player, bool hasMod)
{
_characterSlot = player.CharacterIndex;
_characterName = player.NickName;
_parent = data;
_hasMod = hasMod;
SetFormat();
Configuration.Config.OnChanged += SetFormat;
}
public void UpdateMesh()
{
if (_meshNeedsUpdate)
{
_meshNeedsUpdate = false;
TextMeshPro? textMesh = TextMesh;
if (textMesh != null)
{
((TMP_Text)textMesh).SetText(Text, true);
}
}
}
public void Update(bool updateMesh = false)
{
_needsUpdate = true;
_meshNeedsUpdate = !updateMesh;
if (updateMesh)
{
TextMeshPro? textMesh = TextMesh;
if (textMesh != null)
{
((TMP_Text)textMesh).SetText(Text, true);
}
}
}
public string GetEndscreenText(InventorySlot slot)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
ExtractFormatInfo(Configuration.Config.EndScreenFormat, out StringBuilder builder, out List<string> strings, out List<Token> tokens, slot);
return CreateText(builder, strings, tokens);
}
[MemberNotNull(new string[] { "_builder", "_strings", "_tokens" })]
private void SetFormat()
{
ExtractFormatInfo(_hasMod ? Configuration.Config.Format : "N/A (<#a0a0a0>{Damage}</color>)", out _builder, out _strings, out _tokens, (InventorySlot)0);
Update();
}
private void ExtractFormatInfo(string format, out StringBuilder builder, out List<string> strings, out List<Token> tokens, InventorySlot restrictSlot = (InventorySlot)0)
{
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
strings = new List<string>();
tokens = new List<Token>();
int num = 0;
int num2 = format.IndexOf('{');
while (num2 != -1)
{
int num3 = format.IndexOf('}', num2 + 1);
if (num3 == -1)
{
break;
}
List<string> obj = strings;
int num4 = num;
obj.Add(format.Substring(num4, num2 - num4));
List<Token> obj2 = tokens;
StatData parent = _parent;
num4 = num2 + 1;
obj2.Add(new Token(parent, format.Substring(num4, num3 - num4), restrictSlot));
num = num3 + 1;
num2 = format.IndexOf('{', num3 + 1);
}
strings.Add(format.Substring(num));
builder = new StringBuilder(6 + _strings.Sum((string segment) => segment.Length) + _tokens.Count * 11);
}
private void UpdateInner()
{
_builder.Append(FormattedSlotName);
_text = CreateText(_builder, _strings, _tokens);
_needsUpdate = false;
}
private static string CreateText(StringBuilder builder, List<string> strings, List<Token> tokens)
{
for (int i = 0; i < strings.Count; i++)
{
builder.Append(strings[i]);
if (i < tokens.Count)
{
builder.Append(tokens[i].GetString());
}
}
string result = builder.ToString();
builder.Clear();
return result;
}
}
}
namespace StatDisplay.Config
{
public static class Configuration
{
public static readonly IConfiguration Config;
[InvokeOnLoad]
private static void Init()
{
}
static Configuration()
{
if (ArchiveWrapper.HasArchive)
{
Config = LoadArchive();
}
else
{
Config = new VanillaConfiguration();
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static IConfiguration LoadArchive()
{
StatDisplayModule.Load();
return StatDisplayFeature.Instance;
}
}
public interface IConfiguration
{
static readonly string[] DefaultColors = new string[5] { "#a0a0a0", "#ffff00", "#ff3080", "#a0a0a0", "#ffff00" };
const PresetConfigSetting DefaultFormat = PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage;
const PresetConfigSetting DefaultEndScreenFormat = PresetConfigSetting.Hit | PresetConfigSetting.Crit;
string Format { get; }
bool FormatFullName { get; }
Vector2 Offset { get; }
string EndScreenFormat { get; }
event Action? OnChanged;
static string BuildPresetFormat(PresetConfigSetting settings)
{
return BuildPresetFormat(settings, DefaultColors);
}
static string BuildPresetFormat(PresetConfigSetting settings, string[] colors)
{
StringBuilder sb = new StringBuilder();
if (settings.HasFlag(PresetConfigSetting.Hit))
{
StringBuilder stringBuilder = sb;
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(21, 1, stringBuilder);
handler.AppendLiteral("<");
handler.AppendFormatted(colors[0]);
handler.AppendLiteral(">{Hit/Fired}</color>");
stringBuilder2.Append(ref handler);
}
if (settings.HasFlag(PresetConfigSetting.Crit))
{
AppendPrefixNotFirst('/', "<" + colors[1] + ">{Crit/Hit}</color>");
}
if (settings.HasFlag(PresetConfigSetting.Pierce))
{
AppendPrefixNotFirst('/', "<" + colors[2] + ">{FullHit/Hit}</color>");
}
if (settings.HasFlag(PresetConfigSetting.Damage))
{
AppendPrefixNotFirst(' ', "(<" + colors[3] + ">{Damage}</color>");
if (settings.HasFlag(PresetConfigSetting.CritDamage))
{
StringBuilder stringBuilder = sb;
StringBuilder stringBuilder3 = stringBuilder;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(23, 1, stringBuilder);
handler.AppendLiteral("/<");
handler.AppendFormatted(colors[4]);
handler.AppendLiteral(">{DamageCrit}</color>");
stringBuilder3.Append(ref handler);
}
sb.Append(')');
}
else if (settings.HasFlag(PresetConfigSetting.CritDamage))
{
AppendPrefixNotFirst(' ', "(<" + colors[4] + ">{DamageCrit}</color>)");
}
return sb.ToString();
internal void AppendPrefixNotFirst(char prefix, string value)
{
if (sb.Length == 0)
{
sb.Append(value);
}
else
{
sb.Append(prefix);
sb.Append(value);
}
}
}
static Vector2 ParseOffset(string offset)
{
if (!offset.Contains(','))
{
return Vector2.Zero;
}
string[] array = offset.Split(',');
if (!float.TryParse(array[0].Trim(), out var result) || !float.TryParse(array[1].Trim(), out var result2))
{
return Vector2.Zero;
}
return new Vector2(result, result2);
}
}
[Flags]
public enum PresetConfigSetting
{
None = 0,
Hit = 1,
Crit = 2,
Pierce = 4,
Damage = 8,
CritDamage = 0x10
}
}
namespace StatDisplay.Config.Vanilla
{
public class VanillaConfiguration : IConfiguration
{
private readonly ConfigEntry<PresetConfigSetting> _format;
private readonly ConfigEntry<bool> _overrideFormat;
private readonly ConfigEntry<bool> _formatFullName;
private readonly ConfigEntry<string> _customFormat;
private readonly ConfigEntry<string> _offset;
private readonly string[] _colors = (string[])IConfiguration.DefaultColors.Clone();
private readonly ConfigEntry<string> _hitColor;
private readonly ConfigEntry<string> _critColor;
private readonly ConfigEntry<string> _pierceColor;
private readonly ConfigEntry<string> _damageColor;
private readonly ConfigEntry<string> _critDamageColor;
private readonly ConfigEntry<PresetConfigSetting> _endScreenFormat;
private readonly ConfigEntry<bool> _endScreenOverrideFormat;
private readonly ConfigEntry<string> _endScreenCustomFormat;
private readonly ConfigFile _configFile;
public string Format { get; private set; }
public bool FormatFullName => _formatFullName.Value;
public Vector2 Offset { get; private set; }
public string EndScreenFormat { get; private set; }
public event Action? OnChanged;
public VanillaConfiguration()
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected O, but got Unknown
//IL_022b: Unknown result type (might be due to invalid IL or missing references)
//IL_0235: Expected O, but got Unknown
_configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "StatDisplay.cfg"), true);
string text = "Display Settings";
string text2 = "Preset display settings to use.\n- Hit: Shots Hit/Shots Fired\n- Crit: Shots Crit/Shots Hit\n- Pierce: All Hits/Shots Hit\n- CritDamage: Crit Damage Dealt";
_format = _configFile.Bind<PresetConfigSetting>(text, "Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage, text2);
text2 = "Show full player names instead of shorthand names.";
_formatFullName = _configFile.Bind<bool>(text, "Display Full Names", false, text2);
text2 = "x, y offset of stat data.";
_offset = _configFile.Bind<string>(text, "Display Offset", "0, 0", text2);
text2 = "Preset display format to use for the mission end screen. Displayed for each weapon.";
_endScreenFormat = _configFile.Bind<PresetConfigSetting>(text, "End Screen Display Format", PresetConfigSetting.Hit | PresetConfigSetting.Crit, text2);
text = "Preset Colors";
text2 = "Color for Hit preset setting.";
_hitColor = _configFile.Bind<string>(text, "Hit Color", _colors[0], text2);
text2 = "Color for Crit preset setting.";
_critColor = _configFile.Bind<string>(text, "Crit Color", _colors[1], text2);
text2 = "Color for Pierce preset setting.";
_pierceColor = _configFile.Bind<string>(text, "Pierce Color", _colors[2], text2);
text2 = "Color for Damage preset setting.";
_damageColor = _configFile.Bind<string>(text, "Damage Color", _colors[3], text2);
text2 = "Color for CritDamage preset setting.";
_critDamageColor = _configFile.Bind<string>(text, "CritDamage Color", _colors[4], text2);
text = "Custom Settings";
text2 = "Whether to use the custom format instead of the preset format.";
_overrideFormat = _configFile.Bind<bool>(text, "Use Custom Format", false, text2);
text2 = "Custom display format to use. Use variables by placing them in \"{}\", e.g. {PrimaryShotHit}.\nPercentages can be done by separating two variables with a '/', e.g. {FullHit/GroupFired}.\nVariables are composed of slot type, shot type, and stat type. They may occur in any order and are case-insensitive.\nAccepted slots (Unspecified = All):\n- All\n- Primary = Main\n- Secondary = Special\n- Melee (Damage Only)\n- Tool = Class (Damage Only)\nAccepted shot types (Unspecified = Shots; Unused for Damage):\n- Shot: Counts for each individual bullet.\n- Group: Counts for each weapon shot (once per shotgun blast).\n- Full: Includes all hits. Counts each damaging effect as a shot fired (including EWC effects).\nAccepted accuracy stat types (Unspecified = Hit):\n- Hit: Number of hits.\n- Crit: Number of crits.\n- Fired: Number of times fired.\nAccepted damage stat types (Unspecified = Any):\n- Any: Damage from any hit.\n- Crit: Damage from weakspot hits.";
_customFormat = _configFile.Bind<string>(text, "Custom Format", IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage), text2);
text2 = "Whether to use the custom format for the mission end screen instead of the preset format.";
_endScreenOverrideFormat = _configFile.Bind<bool>(text, "End Screen Use Custom Format", false, text2);
text2 = "Custom display format to use for the mission end screen. Follows the same rules as Custom Format, but slot cannot be specified.";
_endScreenCustomFormat = _configFile.Bind<string>(text, "End Screen Custom Format", IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit), text2);
CacheValues();
LiveEdit.CreateListener(Paths.ConfigPath, "StatDisplay.cfg", false).FileChanged += new LiveEditEventHandler(OnFileChanged);
}
private void OnFileChanged(LiveEditEventArgs _)
{
_configFile.Reload();
CacheValues();
this.OnChanged?.Invoke();
}
[MemberNotNull(new string[] { "Format", "EndScreenFormat" })]
private void CacheValues()
{
_colors[0] = _hitColor.Value;
_colors[1] = _critColor.Value;
_colors[2] = _pierceColor.Value;
_colors[3] = _damageColor.Value;
_colors[4] = _critDamageColor.Value;
Offset = IConfiguration.ParseOffset(_offset.Value);
Format = (_overrideFormat.Value ? _customFormat.Value : IConfiguration.BuildPresetFormat(_format.Value, _colors));
EndScreenFormat = (_endScreenOverrideFormat.Value ? _endScreenCustomFormat.Value : IConfiguration.BuildPresetFormat(_endScreenFormat.Value, _colors));
}
}
}
namespace StatDisplay.Config.Archive
{
[EnableFeatureByDefault]
public class StatDisplayFeature : Feature, IConfiguration
{
public class StatDisplaySettings
{
[FSDisplayName("Display Format")]
[FSDescription("Preset display settings to use.\n- Hit: Shots Hit/Shots Fired\n- Crit: Shots Crit/Shots Hit\n- Pierce: All Hits/Shots Hit\n- Damage: All Damage Dealt\n- CritDamage: Crit Damage Dealt")]
public List<PresetConfigSetting> Format { get; set; } = new List<PresetConfigSetting>();
[FSDisplayName("Display Full Names")]
[FSDescription("Show full player names instead of shorthand names.")]
public bool FormatFullName { get; set; }
[FSDisplayName("Display Offset")]
[FSDescription("x, y offset of stat data.")]
public string Offset { get; set; } = "0, 0";
[FSDisplayName("End Screen Display Format")]
[FSDescription("Preset display format to use for the mission end screen. Displayed for each weapon.\n- Hit: Shots Hit/Shots Fired\n- Crit: Shots Crit/Shots Hit\n- Pierce: All Hits/Shots Hit\n- Damage: All Damage Dealt\n- CritDamage: Crit Damage Dealt")]
public List<PresetConfigSetting> EndScreenFormat { get; set; } = new List<PresetConfigSetting>();
[FSHeader("Preset Colors", true)]
[FSDisplayName("Hit Color")]
[FSDescription("Color for Hit preset setting.")]
public SColor HitColor { get; set; } = new SColor(0.63f, 0.63f, 0.63f, (float?)null);
[FSDisplayName("Crit Color")]
[FSDescription("Color for Crit preset setting.")]
public SColor CritColor { get; set; } = new SColor(1f, 1f, 0f, (float?)null);
[FSDisplayName("Pierce Color")]
[FSDescription("Color for Pierce preset setting.")]
public SColor PierceColor { get; set; } = new SColor(1f, 0.2f, 0.5f, (float?)null);
[FSDisplayName("Damage Color")]
[FSDescription("Color for Damage preset setting.")]
public SColor DamageColor { get; set; } = new SColor(0.63f, 0.63f, 0.63f, (float?)null);
[FSDisplayName("Crit Damage Color")]
[FSDescription("Color for Crit Damage preset setting.")]
public SColor CritDamageColor { get; set; } = new SColor(1f, 1f, 0f, (float?)null);
[FSHeader("Custom", true)]
[FSDisplayName("Use Custom Format")]
[FSDescription("Whether to use the custom format instead of the preset format.")]
public bool OverrideFormat { get; set; }
[FSDisplayName("Custom Format")]
[FSMaxLength(255)]
[FSDescription("Custom display format to use. Use variables by placing them in \"{}\", e.g. {PrimaryShotHit}.\nPercentages can be done by separating two variables with a '/', e.g. {FullHit/GroupFired}.\nVariables are composed of slot type, shot type, and stat type. They may occur in any order and are case-insensitive.\nAccepted slots (Unspecified = All):\n- All\n- Primary = Main\n- Secondary = Special\n- Melee (Damage Only)\n- Tool = Class (Damage Only)\nAccepted shot types (Unspecified = Shots; Unused for Damage):\n- Shot: Counts for each individual bullet.\n- Group: Counts for each weapon shot (once per shotgun blast).\n- Full: Includes all hits. Counts each damaging effect as a shot fired (including EWC effects).\nAccepted accuracy stat types (Unspecified = Hit):\n- Hit: Number of hits.\n- Crit: Number of crits.\n- Fired: Number of times fired.\nAccepted damage stat types (Unspecified = Any):\n- Any: Damage from any hit.\n- Crit: Damage from weakspot hits.")]
public string CustomFormat { get; set; } = IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit | PresetConfigSetting.Damage);
[FSDisplayName("End Screen Use Custom Format")]
[FSDescription("Whether to use the custom format for the mission end screen instead of the preset format.")]
public bool EndScreenOverrideFormat { get; set; }
[FSDisplayName("End Screen Custom Format")]
[FSMaxLength(255)]
[FSDescription("Custom display format to use for the mission end screen. Use variables by placing them in \"{}\", e.g. {ShotHit}.\nPercentages can be done by separating two variables with a '/', e.g. {FullHit/GroupFired}.\nVariables are composed of shot type and stat type. They may occur in any order and are case-insensitive.\nAccepted shot types (Unspecified = Shots; Unused for Damage):\n- Shot: Counts for each individual bullet.\n- Group: Counts for each weapon shot (once per shotgun blast).\n- Full: Includes all hits. Counts each damaging effect as a shot fired (including EWC effects).\nAccepted accuracy stat types (Unspecified = Hit):\n- Hit: Number of hits.\n- Crit: Number of crits.\n- Fired: Number of times fired.\nAccepted damage stat types (Unspecified = Any):\n- Any: Damage from any hit.\n- Crit: Damage from weakspot hits.")]
public string EndScreenCustomFormat { get; set; } = IConfiguration.BuildPresetFormat(PresetConfigSetting.Hit | PresetConfigSetting.Crit);
[FSHide]
public bool IsFirstTime { get; set; } = true;
}
private readonly string[] _colors = new string[5];
public static StatDisplayFeature Instance { get; private set; }
public override string Name => "Stat Display";
public override string Description => "Displays in-game accuracy and damage.";
public override FeatureGroup Group => FeatureGroups.Hud;
public override bool SkipInitialOnEnable => true;
public string Format { get; private set; } = string.Empty;
public bool FormatFullName => Settings.FormatFullName;
public Vector2 Offset { get; private set; }
public string EndScreenFormat { get; private set; } = string.Empty;
[FeatureConfig]
public static StatDisplaySettings Settings { get; set; }
public event Action? OnChanged;
public override void Init()
{
Instance = this;
if (Settings.IsFirstTime)
{
Settings.Format = new List<PresetConfigSetting>
{
PresetConfigSetting.Hit,
PresetConfigSetting.Crit,
PresetConfigSetting.Damage
};
Settings.EndScreenFormat = new List<PresetConfigSetting>
{
PresetConfigSetting.Hit,
PresetConfigSetting.Crit
};
Settings.IsFirstTime = false;
}
CacheValues();
}
public override void OnFeatureSettingChanged(FeatureSetting setting)
{
CacheValues();
this.OnChanged?.Invoke();
}
private void CacheValues()
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
_colors[0] = SColorExtensions.ToHexString(Settings.HitColor);
_colors[1] = SColorExtensions.ToHexString(Settings.CritColor);
_colors[2] = SColorExtensions.ToHexString(Settings.PierceColor);
_colors[3] = SColorExtensions.ToHexString(Settings.DamageColor);
_colors[4] = SColorExtensions.ToHexString(Settings.CritDamageColor);
Offset = IConfiguration.ParseOffset(Settings.Offset);
Format = (Settings.OverrideFormat ? Settings.CustomFormat : IConfiguration.BuildPresetFormat((PresetConfigSetting)Settings.Format.Sum((PresetConfigSetting x) => (int)x), _colors));
EndScreenFormat = (Settings.EndScreenOverrideFormat ? Settings.EndScreenCustomFormat : IConfiguration.BuildPresetFormat((PresetConfigSetting)Settings.EndScreenFormat.Sum((PresetConfigSetting x) => (int)x), _colors));
}
public override void OnEnable()
{
CacheValues();
this.OnChanged?.Invoke();
StatHandler.SetVisible(visible: true);
}
public override void OnDisable()
{
StatHandler.SetVisible(visible: false);
}
}
internal sealed class StatDisplayModule : IArchiveModule
{
public ILocalizationService LocalizationService { get; set; }
public IArchiveLogger Logger { get; set; }
public void Init()
{
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Load()
{
ArchiveMod.RegisterArchiveModule(typeof(StatDisplayModule));
}
}
}
namespace StatDisplay.Attributes
{
[AttributeUsage(AttributeTargets.Method)]
internal sealed class InvokeOnAssetLoadAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class InvokeOnBuildStartAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class InvokeOnCleanupAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class InvokeOnEnterAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class InvokeOnLoadAttribute : Attribute
{
}
}