using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using KillCountEvolution.Config;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("KillCountEvolution")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MobLevelSystem")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b0de67da-3bbf-4641-bf42-71bdb5d846ed")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
internal class EnemyHudPatch
{
private static void Postfix(EnemyHud __instance)
{
if (!(AccessTools.Field(typeof(EnemyHud), "m_huds").GetValue(__instance) is IDictionary dictionary))
{
return;
}
foreach (DictionaryEntry item in dictionary)
{
object key = item.Key;
Character val = (Character)((key is Character) ? key : null);
if ((Object)(object)val == (Object)null)
{
continue;
}
int level = val.GetLevel();
if (level <= 3)
{
continue;
}
object value = item.Value;
if (value == null)
{
continue;
}
FieldInfo fieldInfo = AccessTools.Field(value.GetType(), "m_name");
if (!(fieldInfo == null))
{
object? value2 = fieldInfo.GetValue(value);
TMP_Text val2 = (TMP_Text)((value2 is TMP_Text) ? value2 : null);
if (!((Object)(object)val2 == (Object)null) && !val2.text.Contains("★"))
{
int count = level - 1;
string text = new string('★', count);
val2.text = val2.text + " <color=#FFD700>" + text + "</color>";
}
}
}
}
}
namespace KillCountEvolution
{
[BepInPlugin("com.basicMods.KillCountEvolution", "Kill Count Evolution", "1.0.0")]
public class KillCountEvolution : BaseUnityPlugin
{
public static KillCountEvolution Instance;
public MobSpawnConfig mobConfig;
public PlayerData localPlayerData;
private string saveFolder;
private Dictionary<long, int> onlinePeerContributions = new Dictionary<long, int>();
private int globalTotalKills;
private void Awake()
{
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
Instance = this;
saveFolder = Path.Combine(Paths.BepInExRootPath, "plugins", "KillCountEvolution");
Directory.CreateDirectory(saveFolder);
mobConfig = new MobSpawnConfig((BaseUnityPlugin)(object)this);
new Harmony("com.basicMods.KillCountEvolution").PatchAll();
}
public void SetupRPCs()
{
ZRoutedRpc.instance.Register<int>("RPC_SubmitInitialKills", (Action<long, int>)RPC_SubmitInitialKills);
ZRoutedRpc.instance.Register<int>("RPC_SyncGlobalKills", (Action<long, int>)RPC_SyncGlobalKills);
ZRoutedRpc.instance.Register("RPC_AddOneKill", (Action<long>)RPC_AddOneKill);
}
private void RPC_SubmitInitialKills(long sender, int kills)
{
if (ZNet.instance.IsServer())
{
onlinePeerContributions[sender] = kills;
UpdateGlobalTotal();
}
}
private void RPC_AddOneKill(long sender)
{
if (ZNet.instance.IsServer())
{
if (onlinePeerContributions.ContainsKey(sender))
{
onlinePeerContributions[sender]++;
}
else
{
onlinePeerContributions[sender] = 1;
}
UpdateGlobalTotal();
}
}
private void RPC_SyncGlobalKills(long sender, int total)
{
globalTotalKills = total;
}
private void UpdateGlobalTotal()
{
globalTotalKills = onlinePeerContributions.Values.Sum();
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RPC_SyncGlobalKills", new object[1] { globalTotalKills });
SaveMasterJson();
}
public void OnPlayerDisconnect(long peerID)
{
if (ZNet.instance.IsServer() && onlinePeerContributions.ContainsKey(peerID))
{
onlinePeerContributions.Remove(peerID);
UpdateGlobalTotal();
}
}
public void LoadLocalData()
{
if (!((Object)(object)Player.m_localPlayer == (Object)null))
{
string worldName = ZNet.instance.GetWorldName();
string playerName = Player.m_localPlayer.GetPlayerName();
string path = Path.Combine(saveFolder, worldName + "_" + playerName + ".json");
if (File.Exists(path))
{
string text = File.ReadAllText(path);
localPlayerData = JsonUtility.FromJson<PlayerData>(text);
Debug.Log((object)$"[KillCountEvolution] Betöltve: {playerName} - {localPlayerData.TotalKills} ölés.");
}
else
{
localPlayerData = new PlayerData
{
PlayerName = playerName,
TotalKills = 0
};
Debug.Log((object)"[KillCountEvolution] Új player adat létrehozva.");
}
ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_SubmitInitialKills", new object[1] { localPlayerData.TotalKills });
}
}
public void RegisterLocalKill()
{
localPlayerData.TotalKills++;
SaveLocalJson();
ZRoutedRpc.instance.InvokeRoutedRPC(0L, "RPC_AddOneKill", Array.Empty<object>());
}
private void SaveLocalJson()
{
string worldName = ZNet.instance.GetWorldName();
File.WriteAllText(Path.Combine(saveFolder, worldName + "_" + localPlayerData.PlayerName + ".json"), JsonUtility.ToJson((object)localPlayerData, true));
}
public void SaveMasterJson(bool forceZero = false)
{
if (!((Object)(object)ZNet.instance == (Object)null))
{
int num = ((!forceZero) ? globalTotalKills : 0);
File.WriteAllText(Path.Combine(saveFolder, ZNet.instance.GetWorldName() + "_MASTER.json"), "{\"GlobalOnlineKills\": " + num + "}");
}
}
public int GetTotalKills()
{
return globalTotalKills;
}
public void ClearGlobalData()
{
onlinePeerContributions.Clear();
globalTotalKills = 0;
}
}
[Serializable]
public class PlayerData
{
public string PlayerName;
public int TotalKills;
}
[HarmonyPatch(typeof(ZNet), "Awake")]
internal class ZNet_Awake_Patch
{
private static void Postfix()
{
KillCountEvolution.Instance.SetupRPCs();
}
}
[HarmonyPatch(typeof(Player), "OnSpawned")]
internal class Player_Spawn_Patch
{
private static void Postfix()
{
KillCountEvolution.Instance.LoadLocalData();
}
}
[HarmonyPatch(typeof(ZNet), "Shutdown")]
internal class ZNet_Shutdown_Patch
{
private static void Prefix()
{
if ((Object)(object)KillCountEvolution.Instance != (Object)null && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
{
KillCountEvolution.Instance.ClearGlobalData();
KillCountEvolution.Instance.SaveMasterJson(forceZero: true);
}
}
}
[HarmonyPatch(typeof(Character), "Damage")]
internal class Damage_Patch
{
private static void Postfix(Character __instance, HitData hit)
{
if (__instance.GetHealth() <= 0f && !__instance.IsPlayer())
{
Character attacker = hit.GetAttacker();
Player val = (Player)(object)((attacker is Player) ? attacker : null);
if ((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)Player.m_localPlayer)
{
KillCountEvolution.Instance.RegisterLocalKill();
}
}
}
}
public static class RankManager
{
public static int RollCustomLevel(int originalLevel)
{
MobSpawnConfig mobConfig = KillCountEvolution.Instance.mobConfig;
int totalKills = KillCountEvolution.Instance.GetTotalKills();
float num = mobConfig.Chance5Star.Value + Mathf.Min((float)totalKills * mobConfig.BonusPerKill5Star.Value, mobConfig.MaxBonus5Star.Value);
float num2 = mobConfig.Chance4Star.Value + Mathf.Min((float)totalKills * mobConfig.BonusPerKill4Star.Value, mobConfig.MaxBonus4Star.Value);
float num3 = mobConfig.Chance3Star.Value + Mathf.Min((float)totalKills * mobConfig.BonusPerKill3Star.Value, mobConfig.MaxBonus3Star.Value);
float num4 = num + num2 + num3;
float num5 = Mathf.Clamp(mobConfig.VanillaMinSpawnRate.Value, 0f, 100f);
float num6 = 100f - num5;
float num7;
if (num5 <= 0f)
{
num7 = num4;
}
else
{
if (num4 > num6)
{
float num8 = num6 / num4;
num *= num8;
num2 *= num8;
num3 *= num8;
num4 = num6;
}
num7 = 100f;
}
if (num7 <= 0f)
{
return originalLevel;
}
float num9 = Random.Range(0f, num7);
if (num9 < num)
{
return 6;
}
if (num9 < num + num2)
{
return 5;
}
if (num9 < num + num2 + num3)
{
return 4;
}
return originalLevel;
}
}
[HarmonyPatch(typeof(Character), "SetLevel")]
public static class SetLevelOverridePatch
{
private static void Prefix(Character __instance, ref int level)
{
//IL_0060: 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)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Invalid comparison between Unknown and I4
if ((Object)(object)__instance == (Object)null || __instance.IsPlayer() || (Object)(object)KillCountEvolution.Instance == (Object)null || KillCountEvolution.Instance.mobConfig == null)
{
return;
}
string prefabName = ((Object)__instance).name.Replace("(Clone)", "").Trim();
if (KillCountEvolution.Instance.mobConfig.IsPrefabExcluded(prefabName))
{
return;
}
Biome currentBiome = Heightmap.FindBiome(((Component)__instance).transform.position);
if (!KillCountEvolution.Instance.mobConfig.IsBiomeExcluded(currentBiome) && (int)__instance.m_faction != 1 && level <= 3)
{
int num = level;
int num2 = RankManager.RollCustomLevel(num);
if (num2 > num)
{
level = num2;
}
}
}
}
[HarmonyPatch(typeof(Character), "Awake")]
internal class CharacterAwakePatch
{
private static void Postfix(Character __instance)
{
//IL_0060: 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)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Invalid comparison between Unknown and I4
if ((Object)(object)__instance == (Object)null || __instance.IsPlayer() || (Object)(object)KillCountEvolution.Instance == (Object)null || KillCountEvolution.Instance.mobConfig == null)
{
return;
}
string prefabName = ((Object)__instance).name.Replace("(Clone)", "").Trim();
if (!KillCountEvolution.Instance.mobConfig.IsPrefabExcluded(prefabName))
{
Biome currentBiome = Heightmap.FindBiome(((Component)__instance).transform.position);
if (!KillCountEvolution.Instance.mobConfig.IsBiomeExcluded(currentBiome) && (int)__instance.m_faction != 1 && __instance.GetLevel() == 1)
{
__instance.SetLevel(1);
}
}
}
}
}
namespace KillCountEvolution.Config
{
public class MobSpawnConfig
{
public ConfigEntry<float> Chance3Star;
public ConfigEntry<float> Chance4Star;
public ConfigEntry<float> Chance5Star;
public ConfigEntry<string> ExcludedBiomes;
public ConfigEntry<string> ExcludedPrefabsConfig;
public ConfigEntry<float> BonusPerKill3Star;
public ConfigEntry<float> BonusPerKill4Star;
public ConfigEntry<float> BonusPerKill5Star;
public ConfigEntry<float> MaxBonus3Star;
public ConfigEntry<float> MaxBonus4Star;
public ConfigEntry<float> MaxBonus5Star;
public ConfigEntry<float> VanillaMinSpawnRate;
public ConfigEntry<string> LootBonusBlacklist;
private HashSet<string> excludedPrefabsSet = new HashSet<string>();
private HashSet<string> lootBlacklistSet = new HashSet<string>();
private static readonly string[] InternalBlacklist = new string[41]
{
"Eikthyr", "gd_king", "Bonemass", "Dragon", "GoblinKing", "SeekerQueen", "Fader", "Brenna", "Geirrhafa", "GoblinBrute_Hildir",
"GoblinShaman_Hildir", "Skeleton_Hildir", "Fenring_Cultist_Hildir", "Goblin_Hildir", "Charred_Melee_Dyrnwyn", "Haldor", "Hildir", "BogWitch", "Hugin", "Munin",
"Odin", "Dverger", "DvergerMage", "DvergerMageFire", "DvergerMageIce", "DvergerMageSupport", "DvergerAshlands", "Boar", "Wolf", "Lox",
"Asksvin", "Hen", "Chicken", "Deer", "Hare", "Neck", "Seagal", "Crow", "AshCrow", "Leviathan",
"Bat"
};
public MobSpawnConfig(BaseUnityPlugin plugin)
{
Chance3Star = plugin.Config.Bind<float>("Rates", "BaseChance3Star", 5f, "Base chance for 3-star (Level 4) spawn (%). Calculated 3-star bonus rates adds to this base.");
Chance4Star = plugin.Config.Bind<float>("Rates", "BaseChance4Star", 2f, "Base chance for 4-star (Level 5) spawn (%). Calculated 4-star bonus rates adds to this base.");
Chance5Star = plugin.Config.Bind<float>("Rates", "BaseChance5Star", 0.5f, "Base chance for 5-star (Level 6) spawn (%). Calculated 5-star bonus rates adds to this base.");
BonusPerKill3Star = plugin.Config.Bind<float>("Progression", "BonusPerKill3Star", 0.02f, "Bonus % added to 3-star chance per kill.");
BonusPerKill4Star = plugin.Config.Bind<float>("Progression", "BonusPerKill4Star", 0.01f, "Bonus % added to 4-star chance per kill.");
BonusPerKill5Star = plugin.Config.Bind<float>("Progression", "BonusPerKill5Star", 0.005f, "Bonus % added to 5-star chance per kill.");
MaxBonus3Star = plugin.Config.Bind<float>("Progression", "MaxBonus3Star", 20f, "Maximum total bonus % for 3-star chance.");
MaxBonus4Star = plugin.Config.Bind<float>("Progression", "MaxBonus4Star", 18f, "Maximum total bonus % for 4-star chance.");
MaxBonus5Star = plugin.Config.Bind<float>("Progression", "MaxBonus5Star", 9.5f, "Maximum total bonus % for 5-star chance.");
VanillaMinSpawnRate = plugin.Config.Bind<float>("Balance", "VanillaMinSpawnRate", 30f, "Minimum % of spawns that must remain vanilla (Level 1-3). If total custom chances exceed the remaining space, they will be scaled down proportionally. Set to 100 to disable custom spawns. Set to 0 to disable vanilla spawns");
ExcludedBiomes = plugin.Config.Bind<string>("General", "ExcludedBiomes", "Mistlands,Ashlands", "Comma-separated list of biomes where the mod is inactive.");
string text = "Deathsquito,Blob,BlobElite,BlobTar,FrostBlob,LavaBlob,Serpent,BonemawSerpent,Vulture,Skugg";
ExcludedPrefabsConfig = plugin.Config.Bind<string>("General", "ExcludedPrefabs", text, "Comma-separated list of additional prefabs to exclude (Bosses and pets are excluded by default).");
LootBonusBlacklist = plugin.Config.Bind<string>("Loot", "BonusBlacklist", "Coin,Stone,Resin,Wood", "Comma-separated list of item prefab names that should NEVER drop as an extra bonus item. Trophy is also excluded.");
ParseConfig();
}
public void ParseConfig()
{
excludedPrefabsSet.Clear();
string[] internalBlacklist = InternalBlacklist;
foreach (string item in internalBlacklist)
{
excludedPrefabsSet.Add(item);
}
foreach (string item2 in from p in ExcludedPrefabsConfig.Value.Split(new char[1] { ',' })
select p.Trim() into p
where !string.IsNullOrEmpty(p)
select p)
{
excludedPrefabsSet.Add(item2);
}
lootBlacklistSet.Clear();
foreach (string item3 in from p in LootBonusBlacklist.Value.Split(new char[1] { ',' })
select p.Trim().ToLower() into p
where !string.IsNullOrEmpty(p)
select p)
{
lootBlacklistSet.Add(item3);
}
}
public bool IsPrefabExcluded(string prefabName)
{
if (string.IsNullOrEmpty(prefabName))
{
return false;
}
if (prefabName.StartsWith("Fish"))
{
return true;
}
return excludedPrefabsSet.Contains(prefabName);
}
public bool IsItemBlacklisted(string itemName)
{
if (string.IsNullOrEmpty(itemName))
{
return false;
}
string lowerName = itemName.ToLower();
return lootBlacklistSet.Any((string b) => lowerName.Contains(b));
}
public bool IsBiomeExcluded(Biome currentBiome)
{
string biomeStr = ((object)(Biome)(ref currentBiome)).ToString();
return ExcludedBiomes.Value.Split(new char[1] { ',' }).Any((string b) => b.Trim().Equals(biomeStr, StringComparison.OrdinalIgnoreCase));
}
}
}
namespace KillCountEvolution.Patches
{
[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
public static class DropPatch
{
private static bool Prefix(CharacterDrop __instance, ref List<KeyValuePair<GameObject, int>> __result)
{
Character component = ((Component)__instance).GetComponent<Character>();
if ((Object)(object)component == (Object)null)
{
return true;
}
int level = component.GetLevel();
if (level < 4)
{
return true;
}
MobSpawnConfig config = KillCountEvolution.Instance.mobConfig;
List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>();
foreach (Drop drop in __instance.m_drops)
{
if ((Object)(object)drop.m_prefab == (Object)null || 1 == 0)
{
continue;
}
int num = Random.Range(drop.m_amountMin, drop.m_amountMax + 1);
if (num <= 0)
{
continue;
}
if (((Object)drop.m_prefab).name.ToLower().Contains("trophy"))
{
if (Random.value <= drop.m_chance)
{
list.Add(new KeyValuePair<GameObject, int>(drop.m_prefab, num));
}
}
else
{
list.Add(new KeyValuePair<GameObject, int>(drop.m_prefab, num * 4));
}
}
int num2 = level switch
{
6 => 2,
5 => 1,
_ => 0,
};
if (num2 > 0)
{
List<GameObject> list2 = (from d in __instance.m_drops
where (Object)(object)d.m_prefab != (Object)null
select d.m_prefab into p
where !((Object)p).name.ToLower().Contains("trophy") && !config.IsItemBlacklisted(((Object)p).name)
select p).ToList();
if (list2.Count > 0)
{
GameObject key = list2[Random.Range(0, list2.Count)];
list.Add(new KeyValuePair<GameObject, int>(key, num2));
}
}
__result = list;
return false;
}
}
}