Decompiled source of HaldorBounties v0.0.1
HaldorBounties.dll
Decompiled 10 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HaldorOverhaul; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 HaldorBounties { public class BountyEntry { public string Id = ""; public string Title = ""; public string Description = ""; public string Type = "Kill"; public string Target = ""; public int Amount = 1; public int Reward = 50; public string RequiredBoss = ""; public int SpawnLevel; public string Tier = "Easy"; public int Gender; } internal class ConfigWrapper { public int Version; public List<BountyEntry> Bounties; } public static class BountyConfig { private struct KillDef { public string Id; public string Title; public string Desc; public string Target; public int Amount; public int Reward; } private struct MinibossDef { public string Id; public string Title; public string Desc; public int Reward; public int Gender; } private struct RaidDef { public string Id; public string Title; public string Desc; public int Amount; public int Reward; } private const int CurrentConfigVersion = 2; private static string _configPath; public static List<BountyEntry> Bounties { get; private set; } = new List<BountyEntry>(); public static void Initialize(string configPath) { _configPath = configPath; if (File.Exists(_configPath)) { try { ConfigWrapper configWrapper = JsonConvert.DeserializeObject<ConfigWrapper>(File.ReadAllText(_configPath)); if (configWrapper != null && configWrapper.Version >= 2 && configWrapper.Bounties != null && configWrapper.Bounties.Count > 0) { Bounties = configWrapper.Bounties; ValidateEntries(); HaldorBounties.Log.LogInfo((object)$"[BountyConfig] Loaded {Bounties.Count} bounties (v{configWrapper.Version})."); return; } int num = configWrapper?.Version ?? 0; HaldorBounties.Log.LogInfo((object)$"[BountyConfig] Config version {num} < {2}, regenerating."); } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyConfig] Failed to load config: " + ex.Message + ". Using defaults.")); } } Bounties = BuildDefaultBounties(); ValidateEntries(); SaveToFile(); HaldorBounties.Log.LogInfo((object)$"[BountyConfig] Generated {Bounties.Count} default bounties (v{2})."); } private static void ValidateEntries() { foreach (BountyEntry bounty in Bounties) { if ((bounty.Tier == "Miniboss" || bounty.Tier == "Raid") && bounty.SpawnLevel < 1) { HaldorBounties.Log.LogWarning((object)$"[BountyConfig] Entry '{bounty.Id}' is {bounty.Tier} but SpawnLevel={bounty.SpawnLevel}, fixing to 1."); bounty.SpawnLevel = 1; } } } private static void SaveToFile() { try { string contents = JsonConvert.SerializeObject((object)new ConfigWrapper { Version = 2, Bounties = Bounties }, (Formatting)1); string directoryName = Path.GetDirectoryName(_configPath); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(_configPath, contents); } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyConfig] Failed to save config: " + ex.Message)); } } private static List<BountyEntry> BuildDefaultBounties() { List<BountyEntry> list = new List<BountyEntry>(); AddKillBounties(list, "", "Easy", K("m_greyling_1", "Skogvættr's Brood", "The grey ones crawl from the earth like maggots from a wound. Haldor's caravans cannot pass.", "Greyling", 5, 25), K("m_greyling_2", "Scourge of the Glade", "Greylings swarm the meadow in numbers unseen. The roads must be cleared by steel.", "Greyling", 12, 50), K("m_neck_1", "Serpents of the Shallows", "Necks snap at wading travelers along every shore. Make the riverbanks safe again.", "Neck", 5, 30), K("m_neck_2", "Fang-Water", "Every stream and pond teems with Necks grown bold on easy prey.", "Neck", 12, 60), K("m_boar_1", "Tusks of the Wildwood", "Feral boars gore anyone who strays from the paths. Cull them before they breed further.", "Boar", 8, 40), K("m_boar_2", "Razorback Rampage", "The boars have gone berserk — tusks bloody, eyes wild. Something in the meadow drives them mad.", "Boar", 15, 75), K("m_deer_1", "Eikthyr's Offering", "Haldor craves fresh venison for his stores. Track the swiftest deer and bring them down.", "Deer", 5, 30), K("m_deer_2", "The Great Hunt", "A proper hunt worthy of song — enough deer to feast a warband.", "Deer", 12, 60), K("m_greyling_3", "Grey Tide Rising", "An enormous colony festers beneath the meadow roots. Burn them out.", "Greyling", 20, 80), K("m_neck_3", "Jaws of the Runoff", "The Neck swarms have grown beyond all reckoning. The waterways belong to them now — take them back.", "Neck", 20, 90)); AddMinibossBounties(list, "", "HB_BountyNpc_T1", MB("mb_m_m1", "The Oath-Breaker", "A disgraced warrior haunts the crossroads, challenging travelers to holmgang they cannot refuse.", 200, 1), MB("mb_m_f1", "Grelka the Scavenger", "A scarred deserter raids supply caches by moonlight, leaving only bones and ash.", 220, 2), MB("mb_m_m2", "Bjolf Stone-Fist", "A hulking brute blocks the meadow crossing, demanding tribute in blood.", 210, 1), MB("mb_m_f2", "Runa Thorn-Heart", "An exile who fights like the wild creatures she lives among — without mercy.", 230, 2), MB("mb_m_m3", "The Nithing", "An outlaw branded with the mark of shame. He has nothing left to lose.", 200, 1), MB("mb_m_f3", "Ylva Grass-Viper", "She strikes from the tall grass and vanishes before the blood dries.", 240, 2), MB("mb_m_m4", "Vagn the Wanderer", "A drifting sellsword who cuts down anyone foolish enough to draw steel.", 210, 1), MB("mb_m_f4", "Sigrid Dawn-Stalker", "She hunts at first light when travelers are bleary-eyed and careless.", 250, 2), MB("mb_m_m5", "Keld Flint-Fang", "Armed with crude but wicked weapons shaped from the meadow's stones.", 200, 1), MB("mb_m_f5", "Inga Way-Finder", "A former merchant who knows every road — and the best places to ambush them.", 230, 2)); AddRaidBounties(list, "", "HB_BountyNpc_T1", RD("rd_m_1", "Oath-Breakers' Warband", "A pack of disgraced warriors roam the meadow seeking plunder and redemption through blood.", 3, 350), RD("rd_m_2", "Outlaws of the Glade", "Desperate exiles have sworn a blood-oath and banded together. They answer to no jarl.", 4, 500), RD("rd_m_3", "Road-Wolves", "Bandits who prey on lone travelers, picking the roads clean like carrion birds.", 3, 400), RD("rd_m_4", "Camp-Burners", "Firebrands who torch camps and steal every scrap. Smoke marks their passing.", 4, 550), RD("rd_m_5", "Meadow Reavers", "A gang of marauders terrorizes the open fields, emboldened by their easy prey.", 3, 380), RD("rd_m_6", "The Landless", "Exiled warriors stripped of name and land, fighting with the fury of those who have nothing.", 4, 480), RD("rd_m_7", "Grey-Herders", "Outlaws who drive Greylings before them as scouts and distractions.", 3, 420), RD("rd_m_8", "Crossroad Kinsmen", "They control every crossroad in the meadows, demanding toll in steel.", 4, 520), RD("rd_m_9", "Dusk-Prowlers", "They strike at twilight when the shadows grow long and eyes grow dim.", 3, 400), RD("rd_m_10", "The Hunger-Sworn", "Starving raiders who have sworn to take what they need or die fighting.", 4, 500)); AddKillBounties(list, "defeated_eikthyr", "Easy", K("bf_greydwarf_1", "Eyes in the Undergrowth", "Greydwarf scouts watch every road through the forest with hollow, hateful eyes.", "Greydwarf", 8, 50), K("bf_greydwarf_2", "Root and Ruin", "A Greydwarf horde gathers beneath the ancient roots. If left unchecked, the forest will be lost.", "Greydwarf", 20, 120), K("bf_brute_1", "The Trunk-Breakers", "Greydwarf Brutes have blockaded the crossings, smashing carts and travelers alike.", "Greydwarf_Elite", 3, 90), K("bf_brute_2", "Timber Crushers", "Packs of Brutes lumber through the forest, toppling trees and crushing all who flee.", "Greydwarf_Elite", 6, 160), K("bf_shaman_1", "Menders of the Dark", "Shamans knit wounds with foul sap-magic, making the hordes near-unkillable.", "Greydwarf_Shaman", 3, 80), K("bf_shaman_2", "Curse-Weavers", "Their poison magic sustains entire warbands. Cut down the shamans and the rest will follow.", "Greydwarf_Shaman", 6, 140), K("bf_skeleton_1", "The Restless Dead", "Skeletons wander far beyond their burial mounds, rattling through the darkened woods.", "Skeleton", 8, 60), K("bf_skeleton_2", "Crypt-Purge", "The burial chambers overflow with the risen dead. Someone must seal them back in.", "Skeleton", 15, 110), K("bf_troll_1", "Bridge-Breaker", "A Troll has claimed a vital forest bridge. Travelers detour for half a day to avoid it.", "Troll", 1, 90), K("bf_ghost_1", "The Barrow-Haunts", "Spirits of the ancient dead drift through the old burial sites, hungering for warmth.", "Ghost", 5, 120)); AddMinibossBounties(list, "defeated_eikthyr", "HB_BountyNpc_T2", MB("mb_bf_m1", "Grimjaw the Brigand", "A cunning forest brigand who preys on miners hauling ore from the deep woods.", 500, 1), MB("mb_bf_f1", "Hild Iron-Maiden", "A shieldmaiden turned bandit queen. She takes what she wants and burns the rest.", 550, 2), MB("mb_bf_m2", "Torolf Deepwood-Warden", "He demands blood-tribute from anyone who sets foot in his claimed territory.", 580, 1), MB("mb_bf_f2", "Bodil Root-Witch", "Clad in living root-armor, she fights with a ferocity that is not entirely human.", 600, 2), MB("mb_bf_m3", "Ketill Copper-King", "A miner who found it easier to kill for ore than to dig for it.", 520, 1), MB("mb_bf_f3", "Alva Shadow-Fern", "She vanishes into the undergrowth after every kill. No tracker has found her camp.", 560, 2), MB("mb_bf_m4", "Ulf Troll-Tamer", "He fights alongside trained Greydwarves and knows every hollow in the forest.", 620, 1), MB("mb_bf_f4", "Svala Dark-Bark", "Covered in bark and moss, she has become one with the black forest.", 640, 2), MB("mb_bf_m5", "Hreidar Stone-Breaker", "A massive warrior who shatters shields with a single blow of his bronze mace.", 580, 1), MB("mb_bf_f5", "Jorunn Night-Shade", "She tips her blades in poison brewed from forest fungi. Death comes slowly.", 650, 2)); AddRaidBounties(list, "defeated_eikthyr", "HB_BountyNpc_T2", RD("rd_bf_1", "The Iron Ambush", "Brigands with iron weapons lie in wait along the forest road, hungry for blood and bronze.", 3, 750), RD("rd_bf_2", "Root-Clad War Party", "A war party in root armor has fortified a ruin in the heart of the forest.", 4, 1000), RD("rd_bf_3", "Troll-Hide Gang", "Outlaws clad in troll leather who strike without warning and vanish into the trees.", 3, 850), RD("rd_bf_4", "Crypt-Plunderers", "Grave-robbers who kill any witness. They've grown rich — and dangerous.", 4, 950), RD("rd_bf_5", "Forest Kinslayers", "Reavers who know every path through the black wood and use them to deadly effect.", 3, 780), RD("rd_bf_6", "The Bronze Brotherhood", "Deserters bound by oath and bronze, they answer challenges with axe and shield.", 4, 900), RD("rd_bf_7", "Timber-Wolves", "They ambush loggers and steal their haul. The forest roads are theirs.", 3, 800), RD("rd_bf_8", "Flame-Sworn Cult", "Outlaws who worship the Surtling flame and carry cores as holy relics.", 4, 1050), RD("rd_bf_9", "The Undergrowth", "They hide in the brush and strike from below. You'll never see the blade that kills you.", 3, 820), RD("rd_bf_10", "Darkwood Huskarls", "A warband of hardened killers who take no prisoners and leave no survivors.", 4, 1100)); AddKillBounties(list, "defeated_gdking", "Medium", K("sw_draugr_1", "The Walking Dead", "Draugr shuffle near the borders, testing the living with dead hands and rusty steel.", "Draugr", 8, 120), K("sw_draugr_2", "Dead Tide", "The swamp crawls with Draugr rising in numbers not seen since the old wars.", "Draugr", 18, 250), K("sw_elite_1", "Grave-Knights", "Draugr Elites clad in ancient iron lead raiding parties out of the sunken crypts.", "Draugr_Elite", 3, 180), K("sw_elite_2", "Wight Purge", "Draugr Elites have risen in alarming numbers. The dead outnumber the living.", "Draugr_Elite", 6, 320), K("sw_blob_1", "Bile-Cleansing", "Poisonous Blobs ooze through the bog, leaving trails of rot that kill the very earth.", "Blob", 8, 120), K("sw_wraith_1", "Fog-Wraiths", "Wraiths materialize from the mire-fog, their wailing a death-knell for the unwary.", "Wraith", 3, 220), K("sw_leech_1", "Blood-Drinkers", "Giant Leeches infest every body of stagnant water. Wading is a death sentence.", "Leech", 10, 130), K("sw_surtling_1", "Geyser-Flames", "Surtlings blaze near the fire geysers, scorching anyone who seeks their cores.", "Surtling", 8, 130), K("sw_abom_1", "Root-Horror", "An Abomination has torn itself free of the muck. The ground shakes with its fury.", "Abomination", 1, 180), K("sw_abom_2", "The Great Uprooting", "Multiple Abominations tear through the swamp. The land itself has turned hostile.", "Abomination", 3, 420)); AddMinibossBounties(list, "defeated_gdking", "HB_BountyNpc_T3", MB("mb_sw_m1", "Halvard Bog-Stalker", "Born in the mire, he fights with ruthless cunning. The Draugr fear his iron.", 1000, 1), MB("mb_sw_f1", "Groa the Drowned Queen", "Even the walking dead give this one a wide berth. She rules the sunken places.", 1100, 2), MB("mb_sw_m2", "Vidar Crypt-Breaker", "His silver blade drips with undead ichor — he has slain many, and will slay you next.", 1200, 1), MB("mb_sw_f2", "Thora Mire-Witch", "She commands the swamp-fog itself as a weapon, blinding her prey before the kill.", 1150, 2), MB("mb_sw_m3", "Eindride Rot-Walker", "He wades through poison without flinching, armored in leather cured with grave-wax.", 1050, 1), MB("mb_sw_f3", "Hervor Iron-Widow", "She lost every shield-brother to the swamp. Now she seeks vengeance on all who live.", 1250, 2), MB("mb_sw_m4", "Gorm the Undertaker", "He buries his victims in the mud where they rise again to serve him.", 1100, 1), MB("mb_sw_f4", "Solveig Mire-Singer", "Her voice carries through the fog, luring travelers into the drowning-deeps.", 1300, 2), MB("mb_sw_m5", "Bjarke Leech-Lord", "The bloodsuckers seem to obey his whispered commands. He is more swamp than man.", 1150, 1), MB("mb_sw_f5", "Vigdis Guck-Weaver", "She crafts traps from swamp-filth that would make a Draugr recoil.", 1200, 2)); AddRaidBounties(list, "defeated_gdking", "HB_BountyNpc_T3", RD("rd_sw_1", "Silver Marauders", "Silver-clad raiders stalk the mist-shrouded bogs, hunting for worthy prey.", 3, 1500), RD("rd_sw_2", "The Drowned Company", "Battle-hardened and half-mad from the swamp. They fight as if already dead.", 4, 2000), RD("rd_sw_3", "Mire-Reavers", "They use the fog like a war-cloak, striking from the murk and melting away.", 3, 1800), RD("rd_sw_4", "Crypt-Raiders", "Tomb raiders armed with grave-plunder. They kill any witness to their sacrilege.", 4, 1900), RD("rd_sw_5", "The Rotting Hand", "Warriors who have embraced the swamp's decay and wear it like battle-paint.", 3, 1600), RD("rd_sw_6", "Bog-Runners", "Fast and lethal, they vault across the muck while their prey sinks and drowns.", 4, 2100), RD("rd_sw_7", "The Iron Tide", "An iron-clad warband pushing through the swamp, crushing all in their path.", 3, 1700), RD("rd_sw_8", "The Sunken Ones", "They emerge from black water where no man should survive. No one knows how.", 4, 2200), RD("rd_sw_9", "Wraith-Pact Warriors", "Warriors who have sworn oaths to the swamp spirits. They do not die easily.", 3, 1650), RD("rd_sw_10", "Mire-Wolves", "A wolfpack of fighters who hunt in formation through the sucking bog.", 4, 2300)); AddKillBounties(list, "defeated_bonemass", "Medium", K("mt_wolf_1", "Fenrir's Get", "A wolf pack descends on climbers with fangs bared and frost on their breath.", "Wolf", 5, 150), K("mt_wolf_2", "Blood on the Snow", "The mountain is overrun — wolves howl from every ridge and ravine.", "Wolf", 15, 350), K("mt_drake_1", "Ice-Wyrm Hunters", "Frost Drakes rain shards of ice on anyone below. The skies are not safe.", "Hatchling", 5, 190), K("mt_drake_2", "Storm of Wings", "The skies are thick with Drakes. They must be thinned before passage is possible.", "Hatchling", 10, 340), K("mt_golem_1", "Stone-Breaker", "A Stone Golem blocks the mountain pass, grinding boulders in its fists.", "StoneGolem", 1, 200), K("mt_golem_2", "Walking Avalanche", "Multiple Golems descend the slopes, crushing everything in their grinding path.", "StoneGolem", 3, 420), K("mt_fenring_1", "Moon-Hunters", "Fenrings emerge under the cold stars, their howls echoing off the peaks.", "Fenring", 3, 340), K("mt_ulv_1", "Ghost-Wolves", "Ulvs haunt the mountain caves, spectral and savage in equal measure.", "Ulv", 5, 280), K("mt_bat_1", "The Shrieking Dark", "Swarms of bats pour from mountain caves, blotting out the light.", "Bat", 10, 150), K("mt_wolf_3", "Howling Peaks", "Wolf packs grow dangerously large on the heights. The mountain belongs to them.", "Wolf", 10, 260)); AddMinibossBounties(list, "defeated_bonemass", "HB_BountyNpc_T4", MB("mb_mt_m1", "Skallagrim Frost-Bitten", "His silver weapons have claimed lives beyond counting. The cold has made him merciless.", 2000, 1), MB("mb_mt_f1", "Astrid Summit-Huntress", "She commands the wolves of the peaks and hunts alongside them.", 2200, 2), MB("mb_mt_m2", "Thorvald Blizzard-Born", "He walks through storms that would bury other men, blade in hand.", 2100, 1), MB("mb_mt_f2", "Brynhild the Avalanche", "She has crushed more challengers than any golem on the mountain.", 2400, 2), MB("mb_mt_m3", "Egil Frost-Warden", "Self-proclaimed guardian of the high passes. He lets no one through alive.", 2050, 1), MB("mb_mt_f3", "Eira Ice-Whisper", "She moves silently through the snow. Her victims never hear the killing blow.", 2300, 2), MB("mb_mt_m4", "Hallbjorn Peak-Breaker", "He breaks through shield-walls with the force of a mountain gale.", 2500, 1), MB("mb_mt_f4", "Dagny Crystal-Edge", "Her crystal-tipped weapons cut through armor as if it were cloth.", 2600, 2), MB("mb_mt_m5", "Arne the Cold One", "He feels nothing — not frost, not pain, not mercy.", 2150, 1), MB("mb_mt_f5", "Sif Storm-Caller", "She fights hardest when the blizzard howls, as if the storm answers her will.", 2350, 2)); AddRaidBounties(list, "defeated_bonemass", "HB_BountyNpc_T4", RD("rd_mt_1", "Frost-Reavers", "Black metal glints in the cold air. These warriors have slain dragons for their steel.", 3, 3000), RD("rd_mt_2", "Summit Warhost", "They hold the highest peaks and challenge any fool who dares ascend.", 4, 4200), RD("rd_mt_3", "Blizzard Fangs", "They attack only during storms, invisible until the killing blow.", 3, 3500), RD("rd_mt_4", "The Summit-Guard", "Elite warriors sworn to hold the peaks against all comers. They have never broken.", 4, 4000), RD("rd_mt_5", "Silver Brotherhood", "Bound by silver and blood-oaths, they fight as one terrible weapon.", 3, 3200), RD("rd_mt_6", "Ulfhednar", "Berserkers who don wolf-skins and fight with the frenzy of Fenrir's children.", 4, 4500), RD("rd_mt_7", "The Frozen Ones", "Warriors who endure any cold, any wound. They do not stop.", 3, 3300), RD("rd_mt_8", "Crystal-Guard", "Armed with obsidian-edged weapons that sing in the frozen air.", 4, 4100), RD("rd_mt_9", "Hrimthursar's Kin", "Frost-touched warriors as unyielding as the mountain itself.", 3, 3400), RD("rd_mt_10", "The High King's Hird", "The finest warriors of the peaks, forged in ice and sworn to death.", 4, 4800)); AddKillBounties(list, "defeated_dragon", "Hard", K("pl_fuling_1", "Goblin Outriders", "Fuling scouts probe the plains' edges, their crude spears gleaming with malice.", "Goblin", 8, 250), K("pl_fuling_2", "The Great Horde", "A Fuling invasion force marches across the golden fields. The earth trembles.", "Goblin", 25, 600), K("pl_brute_1", "Skull-Crushers", "Fuling Berserkers crush armor like parchment and bone like kindling.", "GoblinBrute", 2, 380), K("pl_brute_2", "The Wrecking Tide", "Multiple Berserkers on a rampage — nothing stands before them.", "GoblinBrute", 4, 650), K("pl_shaman_1", "Flame-Snuffers", "Fuling Shamans channel dark fire-magic that chars flesh and warps iron.", "GoblinShaman", 3, 300), K("pl_squito_1", "Needle-Storm", "Deathsquitos punch through armor with barbed stingers. Death by a thousand needles.", "Deathsquito", 10, 270), K("pl_squito_2", "Plague of Needles", "An overwhelming swarm descends. The buzzing alone drives warriors to madness.", "Deathsquito", 20, 500), K("pl_lox_1", "Beast-Feller", "Lox trample everything beneath their thundering hooves. Even the Fulings flee.", "Lox", 2, 320), K("pl_lox_2", "Thunder Hooves", "A herd of enraged Lox stampedes across the plains, crushing all in their wake.", "Lox", 5, 620), K("pl_tar_1", "Tar-Blight", "Tar Growths spread black corruption from bubbling pits. The land itself sickens.", "BlobTar", 5, 320)); AddMinibossBounties(list, "defeated_dragon", "HB_BountyNpc_T5", MB("mb_pl_m1", "Ragnar Golden-Bane", "Carapace armor gleams on his shoulders. Even Fuling war-chiefs flee at his approach.", 3200, 1), MB("mb_pl_f1", "Freydis the Conqueress", "Her blade has carved a path through villages and war-camps alike.", 3600, 2), MB("mb_pl_m2", "Styrbjorn Dusk-Reaper", "He strikes at twilight when the golden light blinds. None have seen his face and lived.", 3800, 1), MB("mb_pl_f2", "Gudrun Shield-Maiden", "She fights as if Odin himself watches — because she believes he does.", 4000, 2), MB("mb_pl_m3", "Hakon Sand-Viper", "He strikes from the tall barley like a serpent, silent and lethal.", 3400, 1), MB("mb_pl_f3", "Alfhild Barley-Queen", "She controls the farmlands through fear and fire. The harvest is hers.", 3700, 2), MB("mb_pl_m4", "Bjorn Lox-Breaker", "He charges on foot but hits like a rampaging beast.", 4200, 1), MB("mb_pl_f4", "Ranveig Needle-Dancer", "She weaves through combat like a Deathsquito — impossible to pin down.", 3900, 2), MB("mb_pl_m5", "Jarl Skuli the Bloody", "A self-proclaimed jarl of terrible reputation. His saga is written in blood.", 4500, 1), MB("mb_pl_f5", "Sigrun Golden-Fury", "Fury incarnate on the golden fields. She fights until the plains run red.", 4100, 2)); AddRaidBounties(list, "defeated_dragon", "HB_BountyNpc_T5", RD("rd_pl_1", "Carapace Raiders", "Clad in chitin-plate with exotic weapons and deadly coordination.", 4, 6000), RD("rd_pl_2", "The Conqueror's Hird", "The deadliest warband in the realm. Songs are sung of those brave enough to face them.", 5, 8500), RD("rd_pl_3", "Dusk-Riders", "They sweep the plains at sunset, silhouettes against the dying light.", 4, 7000), RD("rd_pl_4", "The Golden Host", "An army ablaze on the golden fields. Odin watches this battle.", 5, 9000), RD("rd_pl_5", "Fuling-Slayers", "They hunt Fulings for sport and wear their trophies as war-paint.", 4, 6500), RD("rd_pl_6", "Vanguard of the Fallen", "First into the shield-wall, last to retreat. They know no fear.", 5, 8000), RD("rd_pl_7", "Plains-Wolves", "Fast and lethal across open ground, they run down prey like a wolf pack.", 4, 7500), RD("rd_pl_8", "The Iron Harvest", "They reap what others have sown, and their scythes are sharpened steel.", 5, 9500), RD("rd_pl_9", "Blood-Drenched Veterans", "Survivors of a hundred battles. They fight without hesitation or mercy.", 4, 7200), RD("rd_pl_10", "Sand-Storm Berserkers", "They charge like wind across the plains, howling war-cries to Tyr.", 5, 10000)); AddKillBounties(list, "defeated_goblinking", "Hard", K("ml_seeker_1", "Hive-Purge", "Seekers strike from the eternal twilight, their mandibles slick with venom.", "Seeker", 8, 420), K("ml_seeker_2", "The Infestation", "Seeker hives spread through every ruin and hollow. The mist breeds them endlessly.", "Seeker", 15, 680), K("ml_brute_1", "Chitin-Cracker", "Seeker Brutes guard the deepest hives, armored in plates that turn steel.", "SeekerBrute", 2, 500), K("ml_brute_2", "Chitin Siege", "Seeker Brutes blockade all passage through the mist. Exploration has ceased.", "SeekerBrute", 4, 880), K("ml_tick_1", "Bloodtick Purge", "Ticks drop from the canopy without warning, draining the life from armored warriors.", "Tick", 10, 360), K("ml_tick_2", "Canopy Scourge", "A plague of Ticks overwhelms even the strongest. The treetops writhe with them.", "Tick", 20, 620), K("ml_gjall_1", "Sky-Terror", "A Gjall rains explosive bile from above, cratering the earth below.", "Gjall", 1, 460), K("ml_gjall_2", "Bombardment", "Multiple Gjall turn the landscape into a cratered wasteland of fire and chitin.", "Gjall", 3, 920), K("ml_dvergr_1", "Rogue Dvergr", "Some Dvergr have turned hostile, wielding arcane weapons of terrible power.", "Dverger", 5, 540), K("ml_seeker_3", "The Deep Hive", "The largest hive pulses beneath the mist. It must be destroyed before it hatches.", "Seeker", 20, 800)); AddMinibossBounties(list, "defeated_goblinking", "HB_BountyNpc_T6", MB("mb_ml_m1", "Arnbjorn Mist-Phantom", "Even the Seekers recoil from his approach. He is more wraith than warrior.", 5000, 1), MB("mb_ml_f1", "Aslaug the Ash-Born", "The most dangerous bounty ever nailed to Haldor's board. Approach with caution.", 5500, 2), MB("mb_ml_m2", "Fenrir Eitr-Walker", "His weapons hum with raw eitr. One cut festers into something worse than death.", 5800, 1), MB("mb_ml_f2", "Gunnhild Void-Walker", "She wears armor no living smith has ever forged. It moves with her like skin.", 6200, 2), MB("mb_ml_m3", "Snorri Mist-Reaver", "He strikes from the fog and vanishes, leaving only corpses and silence.", 5200, 1), MB("mb_ml_f3", "Torhild Seeker-Queen", "She moves like a Seeker but thinks like a war-chief. The worst of both worlds.", 6000, 2), MB("mb_ml_m4", "Floki the Dvergr-Touched", "Trained by the Dvergr in arts no human should know. His weapons defy nature.", 5600, 1), MB("mb_ml_f4", "Hallveig Chitin-Empress", "Carapace armor fused with eitr-magic. She is the mist made flesh.", 6500, 2), MB("mb_ml_m5", "Hrolf Shadow-Blade", "His blade drinks the light itself. You will not see the edge that ends you.", 5400, 1), MB("mb_ml_f5", "Sigrid Mist-Walker", "She has walked deeper into the mist than any living soul — and returned changed.", 6800, 2)); AddRaidBounties(list, "defeated_goblinking", "HB_BountyNpc_T6", RD("rd_ml_1", "Flametal Brotherhood", "Their weapons blaze with flametal fire. Their coordinated assaults are devastating.", 4, 8500), RD("rd_ml_2", "Ashlands Vanguard", "An army unto themselves, forged in realms beyond the mist.", 5, 12000), RD("rd_ml_3", "The Void-Pact", "They fight in eerie silence, communicating without words. Unnerving and lethal.", 4, 9500), RD("rd_ml_4", "The Last Legion", "Flametal from helm to heel. The final obstacle between you and legend.", 5, 14000), RD("rd_ml_5", "Mist-Walkers", "They emerge from the fog as one, a wall of steel and eitr-glow.", 4, 9000), RD("rd_ml_6", "Eitr-Guard", "Their weapons crackle with eitr energy that burns through any defense.", 5, 13000), RD("rd_ml_7", "Chitin-Sworn", "Warriors bound in Seeker chitin who have surrendered their humanity.", 4, 10000), RD("rd_ml_8", "The Forgotten", "No saga remembers their names. Few who face them survive to sing one.", 5, 15000), RD("rd_ml_9", "Deep Patrol", "They patrol the deepest, darkest reaches of the mist where no light penetrates.", 4, 9800), RD("rd_ml_10", "Ragnarok's Herald", "The final challenge for any Viking who would earn a seat in Valhalla.", 5, 16000)); return list; } private static KillDef K(string id, string title, string desc, string target, int amount, int reward) { KillDef result = default(KillDef); result.Id = id; result.Title = title; result.Desc = desc; result.Target = target; result.Amount = amount; result.Reward = reward; return result; } private static MinibossDef MB(string id, string title, string desc, int reward, int gender) { MinibossDef result = default(MinibossDef); result.Id = id; result.Title = title; result.Desc = desc; result.Reward = reward; result.Gender = gender; return result; } private static RaidDef RD(string id, string title, string desc, int amount, int reward) { RaidDef result = default(RaidDef); result.Id = id; result.Title = title; result.Desc = desc; result.Amount = amount; result.Reward = reward; return result; } private static void AddKillBounties(List<BountyEntry> list, string boss, string tier, params KillDef[] defs) { for (int i = 0; i < defs.Length; i++) { KillDef killDef = defs[i]; list.Add(new BountyEntry { Id = killDef.Id, Title = killDef.Title, Description = killDef.Desc, Type = "Kill", Target = killDef.Target, Amount = killDef.Amount, Reward = killDef.Reward, RequiredBoss = boss, Tier = tier }); } } private static void AddMinibossBounties(List<BountyEntry> list, string boss, string target, params MinibossDef[] defs) { for (int i = 0; i < defs.Length; i++) { MinibossDef minibossDef = defs[i]; list.Add(new BountyEntry { Id = minibossDef.Id, Title = minibossDef.Title, Description = minibossDef.Desc, Type = "Kill", Target = target, Amount = 1, Reward = minibossDef.Reward, RequiredBoss = boss, SpawnLevel = 1, Tier = "Miniboss", Gender = minibossDef.Gender }); } } private static void AddRaidBounties(List<BountyEntry> list, string boss, string target, params RaidDef[] defs) { for (int i = 0; i < defs.Length; i++) { RaidDef raidDef = defs[i]; list.Add(new BountyEntry { Id = raidDef.Id, Title = raidDef.Title, Description = raidDef.Desc, Type = "Kill", Target = target, Amount = raidDef.Amount, Reward = raidDef.Reward, RequiredBoss = boss, SpawnLevel = 1, Tier = "Raid" }); } } } [BepInPlugin("com.haldor.bounties", "Haldor Bounties", "0.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class HaldorBounties : BaseUnityPlugin { public const string PluginGUID = "com.haldor.bounties"; public const string PluginName = "Haldor Bounties"; public const string PluginVersion = "0.0.1"; private static Harmony _harmony; internal static ManualLogSource Log; private void Awake() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Haldor Bounties v0.0.1 loading..."); BountyConfig.Initialize(Path.Combine(Paths.ConfigPath, "HaldorBounties.bounties.json")); BountyManager.Initialize(); _harmony = new Harmony("com.haldor.bounties"); try { _harmony.PatchAll(Assembly.GetExecutingAssembly()); } catch (Exception arg) { Log.LogError((object)$"[HaldorBounties] Harmony PatchAll failed: {arg}"); } int num = 0; foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods()) { _ = patchedMethod; num++; } Log.LogInfo((object)string.Format("{0} loaded successfully! ({1} methods patched)", "Haldor Bounties", num)); } private void Update() { BountyManager.Instance?.UpdateBountyPins(); BountyManager.Instance?.UpdateBossHudRange(); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Haldor Bounties unloaded."); } } } public enum BountyState { Available, Active, Ready, Claimed } public class BountyManager { private const string StateKeyPrefix = "HaldorBounty_State_"; private const string ProgressKeyPrefix = "HaldorBounty_Progress_"; private const string BossNameKeyPrefix = "HaldorBounty_BossName_"; private const string SpawnPosKeyPrefix = "HaldorBounty_SpawnPos_"; private const string BankDataKey = "TraderSharedBank_Balance"; private const string BankLegacyHaldor = "HaldorBank_Balance"; private const string BankLegacyHildir = "HildirBank_Balance"; private const string BankLegacyBogWitch = "BogWitchBank_Balance"; private const string LastDayKey = "HaldorBounty_LastDay"; private MethodInfo _reloadBankBalance; private MethodInfo _getTraderUI; private int _lastDay = -1; private readonly Dictionary<string, PinData> _bountyPins = new Dictionary<string, PinData>(); private readonly Dictionary<string, Character> _bountyCreatures = new Dictionary<string, Character>(); private readonly HashSet<string> _activeBountyIds = new HashSet<string>(); private bool _activeBountyIdsLoaded; private readonly Dictionary<string, BountyEntry> _bountyLookup; private static readonly string[] MaleNames = new string[45] { "Ragnar", "Sigurd", "Gunnar", "Halvard", "Agnar", "Torstein", "Ottar", "Ivar", "Bjarke", "Torbjorn", "Thorvald", "Styrbjorn", "Ketil", "Hakon", "Erling", "Folkvar", "Geir", "Vidar", "Grimolf", "Thorgrim", "Rolf", "Orm", "Asmund", "Einar", "Hjalmar", "Dag", "Bodvar", "Ulf", "Bjorn", "Erik", "Leif", "Skallagrim", "Hrothgar", "Bragi", "Kolbein", "Trygve", "Arnbjorn", "Gudmund", "Fenrir", "Alfgeir", "Skuli", "Hrolf", "Guthorm", "Snorri", "Floki" }; private static readonly string[] FemaleNames = new string[30] { "Valdis", "Freya", "Brynhild", "Solveig", "Ingrid", "Ragnhild", "Hervor", "Svanhild", "Jorunn", "Alva", "Astrid", "Sigrid", "Thyra", "Helga", "Sif", "Gudrun", "Ylva", "Thora", "Hilde", "Embla", "Vigdis", "Eira", "Gunnhild", "Dagny", "Aslaug", "Alfhild", "Ranveig", "Sigrun", "Torhild", "Hallveig" }; private static Sprite _bountyIcon; public static BountyManager Instance { get; private set; } private BountyManager() { _bountyLookup = new Dictionary<string, BountyEntry>(BountyConfig.Bounties.Count); foreach (BountyEntry bounty in BountyConfig.Bounties) { _bountyLookup[bounty.Id] = bounty; } } public static void Initialize() { Instance = new BountyManager(); Instance.CacheReflection(); } public bool TryGetEntry(string bountyId, out BountyEntry entry) { return _bountyLookup.TryGetValue(bountyId, out entry); } private void CacheReflection() { try { Type type = Type.GetType("HaldorOverhaul.TraderUI, HaldorOverhaul"); if (type != null) { _reloadBankBalance = type.GetMethod("ReloadBankBalance", BindingFlags.Instance | BindingFlags.Public); } Type type2 = Type.GetType("HaldorOverhaul.ControlPatches, HaldorOverhaul"); if (type2 != null) { _getTraderUI = type2.GetMethod("GetTraderUI", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } if (_getTraderUI == null) { HaldorBounties.Log.LogWarning((object)"[BountyManager] GetTraderUI not found — bank balance will not refresh on coin reward."); } if (_reloadBankBalance == null) { HaldorBounties.Log.LogWarning((object)"[BountyManager] ReloadBankBalance not found — bank balance will not refresh on coin reward."); } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyManager] Reflection cache failed: " + ex.Message)); } } private void EnsureActiveBountyIdsLoaded() { if (_activeBountyIdsLoaded) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } _activeBountyIds.Clear(); foreach (KeyValuePair<string, string> customDatum in localPlayer.m_customData) { if (customDatum.Key.StartsWith("HaldorBounty_State_") && customDatum.Value == "active") { _activeBountyIds.Add(customDatum.Key.Substring("HaldorBounty_State_".Length)); } } _activeBountyIdsLoaded = true; } public int GetCurrentDay() { if ((Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return 0; } return EnvMan.instance.GetDay(ZNet.instance.GetTimeSeconds()); } public void CheckDayReset() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } int currentDay = GetCurrentDay(); if (currentDay <= 0) { return; } if (_lastDay < 0) { if (localPlayer.m_customData.TryGetValue("HaldorBounty_LastDay", out var value) && int.TryParse(value, out var result)) { _lastDay = result; } else { _lastDay = currentDay; } } if (currentDay == _lastDay) { return; } List<string> list = new List<string>(); foreach (KeyValuePair<string, string> customDatum in localPlayer.m_customData) { if (customDatum.Key.StartsWith("HaldorBounty_State_") && customDatum.Value == "claimed") { list.Add(customDatum.Key); } } foreach (string item in list) { localPlayer.m_customData.Remove(item); } foreach (string item2 in list) { string text = item2.Substring("HaldorBounty_State_".Length); localPlayer.m_customData.Remove("HaldorBounty_Progress_" + text); localPlayer.m_customData.Remove("HaldorBounty_BossName_" + text); localPlayer.m_customData.Remove("HaldorBounty_SpawnPos_" + text); } _lastDay = currentDay; localPlayer.m_customData["HaldorBounty_LastDay"] = currentDay.ToString(); _activeBountyIdsLoaded = false; EnsureActiveBountyIdsLoaded(); HaldorBounties.Log.LogInfo((object)$"[BountyManager] New day {currentDay} — reset {list.Count} claimed bounties."); } public List<BountyEntry> GetDailyBounties() { Random rng = new Random((int)((long)GetCurrentDay() * 31337L % int.MaxValue)); List<BountyEntry> source = BountyConfig.Bounties.Where((BountyEntry b) => IsBossGateUnlocked(b)).ToList(); List<BountyEntry> pool = source.Where((BountyEntry b) => b.Type == "Kill" && !IsMiniboss(b) && !IsRaid(b)).ToList(); List<BountyEntry> pool2 = source.Where((BountyEntry b) => IsMiniboss(b)).ToList(); List<BountyEntry> pool3 = source.Where((BountyEntry b) => IsRaid(b)).ToList(); List<BountyEntry> list = new List<BountyEntry>(); list.AddRange(PickRandom(pool, 2, rng)); list.AddRange(PickRandom(pool2, 1, rng)); list.AddRange(PickRandom(pool3, 1, rng)); return list; } private static List<BountyEntry> PickRandom(List<BountyEntry> pool, int count, Random rng) { if (pool.Count <= count) { return new List<BountyEntry>(pool); } return pool.OrderBy((BountyEntry _) => rng.Next()).Take(count).ToList(); } private static bool IsMiniboss(BountyEntry entry) { if (!(entry.Tier == "Miniboss")) { return entry.Tier == "Special"; } return true; } private static bool IsRaid(BountyEntry entry) { return entry.Tier == "Raid"; } private static bool IsBossGateUnlocked(BountyEntry entry) { if (string.IsNullOrEmpty(entry.RequiredBoss)) { return true; } if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } return ZoneSystem.instance.GetGlobalKey(entry.RequiredBoss); } public BountyState GetState(string bountyId) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return BountyState.Available; } if (!localPlayer.m_customData.TryGetValue("HaldorBounty_State_" + bountyId, out var value)) { return BountyState.Available; } if (value == "claimed") { return BountyState.Claimed; } if (value == "active") { if (_bountyLookup.TryGetValue(bountyId, out var value2) && GetProgress(bountyId) >= value2.Amount) { return BountyState.Ready; } return BountyState.Active; } return BountyState.Available; } public int GetProgress(string bountyId) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return 0; } int result = 0; if (localPlayer.m_customData.TryGetValue("HaldorBounty_Progress_" + bountyId, out var value)) { int.TryParse(value, out result); } return result; } public bool IsAvailable(string bountyId) { if (!_bountyLookup.TryGetValue(bountyId, out var value)) { return false; } if (GetState(bountyId) != 0) { return false; } return IsBossGateUnlocked(value); } public List<BountyEntry> GetVisibleBounties() { CheckDayReset(); List<BountyEntry> list = new List<BountyEntry>(); HashSet<string> hashSet = new HashSet<string>(); foreach (BountyEntry dailyBounty in GetDailyBounties()) { if (GetState(dailyBounty.Id) != 0 || IsAvailable(dailyBounty.Id)) { list.Add(dailyBounty); hashSet.Add(dailyBounty.Id); } } foreach (BountyEntry bounty in BountyConfig.Bounties) { if (!hashSet.Contains(bounty.Id)) { BountyState state = GetState(bounty.Id); if (state == BountyState.Active || state == BountyState.Ready) { list.Add(bounty); hashSet.Add(bounty.Id); } } } return list; } public static string GetBossName(string bountyId) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && localPlayer.m_customData.TryGetValue("HaldorBounty_BossName_" + bountyId, out var value) && !string.IsNullOrEmpty(value)) { return value; } int gender = 0; if (Instance != null && Instance._bountyLookup.TryGetValue(bountyId, out var value2)) { gender = value2.Gender; } return GenerateBossName(bountyId, gender); } private static string GenerateBossName(string bountyId, int gender = 0) { int num = StableHash(bountyId); int num2 = 0; if ((Object)(object)EnvMan.instance != (Object)null && (Object)(object)ZNet.instance != (Object)null) { num2 = EnvMan.instance.GetDay(ZNet.instance.GetTimeSeconds()); } string[] array = gender switch { 1 => MaleNames, 2 => FemaleNames, _ => MaleNames, }; if (gender == 0) { int num3 = ((num + num2 * 7) & 0x7FFFFFFF) % (MaleNames.Length + FemaleNames.Length); array = ((num3 < MaleNames.Length) ? MaleNames : FemaleNames); int num4 = ((num3 < MaleNames.Length) ? num3 : (num3 - MaleNames.Length)); return array[num4]; } int num5 = ((num + num2 * 7) & 0x7FFFFFFF) % array.Length; return array[num5]; } private static int StableHash(string s) { int num = 5381; foreach (char c in s) { num = (num << 5) + num + c; } return num & 0x7FFFFFFF; } public bool AcceptBounty(string bountyId) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } if (!IsAvailable(bountyId)) { return false; } localPlayer.m_customData["HaldorBounty_State_" + bountyId] = "active"; localPlayer.m_customData["HaldorBounty_Progress_" + bountyId] = "0"; _activeBountyIds.Add(bountyId); _activeBountyIdsLoaded = true; string text = null; _bountyLookup.TryGetValue(bountyId, out var value); if (value != null && value.SpawnLevel > 0) { text = GenerateBossName(bountyId, value.Gender); localPlayer.m_customData["HaldorBounty_BossName_" + bountyId] = text; } PlaySkillLevelupEffect(localPlayer); if (value != null && value.SpawnLevel > 0) { try { SpawnBountyCreature(localPlayer, value, text); } catch (Exception arg) { HaldorBounties.Log.LogError((object)$"[BountyManager] SpawnBountyCreature failed for {bountyId}: {arg}"); } } if (value != null) { AddBountyStatusEffect(localPlayer, value); } HaldorBounties.Log.LogInfo((object)("[BountyManager] Accepted bounty: " + bountyId)); return true; } public bool AbandonBounty(string bountyId) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } if (GetState(bountyId) != BountyState.Active) { return false; } localPlayer.m_customData.Remove("HaldorBounty_State_" + bountyId); localPlayer.m_customData.Remove("HaldorBounty_Progress_" + bountyId); localPlayer.m_customData.Remove("HaldorBounty_BossName_" + bountyId); localPlayer.m_customData.Remove("HaldorBounty_SpawnPos_" + bountyId); _activeBountyIds.Remove(bountyId); RemoveBountyStatusEffect(localPlayer, bountyId); DespawnBountyCreature(bountyId); RemoveBountyPin(bountyId); HaldorBounties.Log.LogInfo((object)("[BountyManager] Abandoned bounty: " + bountyId)); return true; } private void SpawnBountyCreature(Player player, BountyEntry entry, string bossName) { //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = ZNetScene.instance.GetPrefab(entry.Target); if ((Object)(object)prefab == (Object)null) { HaldorBounties.Log.LogWarning((object)("[BountyManager] Prefab not found: " + entry.Target)); return; } if (string.IsNullOrEmpty(bossName)) { bossName = GetBossName(entry.Id); } bool flag = IsRaid(entry); int num = ((!flag) ? 1 : entry.Amount); Vector3 position = ((Component)player).transform.position; Vector3 val = position; Character val2 = null; int num2 = 0; RemoveBountyPin(entry.Id); CleanupStaleCreatures(entry.Id); for (int i = 0; i < num; i++) { try { Vector3 val3 = ((!flag || i <= 0) ? FindSpawnPosition(position, 700f) : FindSpawnPosition(val, 10f)); if (i == 0) { val = val3; } Quaternion val4 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f); GameObject obj = Object.Instantiate<GameObject>(prefab, val3, val4); Character component = obj.GetComponent<Character>(); ZNetView component2 = obj.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && ((component2 != null) ? component2.GetZDO() : null) != null) { ZDO zDO = component2.GetZDO(); zDO.Set(StringExtensionMethods.GetStableHashCode("HB_NpcGender"), entry.Gender, false); component.SetLevel(entry.SpawnLevel); component.SetHealth(component.GetMaxHealth()); if (flag) { component.m_name = "Valheim Raider"; } else { component.m_name = bossName; component.m_boss = true; component.m_dontHideBossHud = false; } zDO.Set("HaldorBountyMiniboss", true); zDO.Set("HaldorBountyBossName", bossName); zDO.Set("HaldorBountyId", entry.Id); zDO.Persistent = true; } if (i == 0 && (Object)(object)component != (Object)null) { val2 = component; } num2++; } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)$"[BountyManager] Failed to spawn creature {i + 1}/{num}: {ex.Message}"); } } if ((Object)(object)val2 != (Object)null) { _bountyCreatures[entry.Id] = val2; } player.m_customData["HaldorBounty_SpawnPos_" + entry.Id] = string.Format(CultureInfo.InvariantCulture, "{0:F1},{1:F1},{2:F1}", val.x, val.y, val.z); if ((Object)(object)Minimap.instance != (Object)null) { try { PinData val5 = Minimap.instance.AddPin(val, (PinType)13, bossName, false, false, 0L, default(PlatformUserID)); val5.m_worldSize = 80f; val5.m_animate = true; _bountyPins[entry.Id] = val5; } catch { try { PinData value = Minimap.instance.AddPin(val, (PinType)9, bossName, true, false, 0L, default(PlatformUserID)); _bountyPins[entry.Id] = value; } catch { } } } MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, bossName + " the " + entry.Title + " has appeared in the wild!", 0, (Sprite)null, false); } HaldorBounties.Log.LogInfo((object)$"[BountyManager] Spawned {num2}/{num}x {entry.Target} \"{bossName}\" level {entry.SpawnLevel}"); } public void RegisterBountyCreature(string bountyId, Character creature) { //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(bountyId) || (Object)(object)creature == (Object)null) { return; } BountyState state = GetState(bountyId); if (state != BountyState.Active && state != BountyState.Ready) { return; } bool flag = false; if (_bountyLookup.TryGetValue(bountyId, out var value)) { flag = IsRaid(value); } string text = GetBossName(bountyId); if (flag) { creature.m_name = "Valheim Raider"; } else { if (!string.IsNullOrEmpty(text)) { creature.m_name = text; } else { text = creature.m_name; } creature.m_boss = true; creature.m_dontHideBossHud = false; } _bountyCreatures[bountyId] = creature; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { Vector3 position = ((Component)creature).transform.position; localPlayer.m_customData["HaldorBounty_SpawnPos_" + bountyId] = string.Format(CultureInfo.InvariantCulture, "{0:F1},{1:F1},{2:F1}", position.x, position.y, position.z); } if (_bountyPins.ContainsKey(bountyId) || !((Object)(object)Minimap.instance != (Object)null)) { return; } string text2 = (flag ? "Valheim Raiders" : text); try { PinData val = Minimap.instance.AddPin(((Component)creature).transform.position, (PinType)13, text2, false, false, 0L, default(PlatformUserID)); val.m_worldSize = 80f; val.m_animate = true; _bountyPins[bountyId] = val; } catch { try { PinData value2 = Minimap.instance.AddPin(((Component)creature).transform.position, (PinType)9, text2, true, false, 0L, default(PlatformUserID)); _bountyPins[bountyId] = value2; } catch { } } HaldorBounties.Log.LogInfo((object)("[BountyManager] Re-registered bounty creature: " + text2 + " (bounty=" + bountyId + ")")); } public double GetSecondsUntilReset() { if ((Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return 0.0; } double timeSeconds = ZNet.instance.GetTimeSeconds(); long dayLengthSec = EnvMan.instance.m_dayLengthSec; double num = (double)((int)(timeSeconds / (double)dayLengthSec) + 1) * (double)dayLengthSec; return Math.Max(0.0, num - timeSeconds); } public int ResetAll(Player player) { if ((Object)(object)player == (Object)null) { return 0; } List<string> list = new List<string>(); foreach (string key in player.m_customData.Keys) { if (key.StartsWith("HaldorBounty_State_") || key.StartsWith("HaldorBounty_Progress_") || key.StartsWith("HaldorBounty_BossName_") || key.StartsWith("HaldorBounty_SpawnPos_") || key == "HaldorBounty_LastDay") { list.Add(key); } } foreach (string item in list) { player.m_customData.Remove(item); } foreach (KeyValuePair<string, Character> bountyCreature in _bountyCreatures) { if ((Object)(object)bountyCreature.Value != (Object)null && !bountyCreature.Value.IsDead()) { ZNetView component = ((Component)bountyCreature.Value).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.Destroy(); } else { Object.Destroy((Object)(object)((Component)bountyCreature.Value).gameObject); } } } _bountyCreatures.Clear(); foreach (KeyValuePair<string, PinData> bountyPin in _bountyPins) { if ((Object)(object)Minimap.instance != (Object)null && bountyPin.Value != null) { Minimap.instance.RemovePin(bountyPin.Value); } } _bountyPins.Clear(); foreach (string item2 in new List<string>(_activeBountyIds)) { RemoveBountyStatusEffect(player, item2); } _activeBountyIds.Clear(); _activeBountyIdsLoaded = false; _lastDay = -1; HaldorBounties.Log.LogInfo((object)$"[BountyManager] Reset all bounties. Removed {list.Count} keys."); return list.Count; } public void UpdateBossHudRange() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } float num = (((Object)(object)Minimap.instance != (Object)null) ? Minimap.instance.m_exploreRadius : 100f); Vector3 position = ((Component)localPlayer).transform.position; foreach (KeyValuePair<string, Character> bountyCreature in _bountyCreatures) { Character value = bountyCreature.Value; if (!((Object)(object)value == (Object)null) && !value.IsDead() && (!_bountyLookup.TryGetValue(bountyCreature.Key, out var value2) || !IsRaid(value2))) { bool flag = Vector3.Distance(((Component)value).transform.position, position) <= num; if (value.m_boss != flag) { value.m_boss = flag; value.m_dontHideBossHud = false; } } } } public void UpdateBountyPins() { //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } EnsureActiveBountyIdsLoaded(); List<KeyValuePair<string, bool>> list = new List<KeyValuePair<string, bool>>(); foreach (KeyValuePair<string, Character> bountyCreature in _bountyCreatures) { PinData value2; if ((Object)(object)bountyCreature.Value == (Object)null) { BountyState state = GetState(bountyCreature.Key); bool value = state == BountyState.Ready || state == BountyState.Claimed; list.Add(new KeyValuePair<string, bool>(bountyCreature.Key, value)); } else if (bountyCreature.Value.IsDead()) { list.Add(new KeyValuePair<string, bool>(bountyCreature.Key, value: true)); } else if (_bountyPins.TryGetValue(bountyCreature.Key, out value2) && value2 != null) { value2.m_pos = ((Component)bountyCreature.Value).transform.position; } } foreach (KeyValuePair<string, bool> item in list) { string key = item.Key; bool value3 = item.Value; _bountyCreatures.Remove(key); if (value3 && _bountyPins.TryGetValue(key, out var value4)) { if (value4 != null) { Minimap.instance.RemovePin(value4); } _bountyPins.Remove(key); } } foreach (string activeBountyId in _activeBountyIds) { if (_bountyPins.ContainsKey(activeBountyId) || GetState(activeBountyId) != BountyState.Active || !_bountyLookup.TryGetValue(activeBountyId, out var value5) || value5.SpawnLevel <= 0 || !localPlayer.m_customData.TryGetValue("HaldorBounty_SpawnPos_" + activeBountyId, out var value6) || !TryParsePosition(value6, out var result)) { continue; } string bossName = GetBossName(activeBountyId); string text = (IsRaid(value5) ? "Valheim Raiders" : bossName); try { PinData val = Minimap.instance.AddPin(result, (PinType)13, text, false, false, 0L, default(PlatformUserID)); val.m_worldSize = 80f; val.m_animate = true; _bountyPins[activeBountyId] = val; } catch { try { PinData value7 = Minimap.instance.AddPin(result, (PinType)9, text, true, false, 0L, default(PlatformUserID)); _bountyPins[activeBountyId] = value7; } catch { } } } } private static bool TryParsePosition(string posStr, out Vector3 result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) result = Vector3.zero; if (string.IsNullOrEmpty(posStr)) { return false; } string[] array = posStr.Split(new char[1] { ',' }); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4)) { return false; } result = new Vector3(result2, result3, result4); return true; } private static void CleanupStaleCreatures(string bountyId) { try { int num = 0; List<Character> allCharacters = Character.GetAllCharacters(); for (int num2 = allCharacters.Count - 1; num2 >= 0; num2--) { Character val = allCharacters[num2]; if ((Object)(object)val == (Object)null || val.IsPlayer()) { continue; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); ZDO val2 = ((component != null) ? component.GetZDO() : null); if (val2 == null || val2.GetString("HaldorBountyId", "") != bountyId) { continue; } if (component.IsValid() && component.IsOwner()) { component.Destroy(); } else { if (!component.IsOwner()) { continue; } Object.Destroy((Object)(object)((Component)val).gameObject); } num++; } if (num > 0) { HaldorBounties.Log.LogInfo((object)$"[BountyManager] Cleaned up {num} stale creatures for bounty: {bountyId}"); } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyManager] Stale creature cleanup failed: " + ex.Message)); } } private void DespawnBountyCreature(string bountyId) { if (!_bountyCreatures.TryGetValue(bountyId, out var value)) { return; } if ((Object)(object)value != (Object)null && !value.IsDead()) { ZNetView component = ((Component)value).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.Destroy(); } else { Object.Destroy((Object)(object)((Component)value).gameObject); } HaldorBounties.Log.LogInfo((object)("[BountyManager] Despawned creature for bounty: " + bountyId)); } _bountyCreatures.Remove(bountyId); } private void RemoveBountyPin(string bountyId) { if (_bountyPins.TryGetValue(bountyId, out var value)) { if ((Object)(object)Minimap.instance != (Object)null && value != null) { Minimap.instance.RemovePin(value); } _bountyPins.Remove(bountyId); } _bountyCreatures.Remove(bountyId); } private static Vector3 FindSpawnPosition(Vector3 center, float distance) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(10f, distance * 0.07f); bool flag = distance > 128f && WorldGenerator.instance != null; float num4 = default(float); for (int i = 0; i < 30; i++) { float num2 = Random.Range(0f, 360f); float num3 = distance + Random.Range(0f - num, num); Vector3 val = center + Quaternion.Euler(0f, num2, 0f) * Vector3.forward * num3; if (flag) { float height = WorldGenerator.instance.GetHeight(val.x, val.z); if (height > 30f) { val.y = height + 0.5f; return val; } } else if (ZoneSystem.instance.GetGroundHeight(val, ref num4) && num4 > 30f) { val.y = num4 + 0.5f; return val; } } float num9 = default(float); for (int j = 1; j <= 5; j++) { float num5 = distance * (1f - (float)j * 0.15f); float num6 = Mathf.Max(10f, num5 * 0.1f); for (int k = 0; k < 20; k++) { float num7 = Random.Range(0f, 360f); float num8 = num5 + Random.Range(0f - num6, num6); Vector3 val2 = center + Quaternion.Euler(0f, num7, 0f) * Vector3.forward * num8; if (flag) { float height2 = WorldGenerator.instance.GetHeight(val2.x, val2.z); if (height2 > 30f) { val2.y = height2 + 0.5f; return val2; } } else if (ZoneSystem.instance.GetGroundHeight(val2, ref num9) && num9 > 30f) { val2.y = num9 + 0.5f; return val2; } } } Vector3 result = center; float num10 = default(float); if (flag) { float height3 = WorldGenerator.instance.GetHeight(center.x, center.z); result.y = Mathf.Max(height3, 30f) + 0.5f; } else if (ZoneSystem.instance.GetGroundHeight(center, ref num10)) { result.y = Mathf.Max(num10, 30f) + 0.5f; } else { result.y = Mathf.Max(center.y, 31f); } HaldorBounties.Log.LogWarning((object)"[BountyManager] FindSpawnPosition: all attempts hit ocean — spawning near player as last resort"); return result; } public void IncrementKill(string prefabName, int level) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } EnsureActiveBountyIdsLoaded(); if (_activeBountyIds.Count == 0) { return; } HaldorBounties.Log.LogInfo((object)$"[BountyManager] Kill detected: {prefabName} (level {level})"); foreach (string activeBountyId in _activeBountyIds) { if (!_bountyLookup.TryGetValue(activeBountyId, out var value) || value.Type != "Kill" || !string.Equals(value.Target, prefabName, StringComparison.OrdinalIgnoreCase)) { continue; } if (value.SpawnLevel > 0 && level != value.SpawnLevel) { HaldorBounties.Log.LogInfo((object)$"[BountyManager] Level mismatch for {activeBountyId}: need {value.SpawnLevel}, got {level}"); continue; } int progress = GetProgress(activeBountyId); if (progress >= value.Amount) { continue; } int num = progress + 1; localPlayer.m_customData["HaldorBounty_Progress_" + activeBountyId] = num.ToString(); HaldorBounties.Log.LogInfo((object)$"[BountyManager] Bounty progress: {activeBountyId} ({num}/{value.Amount})"); if (num >= value.Amount) { HaldorBounties.Log.LogInfo((object)("[BountyManager] Bounty ready: " + activeBountyId)); MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Bounty Complete: " + value.Title + "!", 0, (Sprite)null, false); } RemoveBountyPin(activeBountyId); } } } public bool ClaimReward(string bountyId) { return ClaimRewardChoice(bountyId, RewardCategory.Coins); } public bool ClaimRewardChoice(string bountyId, RewardCategory category) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } if (!_bountyLookup.TryGetValue(bountyId, out var value)) { return false; } if (GetState(bountyId) != BountyState.Ready) { return false; } RewardResolver.ResolvedReward resolvedReward = RewardResolver.ResolveRewards(value).Find((RewardResolver.ResolvedReward r) => r.Category == category); if (resolvedReward == null) { return false; } if (category == RewardCategory.Coins) { int value2 = ReadBankBalance(localPlayer) + resolvedReward.CoinAmount; WriteBankBalance(localPlayer, value2); RefreshBankUI(); MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, $"Deposited {resolvedReward.CoinAmount:N0} coins into your bank!", 0, (Sprite)null, false); } } else if (!RewardResolver.DeliverReward(resolvedReward, localPlayer)) { HaldorBounties.Log.LogWarning((object)$"[BountyManager] Failed to deliver {category} reward for {bountyId}"); return false; } localPlayer.m_customData["HaldorBounty_State_" + bountyId] = "claimed"; localPlayer.m_customData.Remove("HaldorBounty_BossName_" + bountyId); localPlayer.m_customData.Remove("HaldorBounty_SpawnPos_" + bountyId); RemoveBountyStatusEffect(localPlayer, bountyId); RemoveBountyPin(bountyId); _activeBountyIds.Remove(bountyId); PlaySkillLevelupEffect(localPlayer); HaldorBounties.Log.LogInfo((object)$"[BountyManager] Claimed bounty: {bountyId}, reward: {category} ({resolvedReward.DisplayText})"); return true; } private static void PlaySkillLevelupEffect(Player player) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) if ((Object)(object)player == (Object)null) { return; } try { if (player.m_skillLevelupEffects != null && player.m_skillLevelupEffects.m_effectPrefabs != null && player.m_skillLevelupEffects.m_effectPrefabs.Length != 0) { player.m_skillLevelupEffects.Create(((Component)player).transform.position, ((Component)player).transform.rotation, ((Component)player).transform, 1f, -1); return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance != (Object)null) { GameObject prefab = instance.GetPrefab("vfx_RaiseSkill"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate<GameObject>(prefab, ((Component)player).transform.position, ((Component)player).transform.rotation); return; } } HaldorBounties.Log.LogWarning((object)"[BountyManager] Could not play skill levelup effect — no source found"); } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyManager] PlaySkillLevelupEffect error: " + ex.Message)); } } private static Sprite GetOrLoadBountyIcon() { if ((Object)(object)_bountyIcon != (Object)null) { return _bountyIcon; } try { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab("Coins") : null); Sprite[] array = ((obj == null) ? null : obj.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_icons); if (array != null && array.Length != 0) { _bountyIcon = array[0]; } } catch { } return _bountyIcon; } private static void AddBountyStatusEffect(Player player, BountyEntry entry) { if ((Object)(object)player == (Object)null || entry == null) { return; } try { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { int stableHashCode = StringExtensionMethods.GetStableHashCode("HaldorBounty_" + entry.Id); if (!((Object)(object)sEMan.GetStatusEffect(stableHashCode) != (Object)null)) { BountyStatusEffect bountyStatusEffect = ScriptableObject.CreateInstance<BountyStatusEffect>(); bountyStatusEffect.Setup(entry.Id, entry.Title, entry.Amount, GetOrLoadBountyIcon()); sEMan.AddStatusEffect((StatusEffect)(object)bountyStatusEffect, false, 0, 0f); } } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyManager] Failed to add status effect for " + entry.Id + ": " + ex.Message)); } } private static void RemoveBountyStatusEffect(Player player, string bountyId) { if ((Object)(object)player == (Object)null) { return; } try { int stableHashCode = StringExtensionMethods.GetStableHashCode("HaldorBounty_" + bountyId); SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { sEMan.RemoveStatusEffect(stableHashCode, false); } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyManager] Failed to remove status effect for " + bountyId + ": " + ex.Message)); } } public void RefreshStatusEffects(Player player) { if ((Object)(object)player == (Object)null) { return; } EnsureActiveBountyIdsLoaded(); foreach (string activeBountyId in _activeBountyIds) { if (_bountyLookup.TryGetValue(activeBountyId, out var value)) { AddBountyStatusEffect(player, value); } } } public void RestoreBountyPins(Player player) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)Minimap.instance == (Object)null) { return; } EnsureActiveBountyIdsLoaded(); foreach (string activeBountyId in _activeBountyIds) { if (_bountyPins.ContainsKey(activeBountyId) || !_bountyLookup.TryGetValue(activeBountyId, out var value) || value.SpawnLevel <= 0 || !player.m_customData.TryGetValue("HaldorBounty_SpawnPos_" + activeBountyId, out var value2) || !TryParsePosition(value2, out var result)) { continue; } string text = (IsRaid(value) ? "Valheim Raiders" : GetBossName(activeBountyId)); try { PinData val = Minimap.instance.AddPin(result, (PinType)13, text, false, false, 0L, default(PlatformUserID)); val.m_worldSize = 80f; val.m_animate = true; _bountyPins[activeBountyId] = val; } catch { try { PinData value3 = Minimap.instance.AddPin(result, (PinType)9, text, true, false, 0L, default(PlatformUserID)); _bountyPins[activeBountyId] = value3; } catch { } } HaldorBounties.Log.LogInfo((object)$"[BountyManager] Restored pin for bounty: {activeBountyId} at {result}"); } } private static int ReadBankBalance(Player player) { if ((Object)(object)player == (Object)null) { return 0; } if (player.m_customData.TryGetValue("TraderSharedBank_Balance", out var value) && int.TryParse(value, out var result)) { return result; } if (player.m_customData.TryGetValue("HaldorBank_Balance", out value) && int.TryParse(value, out result)) { return result; } return 0; } private static void WriteBankBalance(Player player, int value) { if (!((Object)(object)player == (Object)null)) { value = Mathf.Max(0, value); string value2 = value.ToString(); player.m_customData["TraderSharedBank_Balance"] = value2; player.m_customData["HaldorBank_Balance"] = value2; player.m_customData["HildirBank_Balance"] = value2; player.m_customData["BogWitchBank_Balance"] = value2; } } private void RefreshBankUI() { try { if (!(_reloadBankBalance == null)) { object obj = TraderUIPatches.TraderUIInstance; if (obj == null && _getTraderUI != null) { obj = _getTraderUI.Invoke(null, null); } if (obj != null) { _reloadBankBalance.Invoke(obj, null); } } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[BountyManager] Failed to refresh bank UI: " + ex.Message)); } } } public class BountyStatusEffect : StatusEffect { public string BountyId; private int _target; public void Setup(string bountyId, string title, int target, Sprite icon) { BountyId = bountyId; _target = target; base.m_name = title; base.m_ttl = 0f; base.m_icon = icon; ((Object)this).name = "HaldorBounty_" + bountyId; } public override string GetIconText() { if (BountyManager.Instance == null || string.IsNullOrEmpty(BountyId)) { return ""; } int num = Mathf.Min(BountyManager.Instance.GetProgress(BountyId), _target); return $"{num}/{_target}"; } public override string GetTooltipString() { if (BountyManager.Instance == null || string.IsNullOrEmpty(BountyId)) { return base.m_name; } int progress = BountyManager.Instance.GetProgress(BountyId); if (BountyManager.Instance.GetState(BountyId) == BountyState.Ready) { return base.m_name + "\nCompleted! Return to Haldor to choose your reward."; } return $"{base.m_name}\nProgress: {Mathf.Min(progress, _target)} / {_target}"; } public override void UpdateStatusEffect(float dt) { } public override bool IsDone() { return false; } } public enum RewardCategory { Coins, Ingots, Resources, Consumables } public enum BiomeTier { Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, Ashlands } public class RewardItem { public string PrefabName; public int MinStack; public int MaxStack; public int Quality; public RewardItem(string prefab, int min, int max, int quality = 1) { PrefabName = prefab; MinStack = min; MaxStack = max; Quality = quality; } } public static class RewardPool { public static readonly Dictionary<BiomeTier, RewardItem[]> IngotPool = new Dictionary<BiomeTier, RewardItem[]> { { BiomeTier.Meadows, new RewardItem[3] { new RewardItem("CopperOre", 5, 10), new RewardItem("TinOre", 5, 10), new RewardItem("Flint", 8, 14) } }, { BiomeTier.BlackForest, new RewardItem[4] { new RewardItem("Bronze", 4, 8), new RewardItem("Copper", 5, 8), new RewardItem("Tin", 5, 8), new RewardItem("Coal", 8, 14) } }, { BiomeTier.Swamp, new RewardItem[3] { new RewardItem("Iron", 4, 8), new RewardItem("IronScrap", 6, 12), new RewardItem("Chain", 1, 3) } }, { BiomeTier.Mountain, new RewardItem[3] { new RewardItem("Silver", 4, 8), new RewardItem("SilverOre", 6, 10), new RewardItem("Crystal", 3, 6) } }, { BiomeTier.Plains, new RewardItem[3] { new RewardItem("BlackMetal", 4, 8), new RewardItem("BlackMetalScrap", 6, 12), new RewardItem("LinenThread", 4, 8) } }, { BiomeTier.Mistlands, new RewardItem[4] { new RewardItem("BlackCore", 1, 2), new RewardItem("Eitr", 3, 6), new RewardItem("Carapace", 5, 10), new RewardItem("Sap", 4, 8) } }, { BiomeTier.Ashlands, new RewardItem[5] { new RewardItem("FlametalNew", 4, 8), new RewardItem("GemstoneRed", 1, 3), new RewardItem("GemstoneBlue", 1, 3), new RewardItem("GemstoneGreen", 1, 3), new RewardItem("CelestialFeather", 1, 2) } } }; public static readonly Dictionary<BiomeTier, RewardItem[]> ResourcePool = new Dictionary<BiomeTier, RewardItem[]> { { BiomeTier.Meadows, new RewardItem[6] { new RewardItem("Wood", 15, 30), new RewardItem("Stone", 12, 25), new RewardItem("LeatherScraps", 6, 12), new RewardItem("DeerHide", 4, 8), new RewardItem("Resin", 8, 16), new RewardItem("BoneFragments", 6, 12) } }, { BiomeTier.BlackForest, new RewardItem[6] { new RewardItem("FineWood", 10, 20), new RewardItem("SurtlingCore", 2, 4), new RewardItem("BoneFragments", 8, 14), new RewardItem("TrollHide", 3, 6), new RewardItem("Thistle", 5, 10), new RewardItem("GreydwarfEye", 6, 12) } }, { BiomeTier.Swamp, new RewardItem[6] { new RewardItem("ElderBark", 8, 15), new RewardItem("Guck", 4, 8), new RewardItem("Bloodbag", 4, 8), new RewardItem("Entrails", 5, 10), new RewardItem("WitheredBone", 2, 4), new RewardItem("Thistle", 6, 12) } }, { BiomeTier.Mountain, new RewardItem[5] { new RewardItem("WolfPelt", 3, 6), new RewardItem("FreezeGland", 4, 8), new RewardItem("Obsidian", 6, 12), new RewardItem("DragonTear", 1, 1), new RewardItem("JuteRed", 2, 4) } }, { BiomeTier.Plains, new RewardItem[5] { new RewardItem("Needle", 6, 12), new RewardItem("Flax", 6, 10), new RewardItem("Barley", 8, 14), new RewardItem("LoxPelt", 2, 5), new RewardItem("Tar", 5, 10) } }, { BiomeTier.Mistlands, new RewardItem[5] { new RewardItem("Softtissue", 5, 10), new RewardItem("RoyalJelly", 3, 6), new RewardItem("ScaleHide", 4, 8), new RewardItem("YggdrasilWood", 8, 14), new RewardItem("DvergrNeedle", 2, 4) } }, { BiomeTier.Ashlands, new RewardItem[5] { new RewardItem("Grausten", 8, 14), new RewardItem("CharredBone", 5, 10), new RewardItem("AskHide", 3, 6), new RewardItem("MorgenSinew", 1, 3), new RewardItem("BellFragment", 1, 2) } } }; public static readonly Dictionary<BiomeTier, RewardItem[]> ConsumablePool = new Dictionary<BiomeTier, RewardItem[]> { { BiomeTier.Meadows, new RewardItem[6] { new RewardItem("CookedMeat", 3, 6), new RewardItem("CookedDeerMeat", 3, 5), new RewardItem("QueensJam", 3, 5), new RewardItem("Honey", 3, 6), new RewardItem("MeadHealthMinor", 3, 5), new RewardItem("MeadStaminaMinor", 3, 5) } }, { BiomeTier.BlackForest, new RewardItem[5] { new RewardItem("CarrotSoup", 3, 5), new RewardItem("MeadPoisonResist", 3, 5), new RewardItem("MeadHealthMedium", 3, 5), new RewardItem("Sausages", 3, 6), new RewardItem("DeerStew", 3, 5) } }, { BiomeTier.Swamp, new RewardItem[6] { new RewardItem("MeadPoisonResist", 4, 6), new RewardItem("MeadStaminaMedium", 3, 5), new RewardItem("TurnipStew", 3, 5), new RewardItem("Sausages", 4, 6), new RewardItem("BlackSoup", 3, 5), new RewardItem("MeadHealthMedium", 3, 6) } }, { BiomeTier.Mountain, new RewardItem[6] { new RewardItem("WolfSkewer", 3, 5), new RewardItem("EyeScream", 3, 5), new RewardItem("MeadFrostResist", 3, 5), new RewardItem("MeadStaminaMedium", 4, 6), new RewardItem("OnionSoup", 3, 5), new RewardItem("MeadHealthMajor", 2, 4) } }, { BiomeTier.Plains, new RewardItem[6] { new RewardItem("LoxPie", 2, 4), new RewardItem("BloodPudding", 2, 4), new RewardItem("FishAndBread", 2, 4), new RewardItem("MeadHealthMajor", 3, 5), new RewardItem("MeadStaminaLingering", 3, 5), new RewardItem("Bread", 3, 6) } }, { BiomeTier.Mistlands, new RewardItem[6] { new RewardItem("YggdrasilPorridge", 2, 4), new RewardItem("SeekerAspic", 2, 4), new RewardItem("MagicallyStuffedShroom", 2, 4), new RewardItem("MeadEitrMinor", 3, 5), new RewardItem("MushroomOmelette", 2, 4), new RewardItem("MisthareSupreme", 2, 4) } }, { BiomeTier.Ashlands, new RewardItem[6] { new RewardItem("MeatPlatter", 2, 4), new RewardItem("ScorchingMedley", 2, 4), new RewardItem("FierySvinstew", 2, 3), new RewardItem("MeadEitrLingering", 2, 4), new RewardItem("PiquantPie", 2, 3), new RewardItem("SparklingShroomshake", 2, 3) } } }; public static BiomeTier GetBiomeTier(BountyEntry entry) { return (entry.RequiredBoss ?? "") switch { "" => BiomeTier.Meadows, "defeated_eikthyr" => BiomeTier.BlackForest, "defeated_gdking" => BiomeTier.Swamp, "defeated_bonemass" => BiomeTier.Mountain, "defeated_dragon" => BiomeTier.Plains, "defeated_goblinking" => BiomeTier.Mistlands, "defeated_queen" => BiomeTier.Ashlands, _ => BiomeTier.Meadows, }; } } public static class RewardResolver { public class ResolvedReward { public RewardCategory Category; public string DisplayText; public string PrefabName; public int Stack; public int Quality; public int CoinAmount; public Sprite Icon; } public static List<ResolvedReward> ResolveRewards(BountyEntry entry) { List<ResolvedReward> list = new List<ResolvedReward>(); Random rng = new Random(StableHash(entry.Id)); BiomeTier biomeTier = RewardPool.GetBiomeTier(entry); bool num = entry.Tier == "Miniboss" || entry.Tier == "Special"; bool flag = entry.Tier == "Raid"; float num2 = (num ? 1.5f : (flag ? 1.25f : 1f)); float stackMult = (num ? 1.5f : (flag ? 1.25f : 1f)); int num3 = (int)((float)entry.Reward * num2); list.Add(new ResolvedReward { Category = RewardCategory.Coins, CoinAmount = num3, PrefabName = "Coins", DisplayText = $"{num3}c", Stack = num3, Quality = 1 }); list.Add(ResolveFromPool(RewardPool.IngotPool, biomeTier, rng, stackMult, RewardCategory.Ingots)); list.Add(ResolveFromPool(RewardPool.ResourcePool, biomeTier, rng, stackMult, RewardCategory.Resources)); list.Add(ResolveFromPool(RewardPool.ConsumablePool, biomeTier, rng, stackMult, RewardCategory.Consumables)); return list; } private static ResolvedReward ResolveFromPool(Dictionary<BiomeTier, RewardItem[]> pool, BiomeTier biome, Random rng, float stackMult, RewardCategory category) { if ((!pool.TryGetValue(biome, out var value) || value.Length == 0) && (!pool.TryGetValue(BiomeTier.Meadows, out value) || value == null || value.Length == 0)) { return new ResolvedReward { Category = category, DisplayText = "???", PrefabName = "", Stack = 1, Quality = 1 }; } int num = rng.Next(value.Length); RewardItem rewardItem = value[num]; int num2 = rng.Next(rewardItem.MinStack, rewardItem.MaxStack + 1); if (stackMult > 1f) { num2 = Mathf.Max(1, (int)((float)num2 * stackMult)); } string displayName = GetDisplayName(rewardItem.PrefabName); string displayText = ((num2 > 1) ? $"{num2}x {displayName}" : displayName); return new ResolvedReward { Category = category, PrefabName = rewardItem.PrefabName, Stack = Mathf.Max(1, num2), Quality = rewardItem.Quality, DisplayText = displayText }; } public static bool DeliverReward(ResolvedReward reward, Player player) { //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || reward == null) { return false; } if (reward.Category == RewardCategory.Coins) { return true; } try { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(reward.PrefabName) : null); if ((Object)(object)val == (Object)null) { HaldorBounties.Log.LogWarning((object)("[RewardResolver] Prefab not found: " + reward.PrefabName)); return false; } ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { return false; } ItemData val2 = component.m_itemData.Clone(); val2.m_dropPrefab = val; val2.m_worldLevel = (byte)Game.m_worldLevel; val2.m_stack = reward.Stack; val2.m_quality = reward.Quality; val2.m_durability = val2.GetMaxDurability(); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory.CanAddItem(val2, -1)) { inventory.AddItem(val2); HaldorBounties.Log.LogInfo((object)$"[RewardResolver] Added to inventory: {reward.PrefabName} x{reward.Stack}"); return true; } Vector3 val3 = ((Component)player).transform.position + ((Component)player).transform.forward * 1.5f + Vector3.up; ItemDrop.DropItem(val2, reward.Stack, val3, ((Component)player).transform.rotation); MessageHud instance2 = MessageHud.instance; if (instance2 != null) { instance2.ShowMessage((MessageType)1, "Inventory full — reward dropped nearby", 0, (Sprite)null, false); } HaldorBounties.Log.LogInfo((object)$"[RewardResolver] Dropped at player: {reward.PrefabName} x{reward.Stack}"); return true; } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[RewardResolver] Delivery failed: " + ex.Message)); return false; } } public static string GetDisplayName(string prefabName) { try { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab(prefabName) : null); ItemDrop val = ((obj != null) ? obj.GetComponent<ItemDrop>() : null); if (val?.m_itemData?.m_shared != null) { Localization instance2 = Localization.instance; string text = ((instance2 != null) ? instance2.Localize(val.m_itemData.m_shared.m_name) : null); if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } return prefabName; } public static Sprite GetRewardIcon(ResolvedReward reward) { if ((Object)(object)reward.Icon != (Object)null) { return reward.Icon; } try { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(reward.PrefabName) : null); object icon; if (val == null) { icon = null; } else { ItemDrop component = val.GetComponent<ItemDrop>(); if (component == null) { icon = null; } else { ItemData itemData = component.m_itemData; icon = ((itemData != null) ? itemData.GetIcon() : null); } } reward.Icon = (Sprite)icon; } catch { } return reward.Icon; } private static int StableHash(string s) { int num = 5381; foreach (char c in s) { num = (num << 5) + num + c; } return num & 0x7FFFFFFF; } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] public static class ConsoleCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; internal void <Postfix>b__0_0(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("No player found."); return; } int num = BountyManager.Instance?.ResetAll(localPlayer) ?? 0; args.Context.AddString($"Bounty reset complete. Cleared {num} bounty entries."); ((Character)localPlayer).Message((MessageType)2, "All bounties have been reset!", 0, (Sprite)null); } } [HarmonyPostfix] private static void Postfix() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0029: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("No player found."); } else { int num = BountyManager.Instance?.ResetAll(localPlayer) ?? 0; args.Context.AddString($"Bounty reset complete. Cleared {num} bounty entries."); ((Character)localPlayer).Message((MessageType)2, "All bounties have been reset!", 0, (Sprite)null); } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("BountyReset", "Reset all bounty progress, active bounties, and claimed states.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class KillTracker { [ThreadStatic] private static Stack<(string Prefab, int Level)?> _killStack; private static FieldInfo _hasHitField; private static FieldInfo _lastHitField; private static bool _fieldsCached; private static bool IsActiveTaggedBounty(Character character) { ZNetView component = ((Component)character).GetComponent<ZNetView>(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null || !val.GetBool("HaldorBountyMiniboss", false)) { return false; } string @string = val.GetString("HaldorBountyId", ""); if (string.IsNullOrEmpty(@string)) { return false; } BountyState bountyState = BountyManager.Instance?.GetState(@string) ?? BountyState.Available; if (bountyState != BountyState.Active) { return bountyState == BountyState.Ready; } return true; } [HarmonyPrefix] private static void Prefix(Character __instance) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if (_killStack == null) { _killStack = new Stack<(string, int)?>(); } try { if ((Object)(object)__instance == (Object)null || __instance.IsPlayer()) { _killStack.Push(null); return; } if (!_fieldsCached) { _hasHitField = AccessTools.Field(typeof(Character), "m_localPlayerHasHit"); _lastHitField = AccessTools.Field(typeof(Character), "m_lastHit"); _fieldsCached = true; } bool flag = false; bool flag2 = false; bool flag3 = IsActiveTaggedBounty(__instance); try { if (_hasHitField != null) { flag = (bool)_hasHitField.GetValue(__instance); } object? obj = _lastHitField?.GetValue(__instance); HitData val = (HitData)((obj is HitData) ? obj : null); if (val != null) { flag2 = (Object)(object)val.GetAttacker() == (Object)(object)Player.m_localPlayer; } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[KillTracker] Failed to read kill fields: " + ex.Message)); } bool flag4 = flag || flag2 || flag3; if (!flag4 && (Object)(object)Player.m_localPlayer != (Object)null && Vector3.Distance(((Component)__instance).transform.position, ((Component)Player.m_localPlayer).transform.position) < 40f) { flag4 = true; } if (!flag4) { _killStack.Push(null); return; } string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject); int level = __instance.GetLevel(); _killStack.Push((prefabName, level)); } catch (Exception ex2) { HaldorBounties.Log.LogWarning((object)("[KillTracker] Prefix error: " + ex2.Message)); _killStack?.Push(null); } } [HarmonyPostfix] private static void Postfix() { if (_killStack == null || _killStack.Count == 0) { return; } (string, int)? tuple = _killStack.Pop(); try { if (tuple.HasValue && !string.IsNullOrEmpty(tuple.Value.Item1)) { BountyManager.Instance?.IncrementKill(tuple.Value.Item1, tuple.Value.Item2); } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[KillTracker] Postfix error: " + ex.Message)); } } } public static class MinibossHud { [HarmonyPatch(typeof(Character), "Start")] public static class CharacterStartPatch { [HarmonyPostfix] private static void Postfix(Character __instance) { try { if (TryApplyMinibossFlags(__instance, out var bountyId)) { if (!string.IsNullOrEmpty(bountyId)) { BountyManager.Instance?.RegisterBountyCreature(bountyId, __instance); } HaldorBounties.Log.LogInfo((object)("[MinibossHud] Restored bounty creature: " + __instance.m_name + " (bounty=" + bountyId + ")")); } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[MinibossHud] CharacterStart error: " + ex.Message)); } } } [HarmonyPatch(typeof(EnemyHud), "ShowHud")] public static class ShowHudPatch { [HarmonyPrefix] private static void Prefix(EnemyHud __instance, Character c, bool isMount) { try { if (isMount || (Object)(object)c == (Object)null) { return; } bool flag = c.IsBoss(); if (!TryApplyMinibossFlags(c, out var bountyId)) { return; } if (!string.IsNullOrEmpty(bountyId)) { BountyManager.Instance?.RegisterBountyCreature(bountyId, c); } if (!flag && !(_hudsField == null) && _hudsField.GetValue(__instance) is IDictionary dictionary && dictionary.Contains(c)) { object obj = dictionary[c]; if (_guiField == null) { _guiField = AccessTools.Field(obj.GetType(), "m_gui"); } object? obj2 = _guiField?.GetValue(obj); GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null); if (val != null) { Object.Destroy((Object)(object)val); } dictionary.Remove(c); } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[MinibossHud] ShowHud prefix error: " + ex.Message)); } } [HarmonyPostfix] private static void Postfix(EnemyHud __instance, Character c, bool isMount) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) try { if (isMount || !TryApplyMinibossFlags(c, out var _) || _hudsField == null || !(_hudsField.GetValue(__instance) is IDictionary dictionary) || !dictionary.Contains(c)) { return; } object obj = dictionary[c]; if (_guiField == null) { _guiField = AccessTools.Field(obj.GetType(), "m_gui"); } if (!(_guiField == null)) { object? value = _guiField.GetValue(obj); GameObject val = (GameObject)((value is GameObject) ? value : null); if (!((Object)(object)val == (Object)null)) { val.transform.localScale = new Vector3(0.75f, 0.75f, 1f); } } } catch (Exception ex) { HaldorBounties.Log.LogWarning((object)("[MinibossHud] ShowHud error: " + ex.Message)); } } } private static readonly FieldInfo _hudsField = AccessTools.Field(typeof(EnemyHud), "m_huds"); private static FieldInfo _guiField; private static bool TryApplyMinibossFlags(Character character, out string bountyId) { bountyId = ""; if ((Object)(object)character == (Object)null || character.IsPlayer()) { return false; } ZNetView component = ((Component)character).GetComponent<ZNetVie