Decompiled source of KillCountEvolution v1.0.0

KillCountEvolution.dll

Decompiled 20 hours ago
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;
		}
	}
}