Decompiled source of ValheimQuest v1.0.2

Valheimquest.dll

Decompiled 4 days ago
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 HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Valheimquest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Valheimquest")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("25bf4d3e-bb53-4126-8370-18af0311e6c7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ValheimQuest;

[BepInPlugin("com.yourname.valheimquest", "Valheim Quest", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class ValheimQuest : BaseUnityPlugin
{
	private readonly Harmony harmony = new Harmony("com.yourname.valheimquest");

	private void Awake()
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Valheim Quest mod loaded!");
		harmony.PatchAll();
		PrefabManager.OnVanillaPrefabsAvailable += RegisterAll;
	}

	private void RegisterAll()
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Registering quest objects...");
		QuestGiver.Register();
		PrefabManager.OnVanillaPrefabsAvailable -= RegisterAll;
	}
}
public class QuestGiver
{
	public static void Register()
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Expected O, but got Unknown
		GameObject val = PrefabManager.Instance.CreateClonedPrefab("QuestGiverRaven", "darkwood_raven");
		if ((Object)(object)val == (Object)null)
		{
			Debug.LogError((object)"Failed to clone raven prefab!");
			return;
		}
		val.AddComponent<QuestGiverInteract>();
		PieceConfig val2 = new PieceConfig();
		val2.Name = "Raven Oracle";
		val2.Description = "An ancient raven carving that speaks of quests.";
		val2.PieceTable = "Hammer";
		val2.AddRequirement(new RequirementConfig("FineWood", 5, 0, true));
		val2.AddRequirement(new RequirementConfig("GreydwarfEye", 2, 0, true));
		PieceManager.Instance.AddPiece(new CustomPiece(val, false, val2));
		Debug.Log((object)"Quest Giver registered!");
	}
}
public class QuestGiverInteract : MonoBehaviour, Interactable, Hoverable
{
	private enum QuestState
	{
		NoQuest,
		Quest1Active,
		Quest1Complete,
		Quest2Active,
		Quest2Complete,
		Quest3Active,
		Quest3Complete,
		Quest4Active,
		Quest4Complete,
		Quest5Active,
		Quest5Complete,
		Quest6Active,
		Quest6Complete,
		Quest7Active,
		Quest7Complete,
		Quest8Active,
		Quest8Complete,
		Quest9Active,
		Quest9Complete,
		Quest10Active,
		Quest10Complete,
		Quest11Active,
		Quest11Complete,
		Quest12Active,
		Quest12Complete,
		Quest13Active,
		Quest13Complete,
		Quest14Active,
		Quest14Complete,
		Quest15Active,
		Quest15Complete,
		Quest16Active,
		Quest16Complete,
		Quest17Active,
		Quest17Complete,
		Quest18Active,
		Quest18Complete,
		AllComplete
	}

	private struct QuestData
	{
		public string enemy;

		public int count;

		public string startMessage;

		public string progressMessage;

		public string completeMessage;
	}

	private static QuestState state;

	private static int currentQuest;

	private QuestData[] quests = new QuestData[18]
	{
		new QuestData
		{
			enemy = "Boar",
			count = 5,
			startMessage = "Slay 5 boars that plague these lands.",
			progressMessage = "boars",
			completeMessage = "The boars are dealt with. Take these supplies!"
		},
		new QuestData
		{
			enemy = "Greydwarf",
			count = 10,
			startMessage = "Now slay 10 Greydwarfs lurking in the forest.",
			progressMessage = "Greydwarfs",
			completeMessage = "The forest is safer. Here are your Surtling Cores!"
		},
		new QuestData
		{
			enemy = "Troll",
			count = 2,
			startMessage = "Prove your strength. Slay 2 Trolls.",
			progressMessage = "Trolls",
			completeMessage = "Impressive! Take this bronze and gold."
		},
		new QuestData
		{
			enemy = "Draugr",
			count = 10,
			startMessage = "The dead walk. Slay 10 Draugr.",
			progressMessage = "Draugr",
			completeMessage = "The dead rest now. Take your reward."
		},
		new QuestData
		{
			enemy = "Blob",
			count = 3,
			startMessage = "Slay 3 Slimes from the swamp.",
			progressMessage = "Slimes",
			completeMessage = "The swamp is cleaner. Here is your Guck."
		},
		new QuestData
		{
			enemy = "Wraith",
			count = 3,
			startMessage = "Face the darkness. Slay 3 Wraiths.",
			progressMessage = "Wraiths",
			completeMessage = "The spirits are vanquished. Take these chains."
		},
		new QuestData
		{
			enemy = "Wolf",
			count = 10,
			startMessage = "The mountains are dangerous. Slay 10 Wolves.",
			progressMessage = "Wolves",
			completeMessage = "The mountains are safer. Take this iron."
		},
		new QuestData
		{
			enemy = "Hatchling",
			count = 5,
			startMessage = "Slay 5 Drakes from the mountain peaks.",
			progressMessage = "Drakes",
			completeMessage = "The skies are clear. Take this trophy and gold."
		},
		new QuestData
		{
			enemy = "Fenring",
			count = 1,
			startMessage = "Face the beast of the night. Slay 1 Fenring.",
			progressMessage = "Fenrings",
			completeMessage = "You are truly brave. Take this crystal and gold."
		},
		new QuestData
		{
			enemy = "Goblin",
			count = 20,
			startMessage = "The plains are overrun. Slay 20 Fulings.",
			progressMessage = "Fulings",
			completeMessage = "The plains breathe again. Take this silver and gold."
		},
		new QuestData
		{
			enemy = "Deathsquito",
			count = 10,
			startMessage = "Slay 10 Deathsquitos from the plains.",
			progressMessage = "Deathsquitos",
			completeMessage = "No more buzzing. Take this Lox pelt and gold."
		},
		new QuestData
		{
			enemy = "Lox",
			count = 5,
			startMessage = "Slay 5 Lox from the plains.",
			progressMessage = "Lox",
			completeMessage = "Mighty warrior! Take this gold."
		},
		new QuestData
		{
			enemy = "Tick",
			count = 10,
			startMessage = "Slay 10 Ticks from the Mistlands.",
			progressMessage = "Ticks",
			completeMessage = "The Mistlands are safer. Take these Black Cores."
		},
		new QuestData
		{
			enemy = "Seeker",
			count = 10,
			startMessage = "Slay 10 Seekers in the Mistlands.",
			progressMessage = "Seekers",
			completeMessage = "The seekers fall. Take these Magecaps and gold."
		},
		new QuestData
		{
			enemy = "Gjall",
			count = 1,
			startMessage = "Face the great Gjall. Slay 1 of them.",
			progressMessage = "Gjalls",
			completeMessage = "Legendary! Take this gold and sap."
		},
		new QuestData
		{
			enemy = "Charred_Melee",
			count = 10,
			startMessage = "Slay 10 Charred Warriors in the Ashlands.",
			progressMessage = "Charred Warriors",
			completeMessage = "The ash settles. Take your reward."
		},
		new QuestData
		{
			enemy = "Charred_Twitcher",
			count = 10,
			startMessage = "Slay 10 Charred Twitchers.",
			progressMessage = "Charred Twitchers",
			completeMessage = "They twitch no more. Take your reward."
		},
		new QuestData
		{
			enemy = "Charred_Archer",
			count = 10,
			startMessage = "Slay 10 Charred Marksmen.",
			progressMessage = "Charred Marksmen",
			completeMessage = "Their arrows fly no more. Take your final reward!"
		}
	};

	public static void LoadState(int savedState, int savedQuest)
	{
		state = (QuestState)savedState;
		currentQuest = savedQuest;
		if (IsActiveState())
		{
			QuestGiverInteract questGiverInteract = Object.FindFirstObjectByType<QuestGiverInteract>();
			if ((Object)(object)questGiverInteract != (Object)null)
			{
				QuestData questData = questGiverInteract.quests[currentQuest];
				QuestTracker.StartTracking(questData.enemy, questData.count);
			}
		}
	}

	public static int GetStateAsInt()
	{
		return (int)state;
	}

	public static int GetCurrentQuest()
	{
		return currentQuest;
	}

	public bool Interact(Humanoid user, bool hold, bool alt)
	{
		if (hold)
		{
			return false;
		}
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		if (state == QuestState.AllComplete)
		{
			MessageHud.instance.ShowMessage((MessageType)2, "Raven Oracle: You have completed all my quests. You are a legend!", 0, (Sprite)null, false);
			return true;
		}
		if (state == QuestState.NoQuest || IsCompleteState())
		{
			if (IsCompleteState() && currentQuest < quests.Length - 1)
			{
				currentQuest++;
			}
			else if (IsCompleteState() && currentQuest >= quests.Length - 1)
			{
				state = QuestState.AllComplete;
				QuestSaveData.Save(GetStateAsInt(), QuestTracker.GetKillCount(), currentQuest);
				return true;
			}
			QuestData questData = quests[currentQuest];
			MessageHud.instance.ShowMessage((MessageType)2, "Raven Oracle: " + questData.startMessage, 0, (Sprite)null, false);
			QuestTracker.StartTracking(questData.enemy, questData.count);
			SetActiveState();
			QuestSaveData.Save(GetStateAsInt(), 0, currentQuest);
		}
		else if (IsActiveState())
		{
			QuestData questData2 = quests[currentQuest];
			int killCount = QuestTracker.GetKillCount();
			if (killCount >= questData2.count)
			{
				MessageHud.instance.ShowMessage((MessageType)2, "Raven Oracle: " + questData2.completeMessage, 0, (Sprite)null, false);
				GiveReward(val, currentQuest);
				SetCompleteState();
				QuestSaveData.Save(GetStateAsInt(), killCount, currentQuest);
			}
			else
			{
				MessageHud.instance.ShowMessage((MessageType)2, $"Raven Oracle: You have slain {killCount}/{questData2.count} {questData2.progressMessage}. Keep going!", 0, (Sprite)null, false);
			}
		}
		return true;
	}

	private static bool IsActiveState()
	{
		return (int)state % 2 == 1;
	}

	private static bool IsCompleteState()
	{
		return state != 0 && (int)state % 2 == 0 && state != QuestState.AllComplete;
	}

	private void SetActiveState()
	{
		state = (QuestState)(currentQuest * 2 + 1);
	}

	private void SetCompleteState()
	{
		state = (QuestState)(currentQuest * 2 + 2);
	}

	private void GiveReward(Player player, int questIndex)
	{
		switch (questIndex)
		{
		case 0:
			((Humanoid)player).GetInventory().AddItem("CookedMeat", 10, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Bread", 5, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 20, 1, 0, 0L, "", false);
			break;
		case 1:
			((Humanoid)player).GetInventory().AddItem("SurtlingCore", 5, 1, 0, 0L, "", false);
			break;
		case 2:
			((Humanoid)player).GetInventory().AddItem("Bronze", 10, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 200, 1, 0, 0L, "", false);
			break;
		case 3:
			((Humanoid)player).GetInventory().AddItem("SurtlingCore", 5, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 100, 1, 0, 0L, "", false);
			break;
		case 4:
			((Humanoid)player).GetInventory().AddItem("Guck", 10, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 60, 1, 0, 0L, "", false);
			break;
		case 5:
			((Humanoid)player).GetInventory().AddItem("Chain", 5, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 150, 1, 0, 0L, "", false);
			break;
		case 6:
			((Humanoid)player).GetInventory().AddItem("Iron", 20, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 100, 1, 0, 0L, "", false);
			break;
		case 7:
			((Humanoid)player).GetInventory().AddItem("TrophyHatchling", 1, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 50, 1, 0, 0L, "", false);
			break;
		case 8:
			((Humanoid)player).GetInventory().AddItem("Coins", 100, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Crystal", 20, 1, 0, 0L, "", false);
			break;
		case 9:
			((Humanoid)player).GetInventory().AddItem("Silver", 20, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 200, 1, 0, 0L, "", false);
			break;
		case 10:
			((Humanoid)player).GetInventory().AddItem("LoxPelt", 10, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Coins", 50, 1, 0, 0L, "", false);
			break;
		case 11:
			((Humanoid)player).GetInventory().AddItem("Coins", 300, 1, 0, 0L, "", false);
			break;
		case 12:
			((Humanoid)player).GetInventory().AddItem("Coins", 100, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("BlackCore", 3, 1, 0, 0L, "", false);
			break;
		case 13:
			((Humanoid)player).GetInventory().AddItem("Coins", 200, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Magecap", 10, 1, 0, 0L, "", false);
			break;
		case 14:
			((Humanoid)player).GetInventory().AddItem("Coins", 200, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("Sap", 20, 1, 0, 0L, "", false);
			break;
		case 15:
			((Humanoid)player).GetInventory().AddItem("Coins", 200, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("BlackCore", 3, 1, 0, 0L, "", false);
			break;
		case 16:
			((Humanoid)player).GetInventory().AddItem("Coins", 300, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("BlackCore", 3, 1, 0, 0L, "", false);
			break;
		case 17:
			((Humanoid)player).GetInventory().AddItem("Coins", 400, 1, 0, 0L, "", false);
			((Humanoid)player).GetInventory().AddItem("MoltenCore", 5, 1, 0, 0L, "", false);
			break;
		}
	}

	public bool UseItem(Humanoid user, ItemData item)
	{
		return false;
	}

	public string GetHoverText()
	{
		if (state == QuestState.NoQuest || IsCompleteState())
		{
			return "Raven Oracle\n[E] Speak";
		}
		if (IsActiveState())
		{
			QuestData questData = quests[currentQuest];
			return $"Raven Oracle\n[E] Check Progress ({QuestTracker.GetKillCount()}/{questData.count} {questData.progressMessage})";
		}
		return "Raven Oracle\n[E] Speak";
	}

	public string GetHoverName()
	{
		return "Raven Oracle";
	}
}
public class QuestSaveData
{
	private const string QuestStateKey = "ValheimQuest_State";

	private const string KillCountKey = "ValheimQuest_KillCount";

	private const string CurrentQuestKey = "ValheimQuest_CurrentQuest";

	public static void Save(int questState, int killCount, int currentQuest)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null))
		{
			localPlayer.m_customData["ValheimQuest_State"] = questState.ToString();
			localPlayer.m_customData["ValheimQuest_KillCount"] = killCount.ToString();
			localPlayer.m_customData["ValheimQuest_CurrentQuest"] = currentQuest.ToString();
			Debug.Log((object)$"Quest progress saved! State: {questState} Kills: {killCount} Quest: {currentQuest}");
		}
	}

	public static bool Load(out int questState, out int killCount, out int currentQuest)
	{
		questState = 0;
		killCount = 0;
		currentQuest = 0;
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return false;
		}
		if (!localPlayer.m_customData.ContainsKey("ValheimQuest_State"))
		{
			return false;
		}
		questState = int.Parse(localPlayer.m_customData["ValheimQuest_State"]);
		killCount = int.Parse(localPlayer.m_customData["ValheimQuest_KillCount"]);
		currentQuest = int.Parse(localPlayer.m_customData["ValheimQuest_CurrentQuest"]);
		Debug.Log((object)$"Quest progress loaded! State: {questState} Kills: {killCount} Quest: {currentQuest}");
		return true;
	}
}
[HarmonyPatch(typeof(Player), "OnSpawned")]
public class PlayerSpawnPatch
{
	private static void Postfix(Player __instance)
	{
		if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && QuestSaveData.Load(out var questState, out var killCount, out var currentQuest))
		{
			QuestTracker.LoadState(killCount);
			QuestGiverInteract.LoadState(questState, currentQuest);
			Debug.Log((object)"Quest state restored!");
		}
	}
}
public class QuestTracker
{
	private static int killCount = 0;

	private static bool isTracking = false;

	private static string targetEnemy = "";

	private static int killsRequired = 0;

	private static HashSet<int> countedKills = new HashSet<int>();

	public static void StartTracking(string enemyName, int required)
	{
		killCount = 0;
		isTracking = true;
		targetEnemy = enemyName;
		killsRequired = required;
		countedKills.Clear();
		Debug.Log((object)("Quest started! Tracking " + enemyName + " kills..."));
	}

	public static void LoadState(int savedKillCount)
	{
		killCount = savedKillCount;
		isTracking = true;
		Debug.Log((object)$"Kill count loaded: {killCount}");
	}

	public static int GetKillCount()
	{
		return killCount;
	}

	public static void AddKill(int instanceId, string enemyName)
	{
		if (isTracking && enemyName.Contains(targetEnemy) && !countedKills.Contains(instanceId))
		{
			countedKills.Add(instanceId);
			killCount++;
			Debug.Log((object)$"{targetEnemy} killed! Total: {killCount}/{killsRequired}");
			MessageHud.instance.ShowMessage((MessageType)1, $"{targetEnemy}s slain: {killCount}/{killsRequired}", 0, (Sprite)null, false);
			QuestSaveData.Save(QuestGiverInteract.GetStateAsInt(), killCount, QuestGiverInteract.GetCurrentQuest());
		}
	}
}
[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
public class EnemyDeathPatch
{
	private static void Postfix(CharacterDrop __instance)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)Player.m_localPlayer == (Object)null))
		{
			float num = Vector3.Distance(((Component)__instance).transform.position, ((Component)Player.m_localPlayer).transform.position);
			if (num < 30f)
			{
				QuestTracker.AddKill(((Object)((Component)__instance).gameObject).GetInstanceID(), ((Object)((Component)__instance).gameObject).name);
			}
		}
	}
}