Decompiled source of Razaotium Client Side v1.0.2

RazaotiumClientSide.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Utils;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Razaotium.ClientSide;

[SynchronizationMode(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("razaotium.valheim.client_side", "Razaotium Client Side", "1.0.2")]
public class RazaotiumClientSidePlugin : BaseUnityPlugin
{
	private enum Toggle
	{
		Off,
		On
	}

	[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
	private static class CharacterDropGenerateDropListPatch
	{
		private static bool Prefix(CharacterDrop __instance, ref List<KeyValuePair<GameObject, int>> __result)
		{
			if (ShouldBlock(__instance, out var prefab, out var requiredTier, out var playerTier))
			{
				__result = new List<KeyValuePair<GameObject, int>>();
				if (_logBlockedDrops.Value)
				{
					((BaseUnityPlugin)_instance).Logger.LogInfo((object)$"Blocked drops from {prefab}: creature tier {requiredTier}, player tier {playerTier}.");
				}
				return false;
			}
			return true;
		}
	}

	public const string PluginGuid = "razaotium.valheim.client_side";

	public const string PluginName = "Razaotium Client Side";

	public const string PluginVersion = "1.0.2";

	private const string DefaultCreatureTiers = "Greydwarf=1,Greydwarf_Elite=1,Greydwarf_Shaman=1,Troll=1,Ghost=1,Skeleton=1,Skeleton_Poison=1,RancidRemains=1,Blob=2,BlobElite=2,Draugr=2,Draugr_Elite=2,Leech=2,Surtling=2,Wraith=2,Abomination=2,Wolf=3,Fenring=3,Fenring_Cultist=3,Ulv=3,Bat=3,Hatchling=3,StoneGolem=3,Deathsquito=4,Goblin=4,GoblinArcher=4,GoblinBrute=4,GoblinShaman=4,Lox=4,Growth=4,Seeker=5,SeekerBrute=5,SeekerBrood=5,Gjall=5,Tick=5,Dverger=5,DvergerMage=5,DvergerMageFire=5,DvergerMageIce=5,DvergerMageSupport=5,DvergerRogue=5,Charred_Archer=6,Charred_Melee=6,Charred_Mage=6,Charred_Twitcher=6,Morgen=6,FallenValkyrie=6,Volture=6,Asksvin=6,BlobLava=6,BonemawSerpent=6,gd_king=2,Bonemass=3,Dragon=4,GoblinKing=5,SeekerQueen=6,Fader=6";

	private static ConfigEntry<Toggle> _lockConfiguration;

	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<bool> _useKillerProgress;

	private static ConfigEntry<float> _closestPlayerFallbackRange;

	private static ConfigEntry<bool> _allowUnknownCreatures;

	private static ConfigEntry<bool> _includeBossDrops;

	private static ConfigEntry<bool> _logBlockedDrops;

	private static ConfigEntry<string> _tierKeys;

	private static ConfigEntry<string> _creatureTiers;

	private static readonly FieldInfo LastHitField = AccessTools.Field(typeof(Character), "m_lastHit");

	private static readonly Dictionary<string, int> ParsedCreatureTiers = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<int, string> ParsedTierKeys = new Dictionary<int, string>();

	private static RazaotiumClientSidePlugin _instance;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Expected O, but got Unknown
		_instance = this;
		_lockConfiguration = BindSynced("General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
		_enabled = BindSynced("General", "Enabled", defaultValue: true, "Enable biome-based drop blocking.");
		_useKillerProgress = BindSynced("General", "UseKillerProgress", defaultValue: true, "Use the player who landed the killing hit. If false, use the closest player.");
		_closestPlayerFallbackRange = BindSynced("General", "ClosestPlayerFallbackRange", 80f, "Range used to find a nearby player when the killer is not a player or cannot be found.");
		_allowUnknownCreatures = BindSynced("General", "AllowUnknownCreatures", defaultValue: true, "If true, creatures not listed in CreatureTiers keep their drops.");
		_includeBossDrops = BindSynced("General", "IncludeBossDrops", defaultValue: false, "If true, listed boss prefabs are also blocked by progression.");
		_logBlockedDrops = BindSynced("General", "LogBlockedDrops", defaultValue: true, "Log when a creature drop is blocked.");
		_tierKeys = BindSynced("Progression", "TierKeys", "1=defeated_eikthyr,2=defeated_gdking,3=defeated_bonemass,4=defeated_dragon,5=defeated_goblinking,6=defeated_queen", "Tier unlock keys. Tier 0 is always unlocked. Defaults: 1 Black Forest, 2 Swamp, 3 Mountains, 4 Plains, 5 Mistlands, 6 Ashlands.");
		_creatureTiers = BindSynced("Progression", "CreatureTiers", "Greydwarf=1,Greydwarf_Elite=1,Greydwarf_Shaman=1,Troll=1,Ghost=1,Skeleton=1,Skeleton_Poison=1,RancidRemains=1,Blob=2,BlobElite=2,Draugr=2,Draugr_Elite=2,Leech=2,Surtling=2,Wraith=2,Abomination=2,Wolf=3,Fenring=3,Fenring_Cultist=3,Ulv=3,Bat=3,Hatchling=3,StoneGolem=3,Deathsquito=4,Goblin=4,GoblinArcher=4,GoblinBrute=4,GoblinShaman=4,Lox=4,Growth=4,Seeker=5,SeekerBrute=5,SeekerBrood=5,Gjall=5,Tick=5,Dverger=5,DvergerMage=5,DvergerMageFire=5,DvergerMageIce=5,DvergerMageSupport=5,DvergerRogue=5,Charred_Archer=6,Charred_Melee=6,Charred_Mage=6,Charred_Twitcher=6,Morgen=6,FallenValkyrie=6,Volture=6,Asksvin=6,BlobLava=6,BonemawSerpent=6,gd_king=2,Bonemass=3,Dragon=4,GoblinKing=5,SeekerQueen=6,Fader=6", "Creature prefab to tier mapping. Format: Prefab=Tier,Prefab=Tier. Creatures whose tier is above the player's unlocked tier drop nothing.");
		ReloadConfig();
		((BaseUnityPlugin)this).Config.SettingChanged += delegate
		{
			ReloadConfig();
		};
		_harmony = new Harmony("razaotium.valheim.client_side");
		_harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded with {2} configured creatures.", "Razaotium Client Side", "1.0.2", ParsedCreatureTiers.Count));
	}

	private ConfigEntry<T> BindSynced<T>(string section, string key, T defaultValue, string description)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		ConfigFile config = ((BaseUnityPlugin)this).Config;
		string text = description + " [Synced with Server]";
		object[] array = new object[1];
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes();
		val.IsAdminOnly = true;
		array[0] = val;
		return config.Bind<T>(section, key, defaultValue, new ConfigDescription(text, (AcceptableValueBase)null, array));
	}

	private void OnDestroy()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}

	private static void ReloadConfig()
	{
		ParsedTierKeys.Clear();
		int result;
		foreach (KeyValuePair<string, string> item in SplitPairs(_tierKeys.Value))
		{
			if (int.TryParse(item.Key, out result) && !string.IsNullOrWhiteSpace(item.Value))
			{
				ParsedTierKeys[result] = item.Value.Trim();
			}
		}
		ParsedCreatureTiers.Clear();
		foreach (KeyValuePair<string, string> item2 in SplitPairs(_creatureTiers.Value))
		{
			if (int.TryParse(item2.Value, out result) && !string.IsNullOrWhiteSpace(item2.Key))
			{
				ParsedCreatureTiers[item2.Key.Trim()] = result;
			}
		}
	}

	private static IEnumerable<KeyValuePair<string, string>> SplitPairs(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			yield break;
		}
		try
		{
			string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string raw in array)
			{
				string[] parts = raw.Split(new char[1] { '=' }, 2);
				if (parts.Length == 2)
				{
					yield return new KeyValuePair<string, string>(parts[0].Trim(), parts[1].Trim());
				}
			}
		}
		finally
		{
		}
	}

	private static bool ShouldBlock(CharacterDrop dropper, out string prefab, out int requiredTier, out int playerTier)
	{
		prefab = GetPrefabName(dropper);
		requiredTier = 0;
		playerTier = 0;
		if (!_enabled.Value || string.IsNullOrWhiteSpace(prefab))
		{
			return false;
		}
		if (!ParsedCreatureTiers.TryGetValue(prefab, out requiredTier))
		{
			return !_allowUnknownCreatures.Value;
		}
		if (!_includeBossDrops.Value && IsBossPrefab(prefab))
		{
			return false;
		}
		Player player = FindProgressPlayer(dropper);
		playerTier = GetUnlockedTier(player);
		return requiredTier > playerTier;
	}

	private static Player FindProgressPlayer(CharacterDrop dropper)
	{
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		Character component = ((Component)dropper).GetComponent<Character>();
		if (_useKillerProgress.Value && (Object)(object)component != (Object)null && LastHitField != null)
		{
			object? value = LastHitField.GetValue(component);
			HitData val = (HitData)((value is HitData) ? value : null);
			Character val2 = ((val != null && val.HaveAttacker()) ? val.GetAttacker() : null);
			Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
			if ((Object)(object)val3 != (Object)null)
			{
				return val3;
			}
		}
		return Player.GetClosestPlayer(((Component)dropper).transform.position, _closestPlayerFallbackRange.Value);
	}

	private static int GetUnlockedTier(Player player)
	{
		int num = 0;
		foreach (KeyValuePair<int, string> item in ParsedTierKeys.OrderBy((KeyValuePair<int, string> entry) => entry.Key))
		{
			if (HasKey(player, item.Value))
			{
				num = Math.Max(num, item.Key);
			}
		}
		return num;
	}

	private static bool HasKey(Player player, string key)
	{
		if (string.IsNullOrWhiteSpace(key))
		{
			return true;
		}
		if ((Object)(object)player != (Object)null && ((Humanoid)player).HaveUniqueKey(key))
		{
			return true;
		}
		return (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(key);
	}

	private static string GetPrefabName(CharacterDrop dropper)
	{
		string name = ((Object)((Component)dropper).gameObject).name;
		return (name == null) ? string.Empty : name.Replace("(Clone)", string.Empty).Trim();
	}

	private static bool IsBossPrefab(string prefab)
	{
		return prefab.Equals("Eikthyr", StringComparison.OrdinalIgnoreCase) || prefab.Equals("gd_king", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Bonemass", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Dragon", StringComparison.OrdinalIgnoreCase) || prefab.Equals("GoblinKing", StringComparison.OrdinalIgnoreCase) || prefab.Equals("SeekerQueen", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Fader", StringComparison.OrdinalIgnoreCase);
	}
}