using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using GridEditor; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FTKRandomItemsStats")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("FTKRandomItemsStats")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8c0b2ff8-0930-423c-8133-4d99bd2527bb")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace FTKRandomItemsStats; [HarmonyPatch] public class ChatCommands { private static Dictionary statColors = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "toughness", "#8B4513" }, { "vitality", "#32CD32" }, { "awareness", "#4B0082" }, { "quickness", "#FFA500" }, { "talent", "#C2B280" }, { "evade", "#006400" }, { "focus", "#FFFF00" }, { "armor", "#87CEEB" }, { "resistance", "#FF69B4" }, { "strength", "#8B0000" }, { "intelligence", "#ADD8E6" }, { "speed", "#008080" }, { "critChance", "#FF0000" } }; [HarmonyPatch(typeof(GameFlow), "ChatMessage")] [HarmonyPrefix] private static bool ChatMessagePrefix(string _s) { if (!_s.StartsWith("/")) { return true; } string text = _s.ToLower().Trim(); if (text == "/estats" || text == "/stats") { ShowPlayerStatsDetailed(); return false; } return true; } private static void ShowPlayerStatsDetailed() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) CharacterOverworld currentCOW = GameLogic.Instance.GetCurrentCOW(); if ((Object)(object)currentCOW == (Object)null) { return; } List equippedItems = PlayerStatsManager.GetEquippedItems(currentCOW.m_FTKPlayerID); _ = currentCOW.m_CharacterStats; if (equippedItems.Count == 0) { GameFlow.Instance.ChatMessage("=== Tus stats extra: Ninguno ==="); GameFlow.Instance.ChatMessage("=== Your extra stats: None ==="); return; } GameFlow.Instance.ChatMessage("=== Tus stats extra ==="); foreach (PlayerStatsManager.EquippedItemStat item in equippedItems) { if (item.stats.Count == 0) { continue; } string text = item.itemName + ": "; for (int i = 0; i < item.stats.Count; i++) { PlayerStatsManager.PlayerStat playerStat = item.stats[i]; string text2 = (statColors.ContainsKey(playerStat.statName) ? statColors[playerStat.statName] : "#FFFFFF"); string text3 = ((playerStat.value >= 0) ? "+" : ""); text += $"{text3}{playerStat.value} {playerStat.statName}"; if (i < item.stats.Count - 1) { text += ", "; } } GameFlow.Instance.ChatMessage(text); } GameFlow.Instance.ChatMessage("=== Your extra stats ==="); foreach (PlayerStatsManager.EquippedItemStat item2 in equippedItems) { if (item2.stats.Count == 0) { continue; } string text4 = item2.itemName + ": "; for (int j = 0; j < item2.stats.Count; j++) { PlayerStatsManager.PlayerStat playerStat2 = item2.stats[j]; string text5 = (statColors.ContainsKey(playerStat2.statName) ? statColors[playerStat2.statName] : "#FFFFFF"); string text6 = ((playerStat2.value >= 0) ? "+" : ""); text4 += $"{text6}{playerStat2.value} {playerStat2.statName}"; if (j < item2.stats.Count - 1) { text4 += ", "; } } GameFlow.Instance.ChatMessage(text4); } } } public static class ExtraStatsData { public class ExtraStat { public string statName; public int value; } public static Dictionary> ItemMods = new Dictionary>(); } public static class PlayerStatsManager { public class EquippedItemStat { public string itemName; public List stats; } public class PlayerStat { public string statName; public int value; } private static Dictionary> playerStats = new Dictionary>(); private static Dictionary> equippedItems = new Dictionary>(); public static void AddPlayerStat(FTKPlayerID playerID, PlayerStat stat) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!playerStats.ContainsKey(playerID)) { playerStats[playerID] = new List(); } playerStats[playerID].Add(stat); Debug.Log((object)string.Format("\ud83d\udcca [PlayerStats] {0}{1} {2} added to {3}. Total: {4}", (stat.value >= 0) ? "+" : "", stat.value, stat.statName, playerID, playerStats[playerID].Count)); } public static List GetPlayerStats(FTKPlayerID playerID) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (playerStats.ContainsKey(playerID)) { return playerStats[playerID]; } return new List(); } public static void RemovePlayerStatsForItem(FTKPlayerID playerID, List statsToRemove) { //IL_0005: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!playerStats.ContainsKey(playerID)) { return; } List list = playerStats[playerID]; foreach (PlayerStat item in statsToRemove) { for (int num = list.Count - 1; num >= 0; num--) { if (list[num].statName == item.statName) { Debug.Log((object)$"\ud83d\udcca [PlayerStats] -{item.value} {item.statName} removed from {playerID}"); list.RemoveAt(num); break; } } } Debug.Log((object)$"\ud83d\udcca [PlayerStats] Remaining stats for {playerID}: {list.Count}"); } public static void DebugPrintStats(FTKPlayerID playerID) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if (!playerStats.ContainsKey(playerID)) { Debug.Log((object)$"\ud83d\udcca [PlayerStats] {playerID} has no stats"); return; } string text = ""; foreach (PlayerStat item in playerStats[playerID]) { text += string.Format("{0}{1} {2}, ", (item.value >= 0) ? "+" : "", item.value, item.statName); } Debug.Log((object)$"\ud83d\udcca [PlayerStats] {playerID} current stats ({playerStats[playerID].Count} total): {text}"); } public static void ClearAllPlayerStats(FTKPlayerID playerID) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (playerStats.ContainsKey(playerID)) { playerStats[playerID].Clear(); Debug.Log((object)$"\ud83d\udcca [PlayerStats] Cleared all stats for {playerID}"); } if (equippedItems.ContainsKey(playerID)) { equippedItems[playerID].Clear(); Debug.Log((object)$"\ud83d\udce6 [PlayerStats] Cleared all equipped items for {playerID}"); } } public static void AddEquippedItem(FTKPlayerID playerID, string itemName, List stats) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!equippedItems.ContainsKey(playerID)) { equippedItems[playerID] = new List(); } equippedItems[playerID].Add(new EquippedItemStat { itemName = itemName, stats = stats }); Debug.Log((object)$"\ud83d\udce6 [PlayerStats] Added {itemName} with {stats.Count} stats to {playerID}. Total items: {equippedItems[playerID].Count}"); } public static void RemoveEquippedItem(FTKPlayerID playerID, string itemName) { //IL_0005: 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_0028: 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_005c: Unknown result type (might be due to invalid IL or missing references) if (!equippedItems.ContainsKey(playerID)) { return; } for (int num = equippedItems[playerID].Count - 1; num >= 0; num--) { if (equippedItems[playerID][num].itemName == itemName) { Debug.Log((object)$"\ud83d\udce6 [PlayerStats] Removed {itemName} from {playerID}"); equippedItems[playerID].RemoveAt(num); break; } } } public static List GetEquippedItems(FTKPlayerID playerID) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (equippedItems.ContainsKey(playerID)) { return equippedItems[playerID]; } return new List(); } } [HarmonyPatch(typeof(FTK_weaponStats2DB), "GetEntry")] public class ExtraStatsPatch { private static HashSet needsExtraStats = new HashSet(); private static HashSet generatedItems = new HashSet(); private static bool hasMarkedAllItems = false; private static void Postfix(ref FTK_weaponStats2 __result, ID _enumID) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected I4, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (Main.EnableRandomization.Value && !((Object)(object)GameLogic.Instance == (Object)null)) { int num = (int)_enumID; if (!ExtraStatsData.ItemMods.ContainsKey(num) && !needsExtraStats.Contains(num)) { needsExtraStats.Add(num); Debug.Log((object)$"\ud83d\udd16 [FTKRandomItemsStats] {_enumID}: Marcado para generar stats extra al equipar"); } } } public static void MarkAllWeapons() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected I4, but got Unknown if (hasMarkedAllItems || !Main.EnableRandomization.Value) { return; } try { FTK_weaponStats2DB dB = FTK_weaponStats2DB.GetDB(); if (!((Object)(object)dB != (Object)null) || ((GEDataArray)(object)dB).m_Array == null) { return; } FTK_weaponStats2[] array = ((GEDataArray)(object)dB).m_Array; foreach (FTK_weaponStats2 val in array) { if (val != null) { int num = (int)FTK_itembase.GetEnum(((GEDataBase)val).m_ID); if (!needsExtraStats.Contains(num) && !ExtraStatsData.ItemMods.ContainsKey(num)) { needsExtraStats.Add(num); Debug.Log((object)("\ud83d\udd16 [FTKRandomItemsStats] Marcado por lotes: " + ((GEDataBase)val).m_ID)); } } } hasMarkedAllItems = true; Debug.Log((object)$"✅ [FTKRandomItemsStats] Marcadas {needsExtraStats.Count} armas para stats extra"); } catch (Exception ex) { Debug.LogError((object)("❌ Error marcando armas: " + ex.Message)); } } public static bool NeedsExtraStats(int itemKey) { return needsExtraStats.Contains(itemKey); } public static void GenerateExtraStatsForItem(int itemKey, int playerLuckSeed) { if (generatedItems.Contains(itemKey) || ExtraStatsData.ItemMods.ContainsKey(itemKey) || !needsExtraStats.Contains(itemKey)) { return; } try { Random.InitState(GameLogic.Instance.m_MapGenRandomSeed + itemKey + 10000 + playerLuckSeed); int num = Random.Range(Main.ExtraStatsMinCount.Value, Main.ExtraStatsMaxCount.Value + 1); string[] array = Main.PossibleStats.Value.Split(new char[1] { ',' }); string text = ""; for (int i = 0; i < array.Length; i++) { if (i > 0) { text += ", "; } text += array[i]; } Debug.Log((object)$"\ud83d\udd0d Stats posibles ({array.Length}): {text}"); List list = new List(array); List list2 = new List(); int num2 = 0; int num3 = 4; for (int j = 0; j < num; j++) { if (list.Count <= 0) { break; } int index = Random.Range(0, list.Count); string text2 = list[index]; list.RemoveAt(index); int num4; if (num2 < num3 && Random.value < 0.3f) { num4 = Random.Range(-4, 0); num2++; Debug.Log((object)$"\ud83d\udd3b Stat negativo generado: {text2} = {num4}"); } else { num4 = Random.Range(1, 7); } list2.Add(new ExtraStatsData.ExtraStat { statName = text2, value = num4 }); } if (list2.Count > 0) { ExtraStatsData.ItemMods[itemKey] = list2; generatedItems.Add(itemKey); string text3 = ""; for (int k = 0; k < list2.Count; k++) { ExtraStatsData.ExtraStat extraStat = list2[k]; if (k > 0) { text3 += ", "; } text3 += string.Format("{0}{1}{2}", extraStat.statName, (extraStat.value >= 0) ? "+" : "", extraStat.value); } Debug.Log((object)$"✨ [FTKRandomItemsStats] Generados {list2.Count} stats extra para item {itemKey}: {text3}"); } needsExtraStats.Remove(itemKey); } catch (Exception ex) { Debug.LogError((object)("❌ Error generando stats extra: " + ex.Message)); } } } [HarmonyPatch(typeof(CharacterOverworld), "EquipItemRPC")] public class ApplyExtraStatsOnEquip { private static Dictionary> appliedStatsKeys = new Dictionary>(); private static Dictionary currentWeapon = new Dictionary(); private static bool hasMarkedAll = false; private static void Postfix(CharacterOverworld __instance, ID _item, bool _isSwapWeapon = false) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected I4, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) Debug.Log((object)$"\ud83d\udd0d [DIAGNOSTICO] EquipItemRPC ejecutado para {_item} | Frame: {Time.frameCount} | Tiempo: {Time.time}"); if (!Main.EnableRandomization.Value) { return; } if (!hasMarkedAll) { ExtraStatsPatch.MarkAllWeapons(); hasMarkedAll = true; } int num = (int)_item; CharacterStats characterStats = __instance.m_CharacterStats; if ((Object)(object)characterStats == (Object)null) { return; } string localizedName = FTK_itembase.GetItemBase(_item).GetLocalizedName(); Debug.Log((object)$"\ud83d\udd27 [Equip] Player {characterStats.m_CharacterName} equipando {localizedName} ({_item})"); if (currentWeapon.ContainsKey(__instance.m_FTKPlayerID) && currentWeapon[__instance.m_FTKPlayerID] == num) { Debug.Log((object)("\ud83d\udd27 [Equip] Misma arma detectada (" + localizedName + "), no se aplicarán stats duplicados")); return; } if (currentWeapon.ContainsKey(__instance.m_FTKPlayerID)) { int num2 = currentWeapon[__instance.m_FTKPlayerID]; if (appliedStatsKeys.ContainsKey(num2)) { Debug.Log((object)$"\ud83d\udd27 [Equip] Removing old stats from weapon {num2}"); foreach (string item in appliedStatsKeys[num2]) { RemoveStatByKey(characterStats, item); } appliedStatsKeys.Remove(num2); } if (ExtraStatsData.ItemMods.ContainsKey(num2)) { List list = new List(); foreach (ExtraStatsData.ExtraStat item2 in ExtraStatsData.ItemMods[num2]) { list.Add(new PlayerStatsManager.PlayerStat { statName = item2.statName, value = item2.value }); } PlayerStatsManager.RemovePlayerStatsForItem(__instance.m_FTKPlayerID, list); PlayerStatsManager.RemoveEquippedItem(__instance.m_FTKPlayerID, FTK_itembase.GetItemBase((ID)num2).GetLocalizedName()); } } if (ExtraStatsPatch.NeedsExtraStats(num)) { int playerLuckSeed = (int)characterStats.Luck; ExtraStatsPatch.GenerateExtraStatsForItem(num, playerLuckSeed); } if (!ExtraStatsData.ItemMods.ContainsKey(num)) { Debug.Log((object)("\ud83d\udd27 [Equip] No extra stats for " + localizedName)); currentWeapon[__instance.m_FTKPlayerID] = num; return; } List list2 = new List(); List list3 = new List(); HashSet hashSet = new HashSet(); foreach (ExtraStatsData.ExtraStat item3 in ExtraStatsData.ItemMods[num]) { if (hashSet.Contains(item3.statName)) { continue; } hashSet.Add(item3.statName); try { string text = ApplyStatAndGetKey(characterStats, item3.statName, item3.value); if (text != null) { list2.Add(text); } list3.Add(new PlayerStatsManager.PlayerStat { statName = item3.statName, value = item3.value }); _ = item3.value; _ = 0; } catch (Exception ex) { if ((Object)(object)characterStats != (Object)null && (Object)(object)characterStats.m_CharacterOverworld != (Object)null) { characterStats.UpdateAllCharacterStats(false); characterStats.m_CharacterOverworld.m_UIPlayMainHud.UpdateHud(); } Debug.LogError((object)("❌ Error aplicando " + item3.statName + ": " + ex.Message)); } } if (list2.Count > 0) { appliedStatsKeys[num] = list2; } currentWeapon[__instance.m_FTKPlayerID] = num; foreach (PlayerStatsManager.PlayerStat item4 in list3) { PlayerStatsManager.AddPlayerStat(__instance.m_FTKPlayerID, item4); } if (list3.Count > 0) { PlayerStatsManager.AddEquippedItem(__instance.m_FTKPlayerID, localizedName, list3); } PlayerStatsManager.DebugPrintStats(__instance.m_FTKPlayerID); Debug.Log((object)$"✨ Aplicados {list3.Count} modificadores a {characterStats.m_CharacterName}"); characterStats.UpdateAllCharacterStats(false); } private static string ApplyStatAndGetKey(CharacterStats stats, string statName, int value) { string text = statName.ToLower(); float num = (float)Math.Abs(value) / 100f; int num2 = Math.Sign(value); string result = $"{statName}_{value}_{DateTime.Now.Ticks}"; switch (text) { case "strength": stats.AugmentCharacterStat((SkillType)0, num * (float)num2); Debug.Log((object)string.Format("\ud83d\udd27 Strength aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "vitality": stats.AugmentCharacterStat((SkillType)3, num * (float)num2); Debug.Log((object)string.Format("\ud83d\udd27 Vitality aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "awareness": stats.AugmentCharacterStat((SkillType)2, num * (float)num2); Debug.Log((object)string.Format("\ud83d\udd27 Awareness aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "speed": stats.AugmentCharacterStat((SkillType)4, num * (float)num2); Debug.Log((object)string.Format("\ud83d\udd27 Speed aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "talent": stats.AugmentCharacterStat((SkillType)5, num * (float)num2); Debug.Log((object)string.Format("\ud83d\udd27 Talent aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "intelligence": stats.AugmentCharacterStat((SkillType)1, num * (float)num2); Debug.Log((object)string.Format("\ud83e\udde0 Intelligence aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "armor": stats.AugmentCharacterOther((TrainerType)2, (float)value); Debug.Log((object)string.Format("\ud83d\udd27 Armor aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "resistance": stats.AugmentCharacterOther((TrainerType)3, (float)value); Debug.Log((object)string.Format("\ud83d\udd27 Resistance aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "evade": stats.AugmentCharacterOther((TrainerType)4, num * (float)num2); Debug.Log((object)string.Format("\ud83d\udd27 Evade aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "focus": stats.AugmentCharacterOther((TrainerType)0, (float)value); Debug.Log((object)string.Format("✨ Focus aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "maxhealth": stats.AugmentCharacterOther((TrainerType)1, (float)value); Debug.Log((object)string.Format("❤\ufe0f MaxHealth aplicado: {0}{1}", (num2 > 0) ? "+" : "", value)); break; case "critchance": { FieldInfo field = typeof(CharacterStats).GetField("m_ModCritChance", BindingFlags.Instance | BindingFlags.NonPublic); if ((object)field == null) { return null; } float num3 = (float)field.GetValue(stats); field.SetValue(stats, num3 + (float)value / 100f); stats.UpdateAllCharacterStats(false); Debug.Log((object)string.Format("⚔\ufe0f CritChance aplicado: {0}{1}%", (value >= 0) ? "+" : "", value)); break; } default: return null; } return result; } private static void RemoveStatByKey(CharacterStats stats, string key) { if (string.IsNullOrEmpty(key)) { return; } string[] array = key.Split(new char[1] { '_' }); if (array.Length < 2) { return; } string text = array[0]; if (!int.TryParse(array[1], out var result)) { return; } int num = Math.Sign(result); float num2 = (float)Math.Abs(result) / 100f; switch (text.ToLower()) { case "strength": stats.AugmentCharacterStat((SkillType)0, (0f - num2) * (float)num); break; case "vitality": stats.AugmentCharacterStat((SkillType)3, (0f - num2) * (float)num); break; case "awareness": stats.AugmentCharacterStat((SkillType)2, (0f - num2) * (float)num); break; case "speed": stats.AugmentCharacterStat((SkillType)4, (0f - num2) * (float)num); break; case "talent": stats.AugmentCharacterStat((SkillType)5, (0f - num2) * (float)num); break; case "intelligence": stats.AugmentCharacterStat((SkillType)1, (0f - num2) * (float)num); break; case "armor": stats.AugmentCharacterOther((TrainerType)2, (float)(-result)); break; case "resistance": stats.AugmentCharacterOther((TrainerType)3, (float)(-result)); break; case "evade": stats.AugmentCharacterOther((TrainerType)4, (0f - num2) * (float)num); break; case "focus": stats.AugmentCharacterOther((TrainerType)0, (float)(-result)); break; case "maxhealth": stats.AugmentCharacterOther((TrainerType)1, (float)(-result)); break; case "critchance": { FieldInfo field = typeof(CharacterStats).GetField("m_ModCritChance", BindingFlags.Instance | BindingFlags.NonPublic); if ((object)field == null) { return; } float num3 = (float)field.GetValue(stats); field.SetValue(stats, num3 - (float)result / 100f); stats.UpdateAllCharacterStats(false); Debug.Log((object)string.Format("⚔\ufe0f CritChance removido: {0}{1}%", (result >= 0) ? "-" : "+", Math.Abs(result))); break; } } Debug.Log((object)("\ud83d\udd27 Stat removido: " + text)); stats.UpdateAllCharacterStats(false); } } [HarmonyPatch(typeof(CharacterOverworld), "Start")] public static class InitialWeaponPatch { private static void Postfix(CharacterOverworld __instance) { //IL_002b: 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) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (Main.EnableRandomization.Value && !((Object)(object)GameLogic.Instance == (Object)null)) { ExtraStatsPatch.MarkAllWeapons(); ID firstItemID = GetFirstItemID((ItemContainer)(object)__instance.m_PlayerInventory.m_ContainerRightHand); if ((int)firstItemID != -1) { ApplyStatsForWeapon(__instance, firstItemID); } ID firstItemID2 = GetFirstItemID((ItemContainer)(object)__instance.m_PlayerInventory.m_ContainerLeftHand); if ((int)firstItemID2 != -1) { ApplyStatsForWeapon(__instance, firstItemID2); } __instance.m_CharacterStats.UpdateAllCharacterStats(false); } } private static ID GetFirstItemID(ItemContainer container) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (((container != null) ? container.m_CountDictionary : null) == null) { return (ID)(-1); } Dictionary.KeyCollection.Enumerator enumerator = container.m_CountDictionary.Keys.GetEnumerator(); if (enumerator.MoveNext()) { return enumerator.Current; } return (ID)(-1); } private static void ApplyStatsForWeapon(CharacterOverworld character, ID weaponID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) int num = (int)weaponID; string localizedName = FTK_itembase.GetItemBase(weaponID).GetLocalizedName(); if (ExtraStatsPatch.NeedsExtraStats(num)) { int playerLuckSeed = (int)character.m_CharacterStats.Luck; ExtraStatsPatch.GenerateExtraStatsForItem(num, playerLuckSeed); } if (!ExtraStatsData.ItemMods.TryGetValue(num, out var value)) { return; } List list = new List(); foreach (ExtraStatsData.ExtraStat item in value) { ApplyStatDirectly(character.m_CharacterStats, item.statName, item.value); list.Add(new PlayerStatsManager.PlayerStat { statName = item.statName, value = item.value }); } foreach (PlayerStatsManager.PlayerStat item2 in list) { PlayerStatsManager.AddPlayerStat(character.m_FTKPlayerID, item2); } if (list.Count > 0) { PlayerStatsManager.AddEquippedItem(character.m_FTKPlayerID, localizedName, list); } Debug.Log((object)$"✨ [InitialWeapon] Stats aplicadas y registradas para {localizedName} ({weaponID}) en {character.m_CharacterStats.m_CharacterName}"); if ((Object)(object)character.m_CharacterStats != (Object)null && (Object)(object)character.m_CharacterStats.m_CharacterOverworld != (Object)null) { character.m_CharacterStats.UpdateAllCharacterStats(false); character.m_CharacterStats.m_CharacterOverworld.m_UIPlayMainHud.UpdateHud(); } } private static void ApplyStatDirectly(CharacterStats stats, string statName, int value) { string text = statName.ToLower(); float num = (float)Math.Abs(value) / 100f; int num2 = Math.Sign(value); switch (text) { case "strength": stats.AugmentCharacterStat((SkillType)0, num * (float)num2); break; case "vitality": stats.AugmentCharacterStat((SkillType)3, num * (float)num2); break; case "awareness": stats.AugmentCharacterStat((SkillType)2, num * (float)num2); break; case "speed": stats.AugmentCharacterStat((SkillType)4, num * (float)num2); break; case "talent": stats.AugmentCharacterStat((SkillType)5, num * (float)num2); break; case "intelligence": stats.AugmentCharacterStat((SkillType)1, num * (float)num2); break; case "armor": stats.AugmentCharacterOther((TrainerType)2, (float)value); break; case "resistance": stats.AugmentCharacterOther((TrainerType)3, (float)value); break; case "evade": stats.AugmentCharacterOther((TrainerType)4, num * (float)num2); break; case "focus": stats.AugmentCharacterOther((TrainerType)0, (float)value); break; case "maxhealth": stats.AugmentCharacterOther((TrainerType)1, (float)value); break; case "critchance": { FieldInfo field = typeof(CharacterStats).GetField("m_ModCritChance", BindingFlags.Instance | BindingFlags.NonPublic); if ((object)field != null) { float num3 = (float)field.GetValue(stats); field.SetValue(stats, num3 + (float)value / 100f); } break; } } } } public static class LuckHelper { public static float GetCurrentPlayerLuck() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)EncounterSessionMC.Instance != (Object)null && EncounterSessionMC.Instance.m_AllCombtatantsAlive != null && EncounterSessionMC.Instance.m_AllCombtatantsAlive.Count > 0) { FTKPlayerID val = EncounterSessionMC.Instance.m_AllCombtatantsAlive[0]; if ((Object)(object)((FTKPlayerID)(ref val)).GetCow() != (Object)null && (Object)(object)((FTKPlayerID)(ref val)).GetCow().m_CharacterStats != (Object)null) { return ((FTKPlayerID)(ref val)).GetCow().m_CharacterStats.Luck; } } } catch (Exception ex) { Debug.LogWarning((object)("LuckHelper: Error obteniendo suerte - " + ex.Message)); } return Main.BaseLuckValue.Value; } public static float GetLuckInfluenceFactor() { float currentPlayerLuck = GetCurrentPlayerLuck(); float num = Main.BaseLuckValue.Value; float num2 = Main.LuckStepSize.Value; float num3 = Main.LuckStepPercent.Value / 100f; return Mathf.Clamp((float)(int)((currentPlayerLuck - num) / num2) * num3, -0.5f, 0.5f); } public static float GetPositiveProbability() { float luckInfluenceFactor = GetLuckInfluenceFactor(); return Mathf.Clamp(0.5f + luckInfluenceFactor, 0.2f, 0.8f); } public static bool ShouldBePositive() { float positiveProbability = GetPositiveProbability(); return Random.value < positiveProbability; } public static float GetRandomizedMultiplier(float minPercent, float maxPercent) { float num = minPercent / 100f; float num2 = maxPercent / 100f; if (ShouldBePositive()) { float value = Random.value; return 1f + num2 * value; } float value2 = Random.value; return 1f + num * value2; } public static int GetExtraStatsCount(int minCount, int maxCount) { float luckInfluenceFactor = GetLuckInfluenceFactor(); float num = (float)(minCount + maxCount) / 2f; float num2 = num + luckInfluenceFactor * (float)(maxCount - minCount) / 2f; int num3 = Mathf.RoundToInt(num2); num3 = Mathf.Clamp(num3, minCount, maxCount); Debug.Log((object)$"\ud83c\udfb2 ExtraStatsCount: influence={luckInfluenceFactor:F2}, base={num:F1}, final={num2:F1}, count={num3}"); return num3; } public static int GetExtraStatValue(int minValue, int maxValue) { float luckInfluenceFactor = GetLuckInfluenceFactor(); return Mathf.Clamp(Mathf.RoundToInt((float)(minValue + maxValue) / 2f + luckInfluenceFactor * (float)(maxValue - minValue) / 2f), minValue, maxValue); } } [BepInPlugin("FTKRandomItemsStats", "FTKRandomItemsStats", "1.0.0")] public class Main : BaseUnityPlugin { public static Main Instance; public static ConfigEntry EnableRandomization; public static ConfigEntry IncludeQuestItems; public static ConfigEntry IncludeLegendary; public static ConfigEntry DamageMinPercent; public static ConfigEntry DamageMaxPercent; public static ConfigEntry ExtraStatsMinCount; public static ConfigEntry ExtraStatsMaxCount; public static ConfigEntry PossibleStats; public static ConfigEntry ExtraStatValueMin; public static ConfigEntry ExtraStatValueMax; public static ConfigEntry BaseLuckValue; public static ConfigEntry LuckStepPercent; public static ConfigEntry LuckStepSize; private void Awake() { //IL_01e3: Unknown result type (might be due to invalid IL or missing references) Instance = this; Type typeFromHandle = typeof(FTK_weaponStats2DB); Debug.Log((object)("\ud83d\udfe2 Ensamblado FTK cargado: " + typeFromHandle.Assembly.FullName)); EnableRandomization = ((BaseUnityPlugin)this).Config.Bind("General", "EnableRandomization", true, "Activar/desactivar randomización"); IncludeQuestItems = ((BaseUnityPlugin)this).Config.Bind("General", "IncludeQuestItems", true, "Randomizar objetos de misión"); IncludeLegendary = ((BaseUnityPlugin)this).Config.Bind("General", "IncludeLegendary", true, "Randomizar objetos legendarios/épicos"); DamageMinPercent = ((BaseUnityPlugin)this).Config.Bind("Damage", "MinPercent", -35f, "Porcentaje mínimo de daño (-100 a 0)"); DamageMaxPercent = ((BaseUnityPlugin)this).Config.Bind("Damage", "MaxPercent", 50f, "Porcentaje máximo de daño (0 a 200)"); ExtraStatsMinCount = ((BaseUnityPlugin)this).Config.Bind("ExtraStats", "MinCount", 0, "Cantidad mínima de stats extra (0-6)"); ExtraStatsMaxCount = ((BaseUnityPlugin)this).Config.Bind("ExtraStats", "MaxCount", 6, "Cantidad máxima de stats extra (0-6)"); PossibleStats = ((BaseUnityPlugin)this).Config.Bind("ExtraStats", "PossibleStats", "vitality,awareness,talent,evade,focus,armor,resistance,strength,intelligence,speed,critChance,maxHealth", "Stats posibles separados por coma"); ExtraStatValueMin = ((BaseUnityPlugin)this).Config.Bind("ExtraStats", "ValueMin", -4, "Valor mínimo del stat extra (-4 a 6, excluyendo 0)"); ExtraStatValueMax = ((BaseUnityPlugin)this).Config.Bind("ExtraStats", "ValueMax", 6, "Valor máximo del stat extra (-4 a 6, excluyendo 0)"); BaseLuckValue = ((BaseUnityPlugin)this).Config.Bind("Luck", "BaseLuckValue", 55, "Valor base de suerte (0% de bonus)"); LuckStepPercent = ((BaseUnityPlugin)this).Config.Bind("Luck", "LuckStepPercent", 4f, "Cada X puntos de suerte da este % de bonus"); LuckStepSize = ((BaseUnityPlugin)this).Config.Bind("Luck", "LuckStepSize", 10, "Cada cuántos puntos de suerte se aplica el bonus"); new Harmony("FTKRandomItemsStats").PatchAll(); Debug.Log((object)"\ud83d\udfe2 Harmony PatchAll ejecutado"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"═══════════════════════════════════════"); ((BaseUnityPlugin)this).Logger.LogInfo((object)" FTKRandomItemsStats ha sido cargado"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"═══════════════════════════════════════"); ((BaseUnityPlugin)this).Logger.LogInfo((object)" Hecho por FINISHIM en el MEJOR pais de CHILE"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"═══════════════════════════════════════"); ((BaseUnityPlugin)this).Logger.LogInfo((object)" Versión: 1.0.0"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"═══════════════════════════════════════"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"FTKRandomItemsStats ha sido cargado correctamente!"); } private void Update() { if (Input.GetKeyDown((KeyCode)289)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"═══════════════════════════════════════"); ((BaseUnityPlugin)this).Logger.LogInfo((object)" CONFIGURACIÓN RECARGADA"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"═══════════════════════════════════════"); ((BaseUnityPlugin)this).Config.Reload(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Configuración recargada!"); } } } [HarmonyPatch(typeof(FTK_weaponStats2DB), "GetEntry")] public class WeaponStatsPatch { private static HashSet randomizedItems = new HashSet(); private const int UNARMED_ID = 100023; private static void Postfix(ref FTK_weaponStats2 __result, ID _enumID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) int num = (int)_enumID; if (num == 100023) { Debug.Log((object)$"\ud83d\udd27 [FTKRandomItemsStats] {_enumID}: Excluido de randomización de daño (unarmed)"); } else if (!randomizedItems.Contains(num) && Main.EnableRandomization.Value && !((Object)(object)GameLogic.Instance == (Object)null)) { try { Random.InitState(GameLogic.Instance.m_MapGenRandomSeed + num); float randomizedMultiplier = LuckHelper.GetRandomizedMultiplier(Main.DamageMinPercent.Value, Main.DamageMaxPercent.Value); float num2 = __result._maxdmg * randomizedMultiplier; num2 = (float)Math.Round(num2, 1); __result._maxdmg = Mathf.Max(1f, num2); Debug.Log((object)$"\ud83d\udd27 [FTKRandomItemsStats] {_enumID}: Daño randomizado → {__result._maxdmg:F1}"); randomizedItems.Add(num); } catch (Exception ex) { Debug.LogError((object)("❌ Error: " + ex.Message)); } } } }