Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TrophyPity v1.0.1
TrophyPity.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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("TrophyPity")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("TrophyPity")] [assembly: AssemblyTitle("TrophyPity")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Gafteds.TrophyPity { [BepInPlugin("com.gafteds.trophypity", "TrophyPity", "0.2.0")] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Character), "OnDeath")] private static class DeathPatch { private static void Prefix(Character __instance) { if ((Object)(object)Instance == (Object)null || !Instance.Enabled.Value || (Object)(object)__instance == (Object)null || __instance.IsPlayer()) { return; } try { HitData val = (HitData)((LastHitField == null) ? null : /*isinst with value type is only supported in some contexts*/); Player val2 = (Player)((val == null) ? null : /*isinst with value type is only supported in some contexts*/); lock (Sync) { if ((Object)(object)val2 != (Object)null) { Killers[((Object)__instance).GetInstanceID()] = val2; } } Log("[Death] creature=" + ((Object)((Component)__instance).gameObject).name + " killer=" + (((Object)(object)val2 == (Object)null) ? "unattributed" : val2.GetPlayerName())); } catch (Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("Attribution error: " + ex.Message)); } } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] private static class LootPatch { private static void Postfix(CharacterDrop __instance, List<KeyValuePair<GameObject, int>> __result) { if ((Object)(object)Instance == (Object)null || !Instance.Enabled.Value || (Object)(object)__instance == (Object)null || __result == null) { return; } try { Character component = ((Component)__instance).GetComponent<Character>(); if ((Object)(object)component == (Object)null || component.IsPlayer() || IsBoss(component)) { return; } ZNetView component2 = ((Component)component).GetComponent<ZNetView>(); if ((Object)(object)component2 == (Object)null || !component2.IsValid() || !component2.IsOwner()) { return; } int instanceID = ((Object)component).GetInstanceID(); lock (Sync) { if (!Processed.Add(instanceID)) { Log("[Skip] repeated loot generation " + ((Object)((Component)component).gameObject).name); return; } if (!Killers.TryGetValue(instanceID, out var value) || (Object)(object)value == (Object)null) { Log("[Skip] no reliable killing-blow player for " + ((Object)((Component)component).gameObject).name); return; } string worldKey = GetWorldKey(); if (string.IsNullOrEmpty(worldKey)) { Log("[Skip] world UID unavailable"); return; } long playerID = value.GetPlayerID(); if (playerID == 0L) { Log("[Skip] player ID unavailable"); return; } EnsureLoaded(worldKey); bool flag = false; foreach (Drop drop in __instance.m_drops) { if (drop == null || (Object)(object)drop.m_prefab == (Object)null || !((Object)drop.m_prefab).name.StartsWith("Trophy", StringComparison.OrdinalIgnoreCase)) { continue; } int num = ((Math.Abs(drop.m_chance - 0.05f) < 0.0001f) ? Instance.FivePercentKills.Value : ((Math.Abs(drop.m_chance - 0.1f) < 0.0001f) ? Instance.TenPercentKills.Value : 0)); if (num >= 1 && !drop.m_levelMultiplier) { string key = playerID.ToString(CultureInfo.InvariantCulture) + "|" + ((Object)((Component)component).gameObject).name.Replace("(Clone)", "") + "|" + ((Object)drop.m_prefab).name; Counters.TryGetValue(key, out var value2); if (__result.Any((KeyValuePair<GameObject, int> x) => (Object)(object)x.Key == (Object)(object)drop.m_prefab && x.Value > 0)) { Counters.Remove(key); flag = true; Log("[Natural] " + value.GetPlayerName() + " " + ((Object)drop.m_prefab).name + " reset from " + value2); } else if (value2 + 1 >= num) { __result.Add(new KeyValuePair<GameObject, int>(drop.m_prefab, 1)); Counters.Remove(key); flag = true; Log("[Guaranteed] " + value.GetPlayerName() + " " + ((Object)drop.m_prefab).name + " at kill " + num); } else { Counters[key] = value2 + 1; flag = true; Log("[Progress] " + value.GetPlayerName() + " " + ((Object)drop.m_prefab).name + " " + (value2 + 1) + "/" + num); } } } if (flag) { Save(worldKey); } Killers.Remove(instanceID); } } catch (Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogError((object)("Pity loot error: " + ex)); } } } internal static Plugin Instance; internal ConfigEntry<bool> Enabled; internal ConfigEntry<bool> Diagnostics; internal ConfigEntry<int> TenPercentKills; internal ConfigEntry<int> FivePercentKills; private Harmony harmony; private static readonly Dictionary<int, Player> Killers = new Dictionary<int, Player>(); private static readonly HashSet<int> Processed = new HashSet<int>(); private static readonly FieldInfo LastHitField = AccessTools.Field(typeof(Character), "m_lastHit"); private static readonly FieldInfo BossField = AccessTools.Field(typeof(Character), "m_boss"); private static readonly MethodInfo WorldUidMethod = AccessTools.Method(typeof(ZNet), "GetWorldUID", (Type[])null, (Type[])null); private static readonly Dictionary<string, int> Counters = new Dictionary<string, int>(); private static string loadedWorld; private static readonly object Sync = new object(); private void Awake() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("TrophyPity", "Enabled", true, "Enable pity rewards (local testing build; dedicated-server-only operation unverified)."); Diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("TrophyPity", "Diagnostics", true, "Log trophy rolls, counters, and guarantees."); TenPercentKills = ((BaseUnityPlugin)this).Config.Bind<int>("TrophyPity", "TenPercentKills", 12, "Kill threshold for exactly 10% trophies."); FivePercentKills = ((BaseUnityPlugin)this).Config.Bind<int>("TrophyPity", "FivePercentKills", 20, "Kill threshold for exactly 5% trophies."); harmony = new Harmony("com.gafteds.trophypity"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"TrophyPity v0.2.0 LOCAL TEST BUILD: pity enabled; server-only compatibility NOT verified."); if (LastHitField == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Cannot read Character.m_lastHit: unattributed kills will be skipped."); } if (WorldUidMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Cannot read world UID: pity progress will be skipped to prevent cross-world mixing."); } } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); } lock (Sync) { Killers.Clear(); Processed.Clear(); Counters.Clear(); loadedWorld = null; } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private static bool IsBoss(Character creature) { if (BossField != null) { object value = BossField.GetValue(creature); if (value is bool) { return (bool)value; } } string name = ((Object)((Component)creature).gameObject).name; return new string[7] { "Eikthyr", "gd_king", "Bonemass", "Dragon", "GoblinKing", "SeekerQueen", "Fader" }.Any((string s) => name.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0); } private static string GetWorldKey() { if ((Object)(object)ZNet.instance == (Object)null || WorldUidMethod == null) { return null; } try { object obj = WorldUidMethod.Invoke(ZNet.instance, null); return (obj == null) ? null : Convert.ToString(obj, CultureInfo.InvariantCulture); } catch { return null; } } private static string FileForWorld(string world) { string text = new string(world.Where((char c) => char.IsLetterOrDigit(c) || c == '_' || c == '-').ToArray()); if (string.IsNullOrEmpty(text)) { return null; } return Path.Combine(Paths.ConfigPath, "Gafteds.TrophyPity." + text + ".progress.tsv"); } private static void EnsureLoaded(string world) { if (loadedWorld == world) { return; } Counters.Clear(); loadedWorld = world; string text = FileForWorld(world); if (text == null || !File.Exists(text)) { return; } try { string[] array = File.ReadAllLines(text); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('\t'); if (array2.Length == 2 && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result > 0) { string key = Encoding.UTF8.GetString(Convert.FromBase64String(array2[0])); Counters[key] = result; } } } catch (Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("Unable to load pity progress: " + ex.Message)); } } private static void Save(string world) { string text = FileForWorld(world); if (text == null) { return; } string text2 = text + ".tmp"; try { File.WriteAllLines(text2, Counters.Select((KeyValuePair<string, int> kv) => Convert.ToBase64String(Encoding.UTF8.GetBytes(kv.Key)) + "\t" + kv.Value.ToString(CultureInfo.InvariantCulture)).ToArray()); if (File.Exists(text)) { File.Delete(text); } File.Move(text2, text); } catch (Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogError((object)("Could not save pity progress: " + ex.Message)); } } private static void Log(string message) { if ((Object)(object)Instance != (Object)null && Instance.Diagnostics.Value) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)message); } } } }