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<string, string> statColors = new Dictionary<string, string>(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<PlayerStatsManager.EquippedItemStat> 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 += $"<color={text2}>{text3}{playerStat.value} {playerStat.statName}</color>";
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 += $"<color={text5}>{text6}{playerStat2.value} {playerStat2.statName}</color>";
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<int, List<ExtraStat>> ItemMods = new Dictionary<int, List<ExtraStat>>();
}
public static class PlayerStatsManager
{
public class EquippedItemStat
{
public string itemName;
public List<PlayerStat> stats;
}
public class PlayerStat
{
public string statName;
public int value;
}
private static Dictionary<FTKPlayerID, List<PlayerStat>> playerStats = new Dictionary<FTKPlayerID, List<PlayerStat>>();
private static Dictionary<FTKPlayerID, List<EquippedItemStat>> equippedItems = new Dictionary<FTKPlayerID, List<EquippedItemStat>>();
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<PlayerStat>();
}
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<PlayerStat> 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<PlayerStat>();
}
public static void RemovePlayerStatsForItem(FTKPlayerID playerID, List<PlayerStat> 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<PlayerStat> 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<PlayerStat> 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<EquippedItemStat>();
}
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<EquippedItemStat> 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<EquippedItemStat>();
}
}
[HarmonyPatch(typeof(FTK_weaponStats2DB), "GetEntry")]
public class ExtraStatsPatch
{
private static HashSet<int> needsExtraStats = new HashSet<int>();
private static HashSet<int> generatedItems = new HashSet<int>();
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<FTK_weaponStats2>)(object)dB).m_Array == null)
{
return;
}
FTK_weaponStats2[] array = ((GEDataArray<FTK_weaponStats2>)(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<string> list = new List<string>(array);
List<ExtraStatsData.ExtraStat> list2 = new List<ExtraStatsData.ExtraStat>();
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<int, List<string>> appliedStatsKeys = new Dictionary<int, List<string>>();
private static Dictionary<FTKPlayerID, int> currentWeapon = new Dictionary<FTKPlayerID, int>();
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<PlayerStatsManager.PlayerStat> list = new List<PlayerStatsManager.PlayerStat>();
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<string> list2 = new List<string>();
List<PlayerStatsManager.PlayerStat> list3 = new List<PlayerStatsManager.PlayerStat>();
HashSet<string> hashSet = new HashSet<string>();
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<ID, int>.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<PlayerStatsManager.PlayerStat> list = new List<PlayerStatsManager.PlayerStat>();
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<bool> EnableRandomization;
public static ConfigEntry<bool> IncludeQuestItems;
public static ConfigEntry<bool> IncludeLegendary;
public static ConfigEntry<float> DamageMinPercent;
public static ConfigEntry<float> DamageMaxPercent;
public static ConfigEntry<int> ExtraStatsMinCount;
public static ConfigEntry<int> ExtraStatsMaxCount;
public static ConfigEntry<string> PossibleStats;
public static ConfigEntry<int> ExtraStatValueMin;
public static ConfigEntry<int> ExtraStatValueMax;
public static ConfigEntry<int> BaseLuckValue;
public static ConfigEntry<float> LuckStepPercent;
public static ConfigEntry<int> 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<bool>("General", "EnableRandomization", true, "Activar/desactivar randomización");
IncludeQuestItems = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeQuestItems", true, "Randomizar objetos de misión");
IncludeLegendary = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeLegendary", true, "Randomizar objetos legendarios/épicos");
DamageMinPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "MinPercent", -35f, "Porcentaje mínimo de daño (-100 a 0)");
DamageMaxPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "MaxPercent", 50f, "Porcentaje máximo de daño (0 a 200)");
ExtraStatsMinCount = ((BaseUnityPlugin)this).Config.Bind<int>("ExtraStats", "MinCount", 0, "Cantidad mínima de stats extra (0-6)");
ExtraStatsMaxCount = ((BaseUnityPlugin)this).Config.Bind<int>("ExtraStats", "MaxCount", 6, "Cantidad máxima de stats extra (0-6)");
PossibleStats = ((BaseUnityPlugin)this).Config.Bind<string>("ExtraStats", "PossibleStats", "vitality,awareness,talent,evade,focus,armor,resistance,strength,intelligence,speed,critChance,maxHealth", "Stats posibles separados por coma");
ExtraStatValueMin = ((BaseUnityPlugin)this).Config.Bind<int>("ExtraStats", "ValueMin", -4, "Valor mínimo del stat extra (-4 a 6, excluyendo 0)");
ExtraStatValueMax = ((BaseUnityPlugin)this).Config.Bind<int>("ExtraStats", "ValueMax", 6, "Valor máximo del stat extra (-4 a 6, excluyendo 0)");
BaseLuckValue = ((BaseUnityPlugin)this).Config.Bind<int>("Luck", "BaseLuckValue", 55, "Valor base de suerte (0% de bonus)");
LuckStepPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Luck", "LuckStepPercent", 4f, "Cada X puntos de suerte da este % de bonus");
LuckStepSize = ((BaseUnityPlugin)this).Config.Bind<int>("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<int> randomizedItems = new HashSet<int>();
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));
}
}
}
}