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(); 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(); 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 countedKills = new HashSet(); 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); } } } }