Decompiled source of WeenieHutJrDifficultyManager v1.0.0

WeenieHutJrDifficultyManager.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("WeenieHutJrDifficultyManager")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+58202811ff20d819581d750e23d60c517d78c78d")]
[assembly: AssemblyProduct("WeenieHutJrDifficultyManager")]
[assembly: AssemblyTitle("WeenieHutJrDifficultyManager")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WeenieHutJrDifficultyManager
{
	public enum DeathMode
	{
		Vanilla,
		WeenieHutJr,
		SuperWeenieHutJr
	}
	internal static class DeathPenalty
	{
		public const int HotbarSlots = 8;

		public static bool Handles(Player player)
		{
			if (Plugin.Enabled.Value && Plugin.Mode.Value != DeathMode.Vanilla && (Object)(object)player != (Object)null)
			{
				return (Object)(object)player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		public static void OnDeath(Player player)
		{
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			bool flag = player.m_timeSinceDeath > player.m_hardDeathCooldown;
			Plugin.Log.LogInfo((object)string.Format("Death: {0} ({1:0} s of play since the last death, {2:0} s needed); skills {3}", flag ? "hard" : "soft", player.m_timeSinceDeath, player.m_hardDeathCooldown, flag ? "are checked" : "are not touched by the game, so no skill is lowered"));
			Inventory inventory = ((Humanoid)player).m_inventory;
			if (inventory == null || inventory.NrOfItems() == 0)
			{
				return;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)23))
			{
				Plugin.Log.LogInfo((object)"Death: the world keeps every item (no item loss), so nothing goes to a grave");
				return;
			}
			DeathMode value = Plugin.Mode.Value;
			if (value == DeathMode.SuperWeenieHutJr)
			{
				Plugin.Log.LogInfo((object)$"Death: {value}, {inventory.NrOfItems()} item(s) kept, no grave");
				return;
			}
			List<ItemData> list = new List<ItemData>();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				string text = KeepReason(allItem);
				if (text == null)
				{
					list.Add(allItem);
					Plugin.Log.LogDebug((object)$"Death: {allItem.m_shared.m_name} x{allItem.m_stack} at ({allItem.m_gridPos.x}, {allItem.m_gridPos.y}) goes to the grave");
				}
				else
				{
					num++;
					dictionary.TryGetValue(text, out var value2);
					dictionary[text] = value2 + 1;
					Plugin.Log.LogDebug((object)$"Death: {allItem.m_shared.m_name} x{allItem.m_stack} at ({allItem.m_gridPos.x}, {allItem.m_gridPos.y}) kept: {text}");
				}
			}
			if (list.Count == 0)
			{
				Plugin.Log.LogInfo((object)$"Death: {value}, {num} item(s) kept ({Describe(dictionary)}), nothing for a grave");
				return;
			}
			foreach (ItemData item in list)
			{
				if (item.m_equipped)
				{
					((Humanoid)player).UnequipItem(item, false);
				}
			}
			GameObject obj = Object.Instantiate<GameObject>(player.m_tombstone, ((Character)player).GetCenterPoint(), ((Component)player).transform.rotation);
			MoveToGrave(obj.GetComponent<Container>().GetInventory(), inventory, list);
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			obj.GetComponent<TombStone>().Setup(playerProfile.GetName(), playerProfile.GetPlayerID());
			Plugin.Log.LogInfo((object)$"Death: {value}, {num} item(s) kept ({Describe(dictionary)}), {list.Count} to the grave");
		}

		internal static string KeepReason(ItemData item)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (item.m_shared.m_questItem)
			{
				return "quest item";
			}
			if (Plugin.KeepEquipped.Value && item.m_equipped)
			{
				return "equipped";
			}
			Vector2i gridPos = item.m_gridPos;
			if (gridPos.y == 0 && gridPos.x >= 0 && gridPos.x < 8)
			{
				if (!Plugin.KeepSlots[gridPos.x].Value)
				{
					return null;
				}
				return "slot " + (gridPos.x + 1);
			}
			switch (DraggableUILink.ExtraSlotKindAt(gridPos.x, gridPos.y))
			{
			case "Arrow":
				if (!Plugin.KeepArrows.Value)
				{
					return null;
				}
				return "arrow slot";
			case "Consumable":
				if (!Plugin.KeepConsumables.Value)
				{
					return null;
				}
				return "consumable slot";
			case "Food":
				if (!Plugin.KeepFood.Value)
				{
					return null;
				}
				return "food slot";
			default:
				return null;
			}
		}

		private static void MoveToGrave(Inventory grave, Inventory from, List<ItemData> items)
		{
			grave.m_inventory.Clear();
			grave.m_width = from.m_width;
			grave.m_height = from.m_height;
			foreach (ItemData item in items)
			{
				grave.m_inventory.Add(item);
				from.m_inventory.Remove(item);
			}
			from.Changed(false, false);
			grave.Changed(false, false);
		}

		private static string Describe(Dictionary<string, int> counts)
		{
			if (counts.Count == 0)
			{
				return "none";
			}
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, int> count in counts)
			{
				list.Add(count.Key + " " + count.Value);
			}
			return string.Join(", ", list.ToArray());
		}
	}
	internal static class DraggableUILink
	{
		public const string DraggableUIGuid = "rhenn.draggableui";

		private const string ApiTypeName = "DraggableUI.DraggableApi";

		private static bool resolved;

		private static MethodInfo extraSlotKindAt;

		public static string ExtraSlotKindAt(int x, int y)
		{
			if (!Resolve())
			{
				return null;
			}
			try
			{
				return (string)extraSlotKindAt.Invoke(null, new object[2] { x, y });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)$"DraggableUI refused ExtraSlotKindAt({x}, {y}): {ex.Message}");
				return null;
			}
		}

		private static bool Resolve()
		{
			if (resolved)
			{
				return extraSlotKindAt != null;
			}
			resolved = true;
			if (!Chainloader.PluginInfos.TryGetValue("rhenn.draggableui", out var value) || (Object)(object)value.Instance == (Object)null)
			{
				Plugin.Log.LogInfo((object)"DraggableUI is not installed: the Arrows, Consumables and Food settings match nothing");
				return false;
			}
			try
			{
				Type type = ((object)value.Instance).GetType().Assembly.GetType("DraggableUI.DraggableApi");
				if (type != null)
				{
					extraSlotKindAt = type.GetMethod("ExtraSlotKindAt", BindingFlags.Static | BindingFlags.Public);
				}
				if (extraSlotKindAt == null)
				{
					Plugin.Log.LogWarning((object)$"DraggableUI {value.Metadata.Version} has no ExtraSlotKindAt (added in 1.1.1): the Arrows, Consumables and Food settings match nothing until it is updated");
					return false;
				}
				Plugin.Log.LogDebug((object)$"DraggableUI {value.Metadata.Version} found; its Extra slots are asked about on death");
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("DraggableUI could not be reached: " + ex.Message));
				extraSlotKindAt = null;
				return false;
			}
		}
	}
	[BepInPlugin("rhenn.weeniehutjrdifficultymanager", "WeenieHutJrDifficultyManager", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "rhenn.weeniehutjrdifficultymanager";

		public const string PluginName = "WeenieHutJrDifficultyManager";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<DeathMode> Mode;

		internal static ConfigEntry<bool> KeepEquipped;

		internal static ConfigEntry<bool>[] KeepSlots;

		internal static ConfigEntry<bool> KeepArrows;

		internal static ConfigEntry<bool> KeepConsumables;

		internal static ConfigEntry<bool> KeepFood;

		internal static ConfigEntry<bool> KeepAllSkills;

		internal static ConfigEntry<bool> KeepCombatSkills;

		internal static ConfigEntry<bool> KeepLifeSkills;

		internal static ConfigEntry<bool> KeepMovementSkills;

		internal static ConfigEntry<bool> KeepModdedSkills;

		private readonly Harmony harmony = new Harmony("rhenn.weeniehutjrdifficultymanager");

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Set to false and the game's own death penalty applies, whatever the settings below say.");
			Mode = ((BaseUnityPlugin)this).Config.Bind<DeathMode>("General", "DeathMode", DeathMode.WeenieHutJr, "What happens to your inventory when you die. Vanilla: the world's own death penalty setting decides, as without this mod. WeenieHutJr: everything ticked in the KeepOnDeath section stays with you, the rest goes to your grave. Named for the player who asked for it, like the Super mode. SuperWeenieHutJr: nothing goes to a grave, you keep every item, the grid too.");
			KeepEquipped = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepOnDeath", "Equipped", true, "Keep what you are wearing and holding: armour, cape, belt, trinket, the weapon and shield in your hands and the arrows loaded in your bow. This is what the game's Casual preset keeps.");
			KeepSlots = new ConfigEntry<bool>[8];
			for (int i = 0; i < KeepSlots.Length; i++)
			{
				int num = i + 1;
				KeepSlots[i] = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepOnDeath", "Slot" + num, true, $"Keep whatever sits in hotbar slot {num} (the top row of the inventory, key {num}), equipped or not.");
			}
			KeepArrows = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepOnDeath", "Arrows", true, "Keep the arrows and bolts in DraggableUI's three Arrow slots. Needs DraggableUI 1.1.1 or later with its Extra slots on; does nothing otherwise.");
			KeepConsumables = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepOnDeath", "Consumables", true, "Keep the mead and potions in DraggableUI's three Consumable slots. Needs DraggableUI 1.1.1 or later with its Extra slots on; does nothing otherwise.");
			KeepFood = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepOnDeath", "Food", true, "Keep the food in DraggableUI's three Food slots. Needs DraggableUI 1.1.1 or later with its Extra slots on; does nothing otherwise.");
			KeepAllSkills = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepSkillsOnDeath", "All", false, "Keep every skill on death: no skill is ever lowered, whatever the group and skill switches say. (Independent of DeathMode, which is about items.)");
			KeepCombatSkills = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepSkillsOnDeath", "Combat", false, "Keep the Combat skills: Swords, Knives, Clubs, Polearms, Spears, Axes, Unarmed, Bows, Crossbows, Elemental magic, Blood magic, Blocking, Dodge. On keeps them all; off lets each skill's own switch decide.");
			KeepLifeSkills = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepSkillsOnDeath", "Life", false, "Keep the Life skills: Pickaxes, Wood cutting, Fishing, Cooking, Farming, Crafting, Riding. On keeps them all; off lets each skill's own switch decide.");
			KeepMovementSkills = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepSkillsOnDeath", "Movement", false, "Keep the Movement skills: Jump, Sneak, Run, Swim. On keeps them all; off lets each skill's own switch decide.");
			KeepModdedSkills = ((BaseUnityPlugin)this).Config.Bind<bool>("KeepSkillsOnDeath", "ModdedSkills", false, "Keep skills added by other mods, which this mod cannot list by name. On keeps them all.");
			SkillLoss.Bind(((BaseUnityPlugin)this).Config);
			harmony.PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)string.Format("{0} {1} loaded. Enabled = {2}, DeathMode = {3}", "WeenieHutJrDifficultyManager", "1.0.0", Enabled.Value, Mode.Value));
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}
	}
	public enum SkillGroup
	{
		Combat,
		Life,
		Movement,
		Other
	}
	internal static class SkillLoss
	{
		public static readonly (SkillType Type, SkillGroup Group)[] GameSkills = new(SkillType, SkillGroup)[24]
		{
			((SkillType)1, SkillGroup.Combat),
			((SkillType)2, SkillGroup.Combat),
			((SkillType)3, SkillGroup.Combat),
			((SkillType)4, SkillGroup.Combat),
			((SkillType)5, SkillGroup.Combat),
			((SkillType)7, SkillGroup.Combat),
			((SkillType)11, SkillGroup.Combat),
			((SkillType)8, SkillGroup.Combat),
			((SkillType)14, SkillGroup.Combat),
			((SkillType)9, SkillGroup.Combat),
			((SkillType)10, SkillGroup.Combat),
			((SkillType)6, SkillGroup.Combat),
			((SkillType)108, SkillGroup.Combat),
			((SkillType)12, SkillGroup.Life),
			((SkillType)13, SkillGroup.Life),
			((SkillType)104, SkillGroup.Life),
			((SkillType)105, SkillGroup.Life),
			((SkillType)106, SkillGroup.Life),
			((SkillType)107, SkillGroup.Life),
			((SkillType)110, SkillGroup.Life),
			((SkillType)100, SkillGroup.Movement),
			((SkillType)101, SkillGroup.Movement),
			((SkillType)102, SkillGroup.Movement),
			((SkillType)103, SkillGroup.Movement)
		};

		private static readonly Dictionary<SkillType, SkillGroup> groupOf = new Dictionary<SkillType, SkillGroup>();

		private static readonly Dictionary<SkillType, ConfigEntry<bool>> keepSkill = new Dictionary<SkillType, ConfigEntry<bool>>();

		public unsafe static void Bind(ConfigFile config)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			(SkillType, SkillGroup)[] gameSkills = GameSkills;
			for (int i = 0; i < gameSkills.Length; i++)
			{
				var (val, skillGroup) = gameSkills[i];
				groupOf[val] = skillGroup;
				keepSkill[val] = config.Bind<bool>("KeepSkillsPerSkill", ((object)(*(SkillType*)(&val))/*cast due to .constrained prefix*/).ToString(), false, $"Keep {Words(val)} on death. A {skillGroup} skill: with the {skillGroup} switch on it is kept whatever this says.");
			}
		}

		public static SkillGroup GroupOf(SkillType type)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (!groupOf.TryGetValue(type, out var value))
			{
				return SkillGroup.Other;
			}
			return value;
		}

		public static bool Lowers(SkillType type)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.KeepAllSkills.Value || GroupKept(GroupOf(type)))
			{
				return false;
			}
			if (keepSkill.TryGetValue(type, out var value))
			{
				return !value.Value;
			}
			return true;
		}

		private static bool GroupKept(SkillGroup group)
		{
			return group switch
			{
				SkillGroup.Combat => Plugin.KeepCombatSkills.Value, 
				SkillGroup.Life => Plugin.KeepLifeSkills.Value, 
				SkillGroup.Movement => Plugin.KeepMovementSkills.Value, 
				_ => Plugin.KeepModdedSkills.Value, 
			};
		}

		public static bool Handles(Skills skills)
		{
			if (Plugin.Enabled.Value && (Object)(object)skills != (Object)null && (Object)(object)skills.m_player != (Object)null)
			{
				return (Object)(object)skills.m_player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		public static void OnDeath(Skills skills)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			float num = skills.m_DeathLowerFactor * Game.m_skillReductionRate;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			foreach (KeyValuePair<SkillType, Skill> skillDatum in skills.m_skillData)
			{
				Skill value = skillDatum.Value;
				if (Lowers(skillDatum.Key))
				{
					float level = value.m_level;
					value.m_level -= value.m_level * num;
					value.m_accumulator = 0f;
					list.Add($"{skillDatum.Key} {level:0.#}>{value.m_level:0.#}");
				}
				else
				{
					list2.Add(((object)skillDatum.Key/*cast due to .constrained prefix*/).ToString());
				}
			}
			if (list.Count > 0)
			{
				((Character)skills.m_player).Message((MessageType)1, "$msg_skills_lowered", 0, (Sprite)null, false);
			}
			Plugin.Log.LogInfo((object)$"Death: {list.Count} skill(s) lowered by {num:P0} ({Join(list)}), {list2.Count} kept ({Join(list2)})");
		}

		private static string Join(List<string> parts)
		{
			if (parts.Count != 0)
			{
				return string.Join(", ", parts);
			}
			return "none";
		}

		private unsafe static string Words(SkillType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			switch (type - 9)
			{
			default:
				if ((int)type != 110)
				{
					break;
				}
				return "Riding";
			case 0:
				return "Elemental magic";
			case 1:
				return "Blood magic";
			case 4:
				return "Wood cutting";
			case 2:
			case 3:
				break;
			}
			return ((object)(*(SkillType*)(&type))/*cast due to .constrained prefix*/).ToString();
		}
	}
}
namespace WeenieHutJrDifficultyManager.Patches
{
	[HarmonyPatch(typeof(Player), "CreateTombStone")]
	internal static class PlayerCreateTombStonePatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix(Player __instance)
		{
			if (!DeathPenalty.Handles(__instance))
			{
				return true;
			}
			try
			{
				DeathPenalty.OnDeath(__instance);
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Death handling failed, the game's own death penalty runs instead: {arg}");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Skills), "OnDeath")]
	internal static class SkillsOnDeathPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool Prefix(Skills __instance)
		{
			if (!SkillLoss.Handles(__instance))
			{
				return true;
			}
			try
			{
				SkillLoss.OnDeath(__instance);
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Skill loss handling failed, the game's own skill loss runs instead: {arg}");
				return true;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}