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 EpicLoot LuckTrinket v0.3.0
LuckyTrinket.dll
Decompiled 5 days 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 BepInEx.Logging; using EpicLoot; using EpicLoot.CraftingV2; using EpicLoot_UnityLib; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("LuckyTrinket")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.0.0")] [assembly: AssemblyInformationalVersion("0.3.0+ee29449de01293b6e97e8af3eb1ad15a817fc303")] [assembly: AssemblyProduct("LuckyTrinket")] [assembly: AssemblyTitle("LuckyTrinket")] [assembly: AssemblyVersion("0.3.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 LuckyTrinket { internal static class DropDebug { [ThreadStatic] private static Stack<int> s_countStack; [ThreadStatic] private static int s_contextCount; [ThreadStatic] private static Stack<bool> s_countItemsStack; [ThreadStatic] private static bool s_countItems; private static float s_baselineAncientPercent = -1f; private static int s_sessionKeptEquip; private static int s_sessionKeptEquipAncient; private static int s_sessionKeptInjectedEquip; private static bool _ratiosResolved; private static FieldInfo _fItem; private static FieldInfo _fShard; private static FieldInfo _fUnidentified; private static FieldInfo _fMaterials; private static FieldInfo _fGlobal; internal static bool Enabled { get { if (LuckyTrinketPlugin.DebugLog != null) { return LuckyTrinketPlugin.DebugLog.Value; } return false; } } internal static int ContextCount { get { if (s_countStack == null || s_countStack.Count <= 0) { return 0; } return s_contextCount; } } internal static void EnterContext(bool countItems) { if (s_countStack == null) { s_countStack = new Stack<int>(); } s_countStack.Push(s_contextCount); s_contextCount = 0; if (s_countItemsStack == null) { s_countItemsStack = new Stack<bool>(); } s_countItemsStack.Push(s_countItems); s_countItems = countItems; } internal static void ExitContext() { if (s_countItemsStack != null && s_countItemsStack.Count > 0) { s_countItems = s_countItemsStack.Pop(); } if (s_countStack != null && s_countStack.Count > 0) { s_contextCount = s_countStack.Pop(); } } internal static void SetBaselineAncientPercent(float percent) { s_baselineAncientPercent = percent; } internal static void OnItemKept(GameObject gameObject) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 if (!s_countItems) { return; } try { ItemDrop val = (((Object)(object)gameObject != (Object)null) ? gameObject.GetComponent<ItemDrop>() : null); ItemData val2 = (((Object)(object)val != (Object)null) ? val.m_itemData : null); if (val2 != null && val2.IsEquipable()) { MagicItem magicItem = ItemDataExtensions.GetMagicItem(val2); bool num = magicItem != null && (int)magicItem.Rarity >= 5; GameObject dropPrefab = val2.m_dropPrefab; bool flag = RarityFloorSystem.IsInjectedItem((dropPrefab != null) ? ((Object)dropPrefab).name : null); s_sessionKeptEquip++; if (num) { s_sessionKeptEquipAncient++; } if (flag) { s_sessionKeptInjectedEquip++; } } } catch { } } internal static void LogCumulative() { if (Enabled && s_countItems && s_sessionKeptEquip > 0) { float num = (float)s_sessionKeptEquipAncient * 100f / (float)s_sessionKeptEquip; string arg = ((s_baselineAncientPercent >= 0f) ? $"原版池期望≈{s_baselineAncientPercent:0.#}%" : "原版池期望=?"); LtrLog.Debug($"本局累计: 保留装备 {s_sessionKeptEquip} 件,Ancient {s_sessionKeptEquipAncient} 件" + $"({num:0.#}%,{arg}),其中穿戴注入 {s_sessionKeptInjectedEquip} 件"); } } internal static void LogContextStart(string objectName, int level, Vector3? point) { if (Enabled) { LtrLog.Debug($"掉落开始: 表={objectName}, 等级={level}, 点={point}"); LtrLog.Debug(" 分类权重(EpicLoot Balance): " + GetRatios()); } } internal static void LogCountTable(LootTable table, int level, List<KeyValuePair<int, float>> drops) { if (!Enabled || drops == null || drops.Count == 0) { return; } float num = 0f; foreach (KeyValuePair<int, float> drop in drops) { num += Math.Max(0f, drop.Value); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"掉落数量几率: 表={table?.Object}, 等级={level}"); foreach (KeyValuePair<int, float> drop2 in drops) { float num2 = ((num > 0f) ? (Math.Max(0f, drop2.Value) / num * 100f) : 0f); stringBuilder.Append($", {drop2.Key}件={num2:0.#}%"); } LtrLog.Debug(stringBuilder.ToString()); } internal static void OnDropTypeSelected(LootDrop lootDrop, bool isShardDrop, LootDropType type, bool injected = false) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) s_contextCount++; if (Enabled) { LtrLog.Debug(" 掉落分类: " + (lootDrop?.Item ?? "?") + " → " + TypeName(type) + (injected ? "(穿戴注入)" : "") + (isShardDrop ? "(固定碎片石,不参与分类 roll)" : "")); } } internal static void LogRolledCount() { if (Enabled) { LtrLog.Debug($"本次实际 roll 出 {ContextCount} 件掉落,开始过滤。"); } } internal static string Describe(GameObject gameObject) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) try { ItemDrop val = (((Object)(object)gameObject != (Object)null) ? gameObject.GetComponent<ItemDrop>() : null); ItemData val2 = (((Object)(object)val != (Object)null) ? val.m_itemData : null); if (val2 == null) { return "?"; } MagicItem magicItem = ItemDataExtensions.GetMagicItem(val2); string[] obj = new string[5] { $"{val2.m_shared?.m_name} [类型={val2.m_shared?.m_itemType}, ", $"品质={magicItem?.Rarity}, 未鉴定={magicItem?.IsUnidentified}, ", "prefab=", null, null }; GameObject dropPrefab = val2.m_dropPrefab; obj[3] = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); obj[4] = "]"; return string.Concat(obj); } catch (Exception ex) { return "?(" + ex.Message + ")"; } } internal unsafe static string TypeName(LootDropType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected I4, but got Unknown return (int)type switch { 0 => "装备/物品", 1 => "碎片石", 2 => "未鉴定装备", 3 => "材料", _ => ((object)(*(LootDropType*)(&type))/*cast due to .constrained prefix*/).ToString(), }; } private static string GetRatios() { try { ResolveRatios(); return "装备=" + Ratio(_fItem) + ", 碎片石=" + Ratio(_fShard) + ", 未鉴定=" + Ratio(_fUnidentified) + ", 材料=" + Ratio(_fMaterials) + ", 全局掉率=" + Ratio(_fGlobal); } catch (Exception ex) { return "读取失败: " + ex.Message; } } private static void ResolveRatios() { if (!_ratiosResolved) { _ratiosResolved = true; Type type = AccessTools.TypeByName("EpicLoot.Config.ELConfig"); if (!(type == null)) { _fItem = AccessTools.Field(type, "ItemDropRatio"); _fShard = AccessTools.Field(type, "ShardStoneDropRatio"); _fUnidentified = AccessTools.Field(type, "ItemsUnidentifiedDropRatio"); _fMaterials = AccessTools.Field(type, "MaterialsDropRatio"); _fGlobal = AccessTools.Field(type, "GlobalDropRateModifier"); } } } private static string Ratio(FieldInfo field) { if (field == null) { return "?"; } if (!(field.GetValue(null) is ConfigEntry<float> val)) { return "?"; } return val.Value.ToString("0.###"); } } internal static class ExtraSlotsCompat { private static bool _resolved; private static MethodInfo _getEquipmentSlotsItems; internal static bool IsAvailable => Resolve(); private static bool Resolve() { if (_resolved) { return _getEquipmentSlotsItems != null; } _resolved = true; Type type = AccessTools.TypeByName("ExtraSlots.API"); if (type == null) { LtrLog.Debug("未检测到 ExtraSlots.API。"); return false; } _getEquipmentSlotsItems = AccessTools.Method(type, "GetEquipmentSlotsItems", (Type[])null, (Type[])null); if (_getEquipmentSlotsItems == null) { LtrLog.Warn("检测到 ExtraSlots 但 API 方法缺失,跳过额外槽检测。"); } return _getEquipmentSlotsItems != null; } internal static List<ItemData> GetEquippedSlotItems() { if (!Resolve()) { return null; } try { return _getEquipmentSlotsItems.Invoke(null, null) as List<ItemData>; } catch (Exception ex) { LtrLog.Debug("ExtraSlots.GetEquipmentSlotsItems 调用失败: " + ex.Message); return null; } } } internal static class LtrLog { internal static void Info(string message) { ManualLogSource log = LuckyTrinketPlugin.Log; if (log != null) { log.LogInfo((object)message); } } internal static void Warn(string message) { ManualLogSource log = LuckyTrinketPlugin.Log; if (log != null) { log.LogWarning((object)message); } } internal static void Error(string message, Exception exception = null) { if (exception == null) { ManualLogSource log = LuckyTrinketPlugin.Log; if (log != null) { log.LogError((object)message); } } else { ManualLogSource log2 = LuckyTrinketPlugin.Log; if (log2 != null) { log2.LogError((object)$"{message} {exception}"); } } } internal static void Debug(string message) { if (LuckyTrinketPlugin.DebugLog != null && LuckyTrinketPlugin.DebugLog.Value) { ManualLogSource log = LuckyTrinketPlugin.Log; if (log != null) { log.LogInfo((object)("[debug] " + message)); } } } } internal static class LuckyTrinketItems { private const string BasePrefab = "TrinketBronzeHealth"; private static bool _registered; internal static void Register() { PrefabManager.OnVanillaPrefabsAvailable += RegisterItem; LtrLog.Info("已订阅 PrefabManager.OnVanillaPrefabsAvailable(克隆基础物品 TrinketBronzeHealth)"); } private static void RegisterItem() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown if (_registered) { return; } try { RegisterTrinket("LuckyTrinket", new Color(1f, 0.82f, 0.25f, 1f), 200, "$item_luckytrinket", "$item_luckytrinket_desc", (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("Wood", 10, 0, true), new RequirementConfig("BoneFragments", 10, 0, true), new RequirementConfig("Coins", 50, 0, true) }); RegisterTrinket("GreatLuckTrinket", new Color(0.85f, 0.2f, 0.85f, 1f), 500, "$item_greatlucktrinket", "$item_greatlucktrinket_desc", (RequirementConfig[])(object)new RequirementConfig[2] { new RequirementConfig("LuckyTrinket", 1, 0, true), new RequirementConfig("Iron", 10, 0, true) }); _registered = true; } catch (Exception exception) { LtrLog.Error("注册护符失败: ", exception); } } private static void RegisterTrinket(string prefabName, Color tint, int value, string nameToken, string descToken, RequirementConfig[] requirements) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) ItemConfig val = new ItemConfig { Name = prefabName, Description = prefabName, CraftingStation = "piece_workbench", MinStationLevel = 1, Amount = 1, Requirements = requirements, Weight = 1.5f }; CustomItem val2 = new CustomItem(prefabName, "TrinketBronzeHealth", val); if ((Object)(object)val2.ItemPrefab == (Object)null) { LtrLog.Error("[" + prefabName + "] CustomItem 克隆 TrinketBronzeHealth 失败。"); return; } Sanitize(val2, prefabName, tint, value, nameToken, descToken); ItemManager.Instance.AddItem(val2); CustomRecipe recipe = val2.Recipe; string[] obj = new string[6] { "[", prefabName, "] 物品与配方注册完成(工作台=", null, null, null }; object obj2; if (recipe == null) { obj2 = null; } else { Recipe recipe2 = recipe.Recipe; if (recipe2 == null) { obj2 = null; } else { CraftingStation craftingStation = recipe2.m_craftingStation; obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null); } } if (obj2 == null) { obj2 = "null"; } obj[3] = (string)obj2; obj[4] = ", "; obj[5] = $"材料={requirements.Length} 项)。"; LtrLog.Info(string.Concat(obj)); ExportIconForModPackaging(val2.ItemDrop.m_itemData.m_shared, prefabName); } private static void ExportIconForModPackaging(SharedData shared, string prefabName) { try { Sprite val = ((shared?.m_icons != null && shared.m_icons.Length != 0) ? shared.m_icons[0] : null); Texture2D val2 = (((Object)(object)val != (Object)null) ? val.texture : null); if ((Object)(object)val2 == (Object)null) { LtrLog.Warn("护符图标导出跳过:纹理为空。"); return; } byte[] array = ImageConversion.EncodeToPNG(val2); if (array == null || array.Length == 0) { LtrLog.Warn("护符图标导出失败:PNG 数据为空。"); return; } string text = Path.Combine(Paths.PluginPath, "LuckyTrinket"); Directory.CreateDirectory(text); string path = ((prefabName == "LuckyTrinket") ? "icon-export.png" : ("icon-export-" + prefabName + ".png")); string text2 = Path.Combine(text, path); File.WriteAllBytes(text2, array); LtrLog.Info($"护符图标已导出: {text2}({((Texture)val2).width}x{((Texture)val2).height}, {array.Length} bytes)"); } catch (Exception ex) { LtrLog.Warn("护符图标导出失败: " + ex.Message); } } private static void Sanitize(CustomItem item, string prefabName, Color tint, int value, string nameToken, string descToken) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) ItemDrop itemDrop = item.ItemDrop; if ((Object)(object)itemDrop == (Object)null) { LtrLog.Error("[" + prefabName + "] 没有 ItemDrop 组件。"); return; } SharedData shared = itemDrop.m_itemData.m_shared; shared.m_itemType = (ItemType)18; shared.m_maxStackSize = 1; shared.m_maxQuality = 1; shared.m_weight = 1.5f; shared.m_value = value; shared.m_teleportable = true; shared.m_questItem = false; shared.m_name = nameToken; shared.m_description = descToken; StripInheritedEffects(shared, "注册"); RecolorIcons(shared, tint); RecolorRenderers(item.ItemPrefab, tint); string text = $"[{prefabName}] 属性: type={shared.m_itemType}, "; object arg = shared.m_weight; Sprite[] icons = shared.m_icons; LtrLog.Info(text + $"weight={arg}, icons={((icons != null) ? icons.Length : 0)}, name={shared.m_name}"); } internal static void StripInheritedEffects(SharedData shared, string context) { if (shared != null) { LtrLog.Info("[" + context + "] 清理前: equipSE=" + NameOf((Object)(object)shared.m_equipStatusEffect) + ", setSE=" + NameOf((Object)(object)shared.m_setStatusEffect) + ", attackSE=" + NameOf((Object)(object)shared.m_attackStatusEffect) + ", consumeSE=" + NameOf((Object)(object)shared.m_consumeStatusEffect) + ", perfectBlockSE=" + NameOf((Object)(object)shared.m_perfectBlockStatusEffect) + ", fullAdrenalineSE=" + NameOf((Object)(object)shared.m_fullAdrenalineSE) + ", appendToolTip=" + NameOf((Object)(object)shared.m_appendToolTip) + ", " + $"maxAdrenaline={shared.m_maxAdrenaline}, " + $"blockAdrenaline={shared.m_blockAdrenaline}, " + $"perfectBlockAdrenaline={shared.m_perfectBlockAdrenaline}"); shared.m_equipStatusEffect = null; shared.m_setStatusEffect = null; shared.m_attackStatusEffect = null; shared.m_consumeStatusEffect = null; shared.m_perfectBlockStatusEffect = null; shared.m_fullAdrenalineSE = null; shared.m_appendToolTip = null; shared.m_setName = ""; shared.m_setSize = 0; shared.m_maxAdrenaline = 0f; shared.m_blockAdrenaline = 0f; shared.m_perfectBlockAdrenaline = 0f; shared.m_eitrRegenModifier = 0f; shared.m_movementModifier = 0f; shared.m_homeItemsStaminaModifier = 0f; shared.m_heatResistanceModifier = 0f; shared.m_jumpStaminaModifier = 0f; shared.m_attackStaminaModifier = 0f; shared.m_blockStaminaModifier = 0f; shared.m_dodgeStaminaModifier = 0f; shared.m_swimStaminaModifier = 0f; shared.m_sneakStaminaModifier = 0f; shared.m_runStaminaModifier = 0f; shared.m_iceSkates = false; shared.m_iceShoes = false; shared.m_food = 0f; shared.m_foodStamina = 0f; shared.m_foodEitr = 0f; shared.m_foodBurnTime = 0f; shared.m_foodRegen = 0f; shared.m_isDrink = false; } } private static string NameOf(Object obj) { if (!(obj == (Object)null)) { return obj.name; } return "null"; } private static void RecolorIcons(SharedData shared, Color tint) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (shared.m_icons == null || shared.m_icons.Length == 0) { LtrLog.Warn("[LuckyTrinket] m_icons 为空,跳过图标染色。"); return; } for (int i = 0; i < shared.m_icons.Length; i++) { shared.m_icons[i] = RecolorSprite(shared.m_icons[i], tint); } } private static Sprite RecolorSprite(Sprite src, Color tint) { //IL_001e: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)src == (Object)null) { return null; } Texture2D texture = src.texture; if ((Object)(object)texture == (Object)null) { return src; } Rect textureRect = src.textureRect; int num = Mathf.Max(1, Mathf.RoundToInt(((Rect)(ref textureRect)).width)); int num2 = Mathf.Max(1, Mathf.RoundToInt(((Rect)(ref textureRect)).height)); int num3 = Mathf.Max(1, ((Texture)texture).width); int num4 = Mathf.Max(1, ((Texture)texture).height); RenderTexture temporary = RenderTexture.GetTemporary(num3, num4, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2); RenderTexture active = RenderTexture.active; try { Graphics.Blit((Texture)(object)texture, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false); val.ReadPixels(new Rect(((Rect)(ref textureRect)).x, ((Rect)(ref textureRect)).y, (float)num, (float)num2), 0, 0); val.Apply(); Color32[] pixels = val.GetPixels32(); for (int i = 0; i < pixels.Length; i++) { Color32 val2 = pixels[i]; if (val2.a != 0) { float num5 = (float)Mathf.Max((int)val2.r, Mathf.Max((int)val2.g, (int)val2.b)) / 255f; num5 = Mathf.Clamp01(0.2f + 0.8f * num5); pixels[i] = new Color32((byte)(tint.r * 255f * num5), (byte)(tint.g * 255f * num5), (byte)(tint.b * 255f * num5), val2.a); } } val.SetPixels32(pixels); val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num2), new Vector2(0.5f, 0.5f), src.pixelsPerUnit, 0u, (SpriteMeshType)0); } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private static void RecolorRenderers(GameObject root, Color tint) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) int num = 0; Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] materials = componentsInChildren[i].materials; foreach (Material val in materials) { if (!((Object)(object)val == (Object)null)) { if (val.HasProperty("_Color")) { val.SetColor("_Color", tint); num++; } if (val.HasProperty("_EmissionColor")) { val.SetColor("_EmissionColor", tint * 0.35f); } } } } LtrLog.Info(string.Format("[{0}] 模型材质染色: {1} 处。", "LuckyTrinket", num)); } } internal static class LuckyTrinketLocalization { internal const string ItemName = "$item_luckytrinket"; internal const string ItemDesc = "$item_luckytrinket_desc"; internal const string GreatItemName = "$item_greatlucktrinket"; internal const string GreatItemDesc = "$item_greatlucktrinket_desc"; internal const string WornPoolLine = "$item_greatlucktrinket_wornpool"; private static readonly (string, string, string, string)[] Translations = new(string, string, string, string)[5] { ("$item_luckytrinket", "Lucky Trinket", "幸运护符", "幸運護符"), ("$item_luckytrinket_desc", "A charm touched by fortune. While equipped: magic gear dropped near you never rolls below this charm's own rarity - it does not upgrade drop quality, it removes low-quality drops instead (equipment below the floor is discarded; materials one step below are kept). Starts unenchanted - enchant it at the enchanting table to choose its rarity, and re-enchant it later to upgrade.", "一枚被幸运眷顾的护符。佩戴时,附近掉落的魔法装备品质不会低于这枚护符——不会提升掉落物品品质,而是取消低品质物品掉落(低于门槛的装备不再掉落;材料允许低一级保留)。护符初始未附魔——在附魔台附魔选择品质,之后可重新附魔升级。", "一枚被幸運眷顧的護符。佩戴時,附近掉落的魔法裝備品質不會低於這枚護符——不會提升掉落物品品質,而是取消低品質物品掉落(低於門檻的裝備不再掉落;材料允許低一級保留)。護符初始未附魔——在附魔台附魔選擇品質,之後可重新附魔升級。"), ("$item_greatlucktrinket", "Great Fortune Trinket", "强运护符", "強運護符"), ("$item_greatlucktrinket_desc", "A charm overflowing with fortune. While equipped: magic gear dropped near you never rolls below this charm's own rarity (it does not upgrade drop quality, it removes low-quality drops instead), and your worn equipment is added to nearby drop pools, so you can find your own gear as loot (other loot is unaffected; injected gear rolls the pool's original rarity odds). Starts unenchanted - enchant it at the enchanting table to choose its rarity, and re-enchant it later to upgrade.", "一枚被强运笼罩的护符。佩戴时,附近掉落的魔法装备品质不会低于这枚护符(不会提升掉落物品品质,而是取消低品质物品掉落),并把你身上可附魔的穿戴装备加入附近掉落池,可以掉到自己穿的装备(其他掉落不受影响;注入装备按原池品质几率 roll)。护符初始未附魔——在附魔台附魔选择品质,之后可重新附魔升级。", "一枚被強運籠罩的護符。佩戴時,附近掉落的魔法裝備品質不會低於這枚護符(不會提升掉落物品品質,而是取消低品質物品掉落),並把你身上可附魔的穿戴裝備加入附近掉落池,可以掉到自己穿的裝備(其他掉落不受影響;注入裝備按原池品質機率 roll)。護符初始未附魔——在附魔台附魔選擇品質,之後可重新附魔升級。"), ("$item_greatlucktrinket_wornpool", "Innate: your worn equipment is added to nearby drop pools", "固有:你身上穿戴的装备会加入附近掉落池", "固有:你身上穿戴的裝備會加入附近掉落池") }; internal static void Register() { try { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); (string, string, string, string)[] translations = Translations; for (int i = 0; i < translations.Length; i++) { (string, string, string, string) tuple = translations[i]; string text = "English"; localization.AddTranslation(ref text, ref tuple.Item1, tuple.Item2); text = "Chinese"; localization.AddTranslation(ref text, ref tuple.Item1, tuple.Item3); text = "Chinese_Trad"; localization.AddTranslation(ref text, ref tuple.Item1, tuple.Item4); } LtrLog.Info($"本地化已注册:{Translations.Length} 个条目 × 3 种语言。"); } catch (Exception exception) { LtrLog.Error("本地化注册失败: ", exception); } } internal static string L(string token) { Localization instance = Localization.instance; if (instance == null) { return token; } return instance.Localize(token); } } internal static class Patches { internal static void Install(Harmony harmony) { HarmonyMethod postfix = HarmonyMethodFor("EquipOrUnequip_Postfix"); TryPatch(harmony, AccessTools.Method(typeof(Humanoid), "EquipItem", new Type[2] { typeof(ItemData), typeof(bool) }, (Type[])null), null, postfix, null, "Humanoid.EquipItem"); TryPatch(harmony, AccessTools.Method(typeof(Humanoid), "UnequipItem", new Type[2] { typeof(ItemData), typeof(bool) }, (Type[])null), null, postfix, null, "Humanoid.UnequipItem"); InstallLootContextPatches(harmony); InstallDropDebugPatches(harmony); TryPatch(harmony, AccessTools.Method(typeof(EnchantingUIController), "GetEnchantableItems", (Type[])null, (Type[])null), null, HarmonyMethodFor("GetEnchantableItems_Postfix"), null, "EnchantingUIController.GetEnchantableItems"); TryPatch(harmony, AccessTools.Method(typeof(LootRoller), "RollMagicItem", new Type[4] { typeof(ItemRarity), typeof(ItemData), typeof(float), typeof(float) }, (Type[])null), null, HarmonyMethodFor("RollMagicItem_Postfix"), null, "LootRoller.RollMagicItem(ItemRarity, ItemData, float, float)"); TryPatch(harmony, AccessTools.Method(typeof(ItemData), "GetTooltip", new Type[6] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) }, (Type[])null), null, HarmonyMethodFor("GetTooltip_Postfix"), null, "ItemDrop.ItemData.GetTooltip"); } private static void InstallLootContextPatches(Harmony harmony) { HarmonyMethod prefix = HarmonyMethodFor("RollLootTableInternal_Prefix"); HarmonyMethod finalizer = HarmonyMethodFor("RollLootTableInternal_Finalizer"); int num = 0; foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(typeof(LootRoller)) where m.Name == "RollLootTableInternal" select m) { if (TryPatch(harmony, item, prefix, null, finalizer, "LootRoller.RollLootTableInternal(" + Describe(item.GetParameters()) + ")")) { num++; } } if (num == 0) { LtrLog.Error("未注入任何 RollLootTableInternal,掉落品质下限不生效(EpicLoot 版本可能不兼容)。"); } TryPatch(harmony, AccessTools.Method(typeof(LootRoller), "GetLootForLevel", (Type[])null, (Type[])null), null, HarmonyMethodFor("GetLootForLevel_Postfix"), null, "LootRoller.GetLootForLevel"); } private static void InstallDropDebugPatches(Harmony harmony) { TryPatch(harmony, AccessTools.Method(typeof(LootRoller), "GetDropsForLevel", (Type[])null, (Type[])null), null, HarmonyMethodFor("GetDropsForLevel_Postfix"), null, "LootRoller.GetDropsForLevel"); TryPatch(harmony, AccessTools.Method(typeof(LootRoller), "SelectDropType", (Type[])null, (Type[])null), null, HarmonyMethodFor("SelectDropType_Postfix"), null, "LootRoller.SelectDropType"); } private static void EquipOrUnequip_Postfix(Humanoid __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { RarityFloorSystem.Refresh(val); } } private static void GetEnchantableItems_Postfix(ref List<InventoryItemListElement> __result) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown try { if (__result == null) { return; } InventoryManagement instance = InventoryManagement.Instance; List<ItemData> list = ((instance != null) ? instance.GetAllItems() : null); if (list == null) { return; } foreach (ItemData item in list) { if (RarityFloorSystem.IsAnyTrinket(item) && !__result.Any((InventoryItemListElement element) => element.Item == item)) { __result.Add(new InventoryItemListElement { Item = item }); } } } catch (Exception ex) { LtrLog.Debug("补充护符到附魔列表异常: " + ex.Message); } } private static void RollMagicItem_Postfix(ItemData baseItem, ref MagicItem __result) { if (__result != null && RarityFloorSystem.IsAnyTrinket(baseItem)) { RarityFloorSystem.StripTrinketFlavor(__result); } } private static void GetTooltip_Postfix(ref string __result, ItemData item, bool appending) { if (!appending && item != null && !string.IsNullOrEmpty(__result) && RarityFloorSystem.IsAnyTrinket(item) && RarityFloorSystem.IsGreatTrinket(item)) { string text = LuckyTrinketLocalization.L("$item_greatlucktrinket_wornpool"); __result = __result + "\n<color=#C77DFF>" + text + "</color>"; } } private static void RollLootTableInternal_Prefix(object[] __args) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Vector3? val = null; string objectName = null; int level = 0; bool flag = false; if (__args != null) { flag = __args.Length != 0 && __args[0] is LootTable; foreach (object obj in __args) { if (obj is Vector3 value) { val = value; } else if (obj is string text) { objectName = text; } else if (obj is int num) { level = num; } } } DropDebug.EnterContext(flag); RarityFloorSystem.EnterDropContext(val); if (flag) { DropDebug.LogContextStart(objectName, level, val); } } private static void RollLootTableInternal_Finalizer(ref List<GameObject> __result) { RarityFloorSystem.FilterDroppedItems(ref __result); RarityFloorSystem.ExitDropContext(); DropDebug.ExitContext(); } private static void GetLootForLevel_Postfix(ref LootDrop[] __result) { RarityFloorSystem.InjectWornItems(ref __result); } private static void GetDropsForLevel_Postfix(LootTable lootTable, int level, ref List<KeyValuePair<int, float>> __result) { DropDebug.LogCountTable(lootTable, level, __result); } private static void SelectDropType_Postfix(LootDrop lootDrop, bool isShardDrop, ref LootDropType __result) { DropDebug.OnDropTypeSelected(lootDrop, isShardDrop, __result, RarityFloorSystem.IsInjectedItem(lootDrop?.Item)); } private static bool TryPatch(Harmony harmony, MethodBase target, HarmonyMethod prefix, HarmonyMethod postfix, HarmonyMethod finalizer, string label) { if (target == null) { LtrLog.Warn("未找到目标方法,跳过: " + label); return false; } try { harmony.Patch(target, prefix, postfix, (HarmonyMethod)null, finalizer, (HarmonyMethod)null); LtrLog.Info("已注入: " + label); return true; } catch (Exception exception) { LtrLog.Error("注入失败 " + label + ": ", exception); return false; } } private static HarmonyMethod HarmonyMethodFor(string name) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(Patches), name, (Type[])null, (Type[])null); if (methodInfo == null) { throw new MissingMethodException("Patches." + name + " 不存在"); } return new HarmonyMethod(methodInfo); } private static string Describe(ParameterInfo[] parameters) { return string.Join(", ", parameters.Select((ParameterInfo p) => p.ParameterType.Name)); } } [BepInPlugin("trigger.epicloot.luckytrinket", "EpicLoot Luck Trinket", "0.3.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class LuckyTrinketPlugin : BaseUnityPlugin { public const string Guid = "trigger.epicloot.luckytrinket"; public const string PluginName = "EpicLoot Luck Trinket"; public const string PluginVersion = "0.3.0"; internal const string ItemPrefab = "LuckyTrinket"; internal const string GreatItemPrefab = "GreatLuckTrinket"; internal static LuckyTrinketPlugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> Range; internal static ConfigEntry<int> MaterialTolerance; internal static ConfigEntry<bool> DebugLog; internal static ConfigEntry<bool> WornPool; internal static ConfigEntry<float> WornPoolShare; internal static ConfigEntry<float> WornPoolWeight; internal static ConfigEntry<float> WornPoolHeldWeaponWeight; internal static ConfigEntry<float> WornPoolUtilityWeight; private Harmony _harmony; private void Awake() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "启用幸运护符的掉落品质下限效果。"); Range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 100f, new ConfigDescription("生效半径(米):掉落点附近该范围内的装备者护符参与门槛计算,取最高品质。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>())); MaterialTolerance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaterialTolerance", 1, new ConfigDescription("带品质材料(碎片石/魔法材料/符文)允许低于门槛多少级保留:0 = 只保留达到门槛的材料,1 = 低一级的材料也保留,5 = 所有材料都保留。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>())); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLog", false, "输出详细调试日志(排查用,默认关闭)。"); WornPool = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "WornPool", true, "强运护符的穿戴装备注入:把你身上穿戴的装备追加到附近掉落池,原掉落池条目不受影响;没有佩戴强运护符时无效果。"); WornPoolShare = ((BaseUnityPlugin)this).Config.Bind<float>("General", "WornPoolShare", 0.2f, new ConfigDescription("注入装备占掉落池总权重的比例:0.2 = 我们的装备约 20%、原版池约 80%。0 = 关闭注入。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.95f), Array.Empty<object>())); WornPoolWeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "WornPoolWeight", 1f, new ConfigDescription("注入组内每件穿戴装备的相对权重(组内比例;实际占池比例由 WornPoolShare 控制)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); WornPoolHeldWeaponWeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "WornPoolHeldWeaponWeight", 2f, new ConfigDescription("手持武器在注入组内的相对权重(组内比例)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); WornPoolUtilityWeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "WornPoolUtilityWeight", 0.4f, new ConfigDescription("饰品(Utility 类型)在注入组内的相对权重;仅在安装 ExtraSlots 时生效。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); LtrLog.Info("EpicLoot Luck Trinket v0.3.0 初始化中..."); _harmony = new Harmony("trigger.epicloot.luckytrinket"); Patches.Install(_harmony); LuckyTrinketLocalization.Register(); LuckyTrinketItems.Register(); RarityFloorSystem.Register(); LtrLog.Info("EpicLoot Luck Trinket v0.3.0 初始化完成。" + $"Enabled={Enabled.Value}, Range={Range.Value}, " + $"WornPool={WornPool.Value}, WornPoolWeight={WornPoolWeight.Value}"); } private void Update() { RarityFloorSystem.Update(); } private void OnDestroy() { RarityFloorSystem.Unregister(); if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal static class RarityFloorSystem { internal const string ZdoKey = "ltr-floor"; internal const string WornZdoKey = "ltr-worn"; private const float AutoRefreshInterval = 5f; [ThreadStatic] private static Stack<int> s_floorStack; [ThreadStatic] private static int s_contextFloor; [ThreadStatic] private static Stack<Dictionary<string, float>> s_wornStack; [ThreadStatic] private static Dictionary<string, float> s_contextWorn; [ThreadStatic] private static Stack<HashSet<string>> s_injectedStack; [ThreadStatic] private static HashSet<string> s_contextInjected; [ThreadStatic] private static List<Player> s_playerBuffer; private static float s_nextAutoRefresh; private static bool s_prefabSanitized; internal static int ContextFloor { get { if (s_floorStack == null || s_floorStack.Count <= 0) { return -1; } return s_contextFloor; } } private static Dictionary<string, float> ContextWorn { get { if (s_wornStack == null || s_wornStack.Count <= 0) { return null; } return s_contextWorn; } } internal static bool IsInjectedItem(string itemName) { if (!string.IsNullOrEmpty(itemName) && s_contextInjected != null) { return s_contextInjected.Contains(itemName); } return false; } internal static void Register() { API.AddMagicItemChangedListener((Action<ItemData, string>)OnMagicItemChanged); LtrLog.Info("已注册 EpicLoot MagicItemChanged 监听器。"); } internal static void Unregister() { API.RemoveMagicItemChangedListener((Action<ItemData, string>)OnMagicItemChanged); } internal static void Update() { if (!(Time.time < s_nextAutoRefresh)) { s_nextAutoRefresh = Time.time + 5f; TrySanitizePrefabOnce(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { Refresh(localPlayer); } } } private static void TrySanitizePrefabOnce() { if (s_prefabSanitized || (Object)(object)ObjectDB.instance == (Object)null) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("LuckyTrinket"); if (!((Object)(object)itemPrefab == (Object)null)) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); SharedData val = (((Object)(object)component != (Object)null) ? component.m_itemData.m_shared : null); if (val != null) { LuckyTrinketItems.StripInheritedEffects(val, "运行时兜底"); s_prefabSanitized = true; } } } internal static void EnterDropContext(Vector3? dropPoint) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (s_floorStack == null) { s_floorStack = new Stack<int>(); } s_floorStack.Push(s_contextFloor); if (s_wornStack == null) { s_wornStack = new Stack<Dictionary<string, float>>(); } s_wornStack.Push(s_contextWorn); if (s_injectedStack == null) { s_injectedStack = new Stack<HashSet<string>>(); } s_injectedStack.Push(s_contextInjected); s_contextInjected = null; int num = -1; Dictionary<string, float> dictionary = null; try { if (dropPoint.HasValue) { num = ComputeNearbyFloor(dropPoint.Value); dictionary = ComputeNearbyWorn(dropPoint.Value); } } catch (Exception ex) { LtrLog.Debug("ComputeNearbyFloor/ComputeNearbyWorn 异常: " + ex.Message); num = -1; dictionary = null; } if (num > s_contextFloor) { s_contextFloor = num; } if (dictionary != null && dictionary.Count > 0) { s_contextWorn = dictionary; } LtrLog.Debug($"进入掉落上下文: dropPoint={dropPoint}, floor={num}, " + $"worn={dictionary?.Count ?? 0}, active={s_contextFloor}"); if (dictionary != null && dictionary.Count > 0) { LtrLog.Debug($" 穿戴装备名单({dictionary.Count}): {DescribeWorn(dictionary)}"); } } internal static void ExitDropContext() { if (s_injectedStack != null && s_injectedStack.Count > 0) { s_contextInjected = s_injectedStack.Pop(); } if (s_wornStack != null && s_wornStack.Count > 0) { s_contextWorn = s_wornStack.Pop(); } if (s_floorStack != null && s_floorStack.Count != 0) { s_contextFloor = s_floorStack.Pop(); } } internal static void FilterDroppedItems(ref List<GameObject> items) { if (!LuckyTrinketPlugin.Enabled.Value || items == null || items.Count == 0) { return; } int contextFloor = ContextFloor; if (contextFloor < 0) { LtrLog.Debug("掉落无门槛(附近无装备护符的玩家),跳过过滤" + $"({items.Count} 个物品)。"); return; } DropDebug.LogRolledCount(); int num = 0; for (int num2 = items.Count - 1; num2 >= 0; num2--) { GameObject val = items[num2]; if ((Object)(object)val == (Object)null) { items.RemoveAt(num2); } else if (ShouldExclude(val, contextFloor)) { if (LuckyTrinketPlugin.DebugLog.Value) { LtrLog.Debug($"剔除(门槛<{contextFloor}): {DropDebug.Describe(val)}"); } DestroyLoot(val); items.RemoveAt(num2); num++; } else { DropDebug.OnItemKept(val); if (LuckyTrinketPlugin.DebugLog.Value) { LtrLog.Debug("保留掉落: " + DropDebug.Describe(val)); } } } if (!LuckyTrinketPlugin.DebugLog.Value) { return; } List<string> list = new List<string>(); foreach (GameObject item in items) { list.Add(DropDebug.Describe(item)); } LtrLog.Debug($"过滤结果: roll={DropDebug.ContextCount}, 剔除门槛={num}, " + $"保留={items.Count}"); LtrLog.Debug("最终掉落: " + ((list.Count > 0) ? string.Join("; ", list) : "(空)")); DropDebug.LogCumulative(); } private static bool ShouldExclude(GameObject gameObject, int floor) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 try { ItemData val = gameObject.GetComponent<ItemDrop>()?.m_itemData; if (val == null) { return false; } MagicItem magicItem = ItemDataExtensions.GetMagicItem(val); bool flag = magicItem != null && (magicItem.IsUnidentified || ((Object)(object)val.m_dropPrefab != (Object)null && ((Object)val.m_dropPrefab).name.EndsWith("_Unidentified", StringComparison.Ordinal))); if (val.IsEquipable() || flag) { return magicItem != null && (int)magicItem.Rarity < floor; } int num = -1; if (API.TryGetRarity(val, ref num)) { int num2 = Math.Max(0, floor - LuckyTrinketPlugin.MaterialTolerance.Value); return num < num2; } return false; } catch (Exception ex) { LtrLog.Debug("ShouldExclude 异常: " + ex.Message); return false; } } internal static void InjectWornItems(ref LootDrop[] loot) { //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Expected O, but got Unknown if (!LuckyTrinketPlugin.Enabled.Value || !LuckyTrinketPlugin.WornPool.Value) { return; } Dictionary<string, float> contextWorn = ContextWorn; if (contextWorn == null || contextWorn.Count == 0) { return; } float num = Mathf.Clamp(LuckyTrinketPlugin.WornPoolShare.Value, 0f, 0.95f); if (num <= 0f) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); float num2 = 0f; if (loot != null) { LootDrop[] array = loot; foreach (LootDrop val in array) { if (val != null) { if (!string.IsNullOrEmpty(val.Item)) { hashSet.Add(val.Item); } num2 += Math.Max(0f, val.Weight); } } } float num3 = 0f; List<KeyValuePair<string, float>> list = new List<KeyValuePair<string, float>>(); foreach (KeyValuePair<string, float> item in contextWorn) { if (!string.IsNullOrEmpty(item.Key) && !hashSet.Contains(item.Key)) { float num4 = ((item.Value > 0f) ? item.Value : LuckyTrinketPlugin.WornPoolWeight.Value); list.Add(new KeyValuePair<string, float>(item.Key, num4)); num3 += num4; } } if (list.Count == 0 || num3 <= 0f) { return; } float num5 = ((num2 > 0f) ? (num2 * num / (1f - num)) : num3); float num6 = num5 / num3; float[] array2 = BuildInjectedRarity(loot); List<LootDrop> list2 = new List<LootDrop>(); foreach (KeyValuePair<string, float> item2 in list) { list2.Add(new LootDrop { Item = item2.Key, Weight = Math.Max(0.0001f, item2.Value * num6), Rarity = array2 }); } LootDrop[] obj = loot; int num7 = ((obj != null) ? obj.Length : 0); LootDrop[] array3 = (LootDrop[])(object)new LootDrop[num7 + list2.Count]; if (loot != null) { Array.Copy(loot, array3, num7); } for (int j = 0; j < list2.Count; j++) { array3[num7 + j] = list2[j]; } loot = array3; if (s_contextInjected == null) { s_contextInjected = new HashSet<string>(StringComparer.Ordinal); } foreach (LootDrop item3 in list2) { s_contextInjected.Add(item3.Item); } if (!LuckyTrinketPlugin.DebugLog.Value) { return; } List<string> list3 = new List<string>(); foreach (LootDrop item4 in list2) { list3.Add(item4.Item + "(" + item4.Weight.ToString("0.###", CultureInfo.InvariantCulture) + ")"); } float num8 = ((num2 > 0f) ? (num5 / (num2 + num5)) : 1f); LtrLog.Debug($"注入掉落池: 追加 {list2.Count} 件穿戴装备" + "(原池总权重=" + num2.ToString("0.###", CultureInfo.InvariantCulture) + ", 注入总权重=" + num5.ToString("0.###", CultureInfo.InvariantCulture) + ", " + $"占比={num8 * 100f:0.#}%): " + string.Join(", ", list3)); float num9 = AncientPercent(array2); DropDebug.SetBaselineAncientPercent(num9); LtrLog.Debug($" 品质分布(原池=注入,未钳制)=[{JoinWeights(array2)}] Ancient≈{num9:0.#}%"); } private static float AncientPercent(float[] weights) { if (weights == null || weights.Length == 0) { return 0f; } float num = 0f; float num2 = 0f; for (int i = 0; i < weights.Length; i++) { float num3 = Math.Max(0f, weights[i]); num += num3; if (i >= 5) { num2 += num3; } } if (!(num > 0f)) { return 0f; } return num2 / num * 100f; } private static string JoinWeights(float[] weights) { if (weights == null) { return "无"; } List<string> list = new List<string>(weights.Length); foreach (float num in weights) { list.Add(num.ToString("0.###", CultureInfo.InvariantCulture)); } return string.Join(",", list); } private static string DescribeWorn(Dictionary<string, float> worn) { if (worn == null || worn.Count == 0) { return ""; } List<string> list = new List<string>(); foreach (KeyValuePair<string, float> item in worn) { list.Add(item.Key + "(" + item.Value.ToString("0.##", CultureInfo.InvariantCulture) + ")"); } list.Sort(StringComparer.Ordinal); return string.Join(", ", list); } private static float[] BuildInjectedRarity(LootDrop[] pool) { int count = Rarities.Count; float[] array = new float[count]; float[] array2 = null; if (pool != null) { foreach (LootDrop val in pool) { if (val?.Rarity != null && val.Rarity.Length == count) { array2 = val.Rarity; break; } } } if (array2 != null) { Array.Copy(array2, array, count); } else { array[0] = 1f; } return array; } private static void DestroyLoot(GameObject gameObject) { if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(gameObject); } else { Object.Destroy((Object)(object)gameObject); } } private static int ComputeNearbyFloor(Vector3 point) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!LuckyTrinketPlugin.Enabled.Value) { return -1; } List<Player> list = s_playerBuffer ?? (s_playerBuffer = new List<Player>()); list.Clear(); Player.GetPlayersInRange(point, LuckyTrinketPlugin.Range.Value, list); int num = -1; foreach (Player item in list) { if ((Object)(object)item == (Object)null) { continue; } ZDO zdo = GetZdo((Component)(object)item); if (zdo != null) { int num2 = zdo.GetInt("ltr-floor", -1); if (num2 > num) { num = num2; } } } return num; } private static Dictionary<string, float> ComputeNearbyWorn(Vector3 point) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!LuckyTrinketPlugin.Enabled.Value || !LuckyTrinketPlugin.WornPool.Value) { return null; } List<Player> list = s_playerBuffer ?? (s_playerBuffer = new List<Player>()); list.Clear(); Player.GetPlayersInRange(point, LuckyTrinketPlugin.Range.Value, list); Dictionary<string, float> dictionary = null; foreach (Player item in list) { if ((Object)(object)item == (Object)null) { continue; } ZDO zdo = GetZdo((Component)(object)item); if (zdo == null) { continue; } string text = zdo.GetString("ltr-worn", ""); if (string.IsNullOrEmpty(text)) { continue; } if (dictionary == null) { dictionary = new Dictionary<string, float>(StringComparer.Ordinal); } string[] array = text.Split(new char[1] { ',' }); foreach (string text2 in array) { if (!string.IsNullOrEmpty(text2)) { string text3 = text2; float num = LuckyTrinketPlugin.WornPoolWeight.Value; int num2 = text2.LastIndexOf(':'); if (num2 > 0 && float.TryParse(text2.Substring(num2 + 1), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { text3 = text2.Substring(0, num2); num = result; } if (!string.IsNullOrEmpty(text3) && (!dictionary.TryGetValue(text3, out var value) || num > value)) { dictionary[text3] = num; } } } } return dictionary; } internal static void Refresh(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } try { ZDO zdo = GetZdo((Component)(object)player); if (zdo != null) { int num = ComputePlayerFloor(player); if (zdo.GetInt("ltr-floor", -1) != num) { zdo.Set("ltr-floor", num); LtrLog.Debug($"装备者门槛 ZDO 更新: {num}"); } string text = ComputeWornList(player); if (zdo.GetString("ltr-worn", "") != text) { zdo.Set("ltr-worn", text); LtrLog.Debug("装备者穿戴名单 ZDO 更新: [" + text + "]"); } SanitizeOwnedTrinkets(player); } } catch (Exception exception) { LtrLog.Error("刷新玩家门槛失败: ", exception); } } internal static void StripTrinketFlavor(MagicItem magicItem) { List<MagicItemEffect> effects = magicItem.Effects; if (effects != null && effects.Count > 0) { effects.Clear(); } magicItem.DisplayName = null; } private static void SanitizeOwnedTrinkets(Player player) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } MagicItem val = default(MagicItem); foreach (ItemData allItem in inventory.GetAllItems()) { if (IsAnyTrinket(allItem) && ItemDataExtensions.IsMagic(allItem, ref val) && val != null) { bool num = val.Effects != null && val.Effects.Count > 0; bool flag = !string.IsNullOrEmpty(val.DisplayName); if (num || flag) { StripTrinketFlavor(val); ItemDataExtensions.SaveMagicItem(allItem, val); LtrLog.Debug("已清除护符的随机词条/装饰名(只保留品质)。"); } } } } private static int ComputePlayerFloor(Player player) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected I4, but got Unknown int num = -1; MagicItem val = default(MagicItem); foreach (ItemData item in CollectEquippedItems(player)) { if (IsAnyTrinket(item) && ItemDataExtensions.IsMagic(item, ref val) && val != null) { int num2 = (int)val.Rarity; if (num2 > num) { num = num2; } } } return num; } private static string ComputeWornList(Player player) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Invalid comparison between Unknown and I4 if (!IsGreatTrinketEquipped(player)) { return ""; } float value = LuckyTrinketPlugin.WornPoolWeight.Value; float value2 = LuckyTrinketPlugin.WornPoolHeldWeaponWeight.Value; float value3 = LuckyTrinketPlugin.WornPoolUtilityWeight.Value; bool isAvailable = ExtraSlotsCompat.IsAvailable; ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); List<string> list = new List<string>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (ItemData item in CollectEquippedItems(player)) { if (item == null || IsAnyTrinket(item) || !EpicLoot.CanBeMagicItem(item)) { continue; } GameObject dropPrefab = item.m_dropPrefab; if (!((Object)(object)dropPrefab == (Object)null) && !string.IsNullOrEmpty(((Object)dropPrefab).name) && hashSet.Add(((Object)dropPrefab).name)) { float num = value; if (currentWeapon != null && item == currentWeapon) { num = value2; } else if (isAvailable && item.m_shared != null && (int)item.m_shared.m_itemType == 18) { num = value3; } list.Add(((Object)dropPrefab).name + ":" + num.ToString("0.###", CultureInfo.InvariantCulture)); } } list.Sort(StringComparer.Ordinal); return string.Join(",", list); } internal static bool IsGreatTrinketEquipped(Player player) { return IsTrinketEquipped(player, IsGreatTrinket); } private static bool IsTrinketEquipped(Player player, Func<ItemData, bool> predicate) { if ((Object)(object)player == (Object)null) { return false; } foreach (ItemData item in CollectEquippedItems(player)) { if (predicate(item)) { return true; } } return false; } private static List<ItemData> CollectEquippedItems(Player player) { List<ItemData> list = new List<ItemData>(); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { list.AddRange(inventory.GetEquippedItems()); } List<ItemData> equippedSlotItems = ExtraSlotsCompat.GetEquippedSlotItems(); if (equippedSlotItems != null) { foreach (ItemData item in equippedSlotItems) { if (item != null && !list.Contains(item)) { list.Add(item); } } } return list; } internal static bool IsLuckyTrinket(ItemData item) { return IsTrinketPrefab(item, "LuckyTrinket"); } internal static bool IsGreatTrinket(ItemData item) { return IsTrinketPrefab(item, "GreatLuckTrinket"); } internal static bool IsAnyTrinket(ItemData item) { if (!IsLuckyTrinket(item)) { return IsGreatTrinket(item); } return true; } private static bool IsTrinketPrefab(ItemData item, string target) { GameObject val = item?.m_dropPrefab; if ((Object)(object)val == (Object)null) { return false; } string name = ((Object)val).name; if (!(name == target)) { return name.StartsWith(target + "(Clone)", StringComparison.Ordinal); } return true; } private static ZDO GetZdo(Component component) { if ((Object)(object)component == (Object)null) { return null; } ZNetView component2 = component.GetComponent<ZNetView>(); if ((Object)(object)component2 == (Object)null || !component2.IsValid()) { return null; } return component2.GetZDO(); } private static void OnMagicItemChanged(ItemData item, string reason) { try { if (IsAnyTrinket(item)) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Humanoid)localPlayer).GetInventory().GetEquippedItems().Contains(item)) { LtrLog.Debug("护符魔法数据变化(" + reason + "),刷新门槛。"); Refresh(localPlayer); } } } catch (Exception ex) { LtrLog.Debug("OnMagicItemChanged 异常: " + ex.Message); } } } }