You are viewing a potentially older version of this package. View all versions.
Wubarrk-Shadows_Of_Midgard-2.0.11 icon

Shadows Of Midgard

A stealth and perception engine for Valheim. Creatures see, hear, remember and doubt - vision cones, line of sight, noise, light, weather and camouflage feed a Stealth Brain that tracks each player separately. Server-synced, dedicated-server ready.

Date uploaded 2 days ago
Version 2.0.11
Download link Wubarrk-Shadows_Of_Midgard-2.0.11.zip
Downloads 54
Dependency string Wubarrk-Shadows_Of_Midgard-2.0.11

This mod requires the following mods to function

denikson-BepInExPack_Valheim-5.4.2360 icon
denikson-BepInExPack_Valheim

BepInEx pack for Valheim. Preconfigured with the correct entry point for mods and preferred defaults for the community.

Preferred version: 5.4.2360

README

πŸŒ™ Shadows of Midgard

SHADOWS OF MIDGARD VERSION SERVER + CLIENT

"You think the dark hides you. The dark hides nothing β€” it only makes them work for it. Step off the gravel. Wait for dark. Let the rain do the rest, and the draugr will walk past close enough to touch."

Creatures see, hear, remember and doubt β€” and each one judges every player on their own.

A creature has a vision cone with a real blind spot behind it, a real line of sight against the same geometry the game uses, and ears that hear you by what you wear and how fast you move. It remembers where it last saw you, goes and looks, gives up, calls for help, and runs when it is losing. It can be hunting your friend and never notice you, crouched two metres away, in the same moment.

It does not fight the game's AI. It answers the questions vanilla already asks β€” can I see this, can I hear this β€” and lets vanilla decide what to do about the answer. If anything ever goes wrong, the floor is always plain vanilla.


NEW IN 2.0.9

A clear night finally hides you. The light that falls on you is now measured the way the game's own stealth check measures it β€” its strength and its colour β€” so the moon is no longer as bright as the noon sun.

When It
A clear moonlit night adds well under half the light of noon; clear days are unchanged
Rain, snow, the Mistlands after dark darker again
Light kit, standing still in the open spotted from roughly half as far away on a clear night as at noon
Heavy armour still easy to see at any hour β€” armour adds more to being seen than daylight does

πŸ”₯ Torches, honestly β€” only the sun and the moon count; a torch in your hand is one more item you carry Β· ↩ Made your nights stealthier by hand? Try the default shade and weather settings again first.


BEFORE YOU INSTALL

If… Then…
🀝 you play on a server install it on the server and every client. A client without it cannot connect β€” on purpose: a client that works out perception differently is playing a different game.
πŸ”’ you run a server leave Lock Configuration on. Off, any connected client can broadcast its own gameplay settings to everyone.
🎽 you add an armour mod later set RebuildArmorData = true and launch once, so the new gear is scored.
β†ͺ you are upgrading nothing to do: the config migrates itself and backs up to .cfg.bak first, and the armour catalogue keeps every entry you marked "Protected": true.
πŸ§ͺ you suspect this mod of something EnableStealthOverride = false makes the game byte-for-byte vanilla. It is the first thing to try.

I

The dark is measured, not looked up β€” and so is everything you hide in.

🌞 Sun and moon your visibility starts from the light actually falling on you: full daylight most, a clear night well under half, a crypt nothing
πŸͺ¨ Shade a wall, a rock face or a roof between you and the sun or moon takes more off
🌧️ Weather rain and snow cut how far a creature can see you; fog cuts it harder
🌿 Bushes and tall grass sampled from the real plants at your feet, not from the biome on the map
🧎 Crouch helps everywhere, and far more with something to crouch in
🦎 Camouflage ghillie, forest, swamp, wolf, lox and mist-themed gear hides you where it matches the biome
πŸƒ Movement adds to being seen by your actual speed β€” walking is not sprinting
πŸ“– Read the saga β€” why a bush in the rain at midnight is a different world

None of this is a biome lookup or a time-of-day switch. It is the game's own sun and moon, exactly as bright as the hour and the weather make them. Torches, fires and forges are not counted.

The cover terms stack. Crouched, in grass, inside a bush is a genuinely different proposition from crouched on bare rock β€” and a bush is the strongest single piece of cover in the game.

Heavy gear adds to how visible you are, scaled by how heavy the item is, so plate costs far more than leather. Camouflage gear adds concealment of its own on top of matching the biome.


II

Plate is loud. Leather is quiet. Foul weather is the sneak-thief's friend twice over.

πŸ”Š Moving makes noise, scaled by how fast you go
πŸ›‘οΈ Armour adds noise by weight β€” and heavy metal adds to being seen as well
🧎 Crouching removes most of it
🌧️ Rain, snow and mist mask a good part of what is left
πŸ‘‚ Hearing all round, through walls β€” a creature that cannot possibly see you can still hear you blunder past in iron
πŸ“œ The armour file every wearable item is scored on first launch by weight and material, modded armour included, into a JSON file you can edit by hand
πŸ“– Read the saga β€” how noise adds up, and how the armour gets its score

Noise. Movement and armour make it; crouching and weather each remove a fraction of it. They multiply, so crouching in the rain makes you quiet, never perfectly silent β€” unless you set crouching itself to remove everything.

Armour is a trade. Every item you can wear is classified by name into a weight and a material. Heavy metal is loud and visible, leather quieter, cloth barely registers. Gear named for ghillie, forest, swamp, wolf, lox or mist grants camouflage when it matches the biome you stand in. Monster attacks β€” which the game counts as equipable items β€” are left out: only things a player can pick up are scored.

When it gets one wrong. The classifier reads names, and names mislead. Correct the entry and mark it "Protected": true, and your answer survives every rebuild and every update (see The Toolbox).


III

A real cone, a real blind spot, a real line of sight.

πŸ‘οΈ Vision cone 120Β° by default, with a blind spot behind β€” for a creature that has not noticed you yet
πŸšͺ Line of sight cast against the same layer mask vanilla uses: a shut door works, a rock works
πŸ” It keeps working an alerted creature re-checks about twice a second, so going indoors and shutting the door really loses it
🌫️ Mist only creatures with mist vision see through it
βš”οΈ Mid-fight once alerted to you it sees all round, as vanilla creatures do; one fighting you sees you up close whatever the light
πŸ“– Read the saga β€” the blind spot is for sneaking, not for fighting

Stepping past a creature's shoulder in a fight does not make you vanish: once it is alerted to you, the cone is gone. And a creature that is actually fighting you sees you within a few metres of its body whatever the light, cover or camouflage, so fights in the dark do not go quiet β€” darkness hides you from the hunt, not from the fight.

Walls, shut doors and mist still block it at any range, and it never sees you where a vanilla creature could not. A creature fighting one player still has its blind spot for anyone else sneaking up behind it, and one that is merely searching can still be hidden from.

The re-check is a rate, never a skip: an alerted creature always loses you when you shut a door; the setting only decides how quickly it notices.


IV

Unaware β†’ Suspicious β†’ Alerted β†’ Engaged.

🧠 Gain and decay apart a single glimpse does not commit a mob; ducking behind one rock does not end a chase
πŸ”Ž Search it walks to where it last saw you and searches at a fraction of its own chase pace, then gives up on a timer
πŸ“― Calls for help alerted creatures call nearby allies, who come without being handed a target they have no business knowing
πŸƒ Flees when losing a badly hurt creature turns and runs β€” at a fraction of its run speed, so it can still be chased down
πŸ‘‘ Bosses never flee nor do Blighted World Heart's sub-bosses
πŸ“– Read the saga β€” what the rungs drive, and how a creature runs

The alert state is the game's own. The aggro roar, the alert animation, the give-up timer, taming, Sneak XP, the enemy icon on every client and the backstab bonus all follow it. Backstab lands only on a creature that has not been alerted β€” as it should.

Searching goes through vanilla's own pathfinder, so a creature does not beeline into walls. Fleeing goes through vanilla's flee behaviour: the creature picks a reachable spot away from water and lava, turns in place, and runs β€” never gliding backwards while staring at you.

Memory survives a handover. A creature's detection state goes with it when it passes from one player's game to another's, so it does not forget who it was chasing when you cross a zone boundary.


V

Walk up on a deer instead of watching the whole meadow scatter.

🦌 Deer, hares, chickens, boar piglets, wolf cubs, lox calves the same light, noise, cover, camouflage and line of sight as everything else
πŸƒ Flee or not that decision stays vanilla's β€” an animal has no target to be handed and no ally to call
😌 Settles at once break cover properly and it calms on the spot, instead of bolting out vanilla's four-second timer
πŸ“– Read the saga β€” when an animal is allowed to calm down

Vanilla's calm-down is a timer, not a re-check, so an alarmed animal keeps fleeing for four full seconds after it has lost you. Shadows of Midgard drops the alarm the same moment β€” but only when it is sure: the measurement is fresh, sight and hearing are both negative, the detection ramp is at zero, and the animal was never escalated past Suspicious. An animal you hit is not an animal that has lost you, and is left alone.

This can only ever make an animal less aware than vanilla would. DriveAnimals switches the whole animal half off.


VI

Built for a full server, not a single hero.

πŸ‘₯ A track per player each creature keeps one per nearby player, admitted by distance β€” so ten players do not give every creature ten tracks
πŸ’€ Death does not launder tracks follow your stable player identity: respawning neither wipes what a creature knows nor gets you out of a chase
πŸ–₯️ Dedicated servers the whole engine runs headless
πŸ“Š Stealth stats the server writes each player's sneak time and distance, times seen and heard, alerts caused, engagements and undetected kills, once a minute
πŸ“– Read the saga β€” the stealth stats file

The file is config/ShadowsOfMidgard/barrkbot_stealth.json, in a shape a Discord bot β€” or anything else that reads JSON β€” can use. The counters cover the current server session and reset when it restarts.

It costs nothing on the wire you were not already paying: each player's counters ride on their own replicated data, with no new messages. TelemetryExport turns the counting and the file off together; a file already written is left behind, so delete it by hand if you opt out for good.


VII

Two widgets inside Valheim's own HUD that tell you how you are doing.

πŸ‘οΈ The eye its colour is how aware the world is of you; its size is how visible you are right now, after cover and camouflage
🎨 Its colours pale ice blue: unnoticed · dark red and pulsing: something has you
πŸ“Ά The bar how much noise you are making
βœ‹ Drag them grab either while a menu has freed the cursor β€” inventory, chest, map, pause β€” and the spot is remembered
πŸ–±οΈ Never in the way they never block or swallow a mouse click, in menus or in play
πŸ“– Read the saga β€” how they live in the HUD

They scale with Valheim's HUD, hide with it in cutscenes and when you hide the interface, and go with it on world exit. They anchor to the bottom-left corner. AllowDrag off locks them where they are; the four position settings are always editable by hand. EnableStealthUI off hides both β€” the simulation carries on regardless.


VIII

A frozen public surface β€” and anything that wants to win by priority, wins.

🚫 Exempt a creature set the ZDO key SoMStealthExempt to 1; vanilla AI runs untouched, and every patch checks it
πŸ” Read what it knows SoMInterop, no reflection; the SoM_Ack key proves Shadows of Midgard actually reached that creature
πŸŽ›οΈ Per-creature directives ZDO keys for a sense-range multiplier, force alert, ignore line of sight, or a named AI profile
🧩 Patching alongside Harmony id wubarrk.shadowsofmidgard; sense patches are prefix-only, run at Priority.Low and honour __runOriginal
πŸ“– Read the saga β€” the details for your own mod

These names, signatures and meanings do not change between releases. The full guide is MIGRATION-for-mod-authors.md, in this package.

nview.GetZDO().Set("SoMStealthExempt".GetStableHashCode(), 1);   // owner-side
bool exempt = ShadowsOfMidgard.StealthExemption.IsExempt(character);

SoM_Ack lets you tell "Shadows of Midgard says unaware" apart from "Shadows of Midgard never reached this creature". AwarenessSystem.GetData(Character) keeps its original signature and never returns null. The Harmony id has never changed, so existing HarmonyBefore / HarmonyAfter attributes still resolve.

No forced priorities, no double-asserting postfixes, no reflection into vanilla. It does not patch BaseAI.IsAlerted, MonsterAI.UpdateTarget, Character.Damage or Character.OnDamaged.


THE TOOLBOX

First ten minutes: install on the server and every client β†’ launch once (the armour catalogue is written, first launch only) β†’ wait for dark β†’ crouch β†’ go and look at a greydwarf, and watch the eye.

File
BepInEx/config/wubarrk.shadowsofmidgard.cfg every setting
BepInEx/config/SoM_ArmorData.json the armour catalogue β€” yours to correct
config/ShadowsOfMidgard/barrkbot_stealth.json the server's stealth stats
πŸ₯· Actually sneak past something

Roughly in order of how much each one moves the needle:

  • Get off the metal. Heavy gear adds more to being seen than daylight does, and plate is loud too; leather and cloth barely register.
  • Crouch. It cuts noise by 60% and adds concealment on top.
  • Wait for dark, and keep out of the sun or moon. A clear night adds well under half the light of noon, and shade takes more off.
  • Use the weather. Rain, snow and fog cut sight; rain and snow mask sound.
  • Break line of sight, then keep it broken. A wall works even against something already hunting you β€” but it re-checks about twice a second, so a doorway you dart straight back across buys nothing.
  • Watch the angle. Behind a creature that has not noticed you is genuinely behind it. Once it is alerted to you, it sees all round.
🎽 Fix a mis-scored piece of armour

The classifier matches whole words, so a modded chestpiece called Werewolfmail will not match wolf. Open BepInEx/config/SoM_ArmorData.json, find the entry, fix Weight / Material / Camo, and add "Protected": true. That answer is now permanent: it survives a rebuild, a migration and every future update.

To re-derive everything else after installing an armour mod, set RebuildArmorData = true and launch once. It backs the file up, rebuilds, keeps every protected entry, and switches itself back off.

πŸ–₯️ Set up a dedicated server

Put the DLL in the server's BepInEx/plugins/ as well as every client's. Launch the server once to make its config, then edit the server's copy β€” every πŸ”’ setting below is pushed from there to all clients and locked to admins. There is no local player on a dedicated server, and nothing in the engine looks for one.

πŸ› Read the log when something is wrong

A healthy launch is two lines: [SoM/VanillaBind] … vanilla capabilities bound, every gate open and [SoM/Patches] … applied, none failed. If a game update moves something out from under the mod, those lines change and the full table prints by itself β€” you never have to switch anything on to see a failure. ShowPatchSaga = true prints the whole table on a healthy launch too.

Two failure states are designed, not crashes:

  • READ-ONLY β€” something the perception layer depends on did not bind, so the mod installed nothing and the game runs vanilla. The table names the exact member that changed.
  • DEGRADED β€” something failed after startup. The mod stops holding opinions about creatures and hands perception back to vanilla rather than guessing.

MULTIPLAYER

Single player The server Every player
works on its own runs the whole engine headless, and pushes its settings to every client, locked to admins needs it installed β€” a client without it cannot connect

⚠️ The settings are the server's. Everything that changes what a creature perceives is synced, so every peer agrees on it. Only the HUD and the debug switches are yours.


COMPATIBILITY

Mod
Other AI mods It yields on priority and never double-asserts. If something conflicts, ShowPatchSaga shows exactly what it installed.
Armour mods Classified automatically. Set RebuildArmorData once after installing one.
Creature mods Anything built on MonsterAI or AnimalAI is picked up automatically, spawned or already in the world. Any other AI gets the perception model and nothing touches its behaviour.
Mists of Avalor Β· Dvergr Allies Need no changes. Both can read Shadows of Midgard through SoMInterop.
Blighted World Heart Its sub-bosses are never ordered to flee.

SETTINGS

All safe to leave alone. Configuration Manager in game β€” synced values push live, no restart β€” or BepInEx/config/wubarrk.shadowsofmidgard.cfg. The file migrates itself: when a setting's meaning changes, only that one is reset, the file is backed up to .cfg.bak first, and keys that no longer exist are tidied away.

πŸ”’ Server-synced β€” set on the server, the same for everyone
Section Setting What it does
0 - General Lock Configuration leave it on β€” only admins change synced settings
1 - Systems EnableStealthOverride the master switch; off is byte-for-byte vanilla
1 - Systems EnableVisibilitySystem Β· EnableNoiseSystem Β· EnableHidingSystem Β· EnableCamoSystem each part of the model on its own switch
1 - Systems DriveAnimals the stealth model for deer, hares and the rest
1 - Systems TelemetryExport the stealth stats file
2 - StealthBrain VisionThreshold Β· HearingThreshold how visible or loud you must be to be seen or heard (0.25 Β· 0.15)
2 - StealthBrain SuspicionGain Β· AlertGain Β· DetectionDecay how fast detection rises when heard, rises when seen, and fades (2.5 Β· 3.0 Β· 0.5 per second)
2 - StealthBrain SearchDuration Β· GiveUpTime seconds searching your last spot, and before giving up entirely (4 Β· 8)
2 - StealthBrain SearchSpeedFactor Β· FleeSpeedFactor search and retreat pace, as a fraction of the creature's own speed (0.5 Β· 0.6)
2 - StealthBrain AlertedLosRecheckInterval how often an alerted creature re-checks line of sight (0.5 s)
3 - Visibility LightPenalty Β· ShadowBonus Β· GrassBonus sun or moon, shade, crouching in grass (0.35 Β· 0.25 Β· 0.20)
3 - Visibility MovementPenalty Β· ArmorVisibility moving and heavy gear (0.8 Β· 0.6)
3 - Visibility WeatherVisReduction Β· FogVisReduction rain or snow, and fog (0.20 Β· 0.30)
4 - Noise MovementNoise Β· ArmorNoise noise from moving and from heavy armour (0.7 Β· 0.6)
4 - Noise CrouchNoiseReduction Β· WeatherNoiseReduction fraction removed by crouching and by weather (0.6 Β· 0.4)
5 - Hiding BushBonus Β· GrassBonus Β· CrouchBonus inside a bush, crouched in tall grass, crouched anywhere (0.4 Β· 0.25 Β· 0.15)
6 - Camo BiomeCamo Β· ArmorCamo gear matching the biome, camouflaging armour (0.15 Β· 0.10)
7 - Awareness SuspiciousThreshold Β· AlertedThreshold Β· EngagedThreshold the ladder β€” keep them in ascending order (0.15 Β· 0.40 Β· 0.75)
8 - Detection Ranges MaxVisualRange Β· MaxHearingRange the furthest a creature can see or hear you (40 m Β· 25 m)
8 - Detection Ranges VisionConeHalfAngle half the cone of a creature not yet alerted to you; 60 is a 120Β° cone, 180 removes the blind spot
8 - Detection Ranges CloseQuartersRange how close a creature fighting you sees you whatever the light (4 m; 0 is off)
8 - Detection Ranges MaxTrackedPlayersPerCreature how many players one creature remembers at once β€” the main cost lever on a busy server (10)
πŸ–₯️ Local to your game β€” yours alone
Section Settings
UI EnableStealthUI Β· EyePosX Β· EyePosY Β· NoisePosX Β· NoisePosY Β· AllowDrag
9 - Debug EnableVisionDebug Β· EnableAIDebug Β· ShowPatchSaga Β· DebugLogInterval Β· RebuildArmorData Β· LogPerformance

The debug switches are all off by default and loud when on. RebuildArmorData is one-shot and switches itself back off. Failures are always reported in full, whatever ShowPatchSaga says.


CREDITS

Needs: BepInExPack Valheim 5.4.2360 β€” and nothing else. No Jotunn, no JSON library, no shared framework: one DLL.

Install with a mod manager, or unzip into BepInEx/plugins β€” on the server and on every client.

πŸ”— Also by me: TortalPortal, a complete portal overhaul, and Mists of Avalor.

Made by Wubarrk β€” ravenirongames.com. Bug reports and abuse welcome on Discord. License: MIT β€” use, modify and redistribute freely, including commercially; keep the copyright notice. Full text in LICENSE.md.

CHANGELOG

Changelog

All notable changes to Shadows of Midgard.

A note on the history below. 2.0.0 is the first public release. Everything under it is internal development history, kept because it is an honest record of what the mod was and what was wrong with it β€” but none of those version numbers ever reached a registry, and nobody has ever run them but the author.

The project was also not under version control for most of that time, so the middle of the line cannot be attributed to specific builds. Those entries are grouped by theme rather than invented as individual versions. Entries are newest first.


2.0.11 β€” One Page For Every Store

  • The store page was rewritten to the new layout and is now the same on every store, and the website link now goes to https://ravenirongames.com/. No change to the mod: the DLL is the same as 2.0.10.

2.0.10 β€” Rebuilt for Valheim 1.0.21

  • Rebuilt and checked against Valheim 1.0.21 (network version 41). Nothing Shadows of Midgard patches or reads from the game changed in this update, so there are no gameplay changes.
  • Website link moved to https://www.rossgilsonlabs.dev.
  • Now depends on BepInExPack 5.4.2360.

2.0.9 β€” The Night Counts

Fixed β€” a clear night hid you no better than noon

  • Night now hides you. Shadows of Midgard judged how bright it was by the strength of the sun or moon light alone. Valheim does not make night by turning that light down: it turns the same light into the moon and gives it a dark blue colour, and in clear weather the moon is set just as strong as the sun. So a clear midnight counted exactly as bright as noon, and a rainy night counted brighter than a rainy day. The light's colour now counts as well, the same way the game's own stealth check measures light. Clear days are unchanged. A clear moonlit night now adds well under half the light of noon, and rain, snow and the Mistlands are darker again after dark. In light kit, a player standing still in the open is now spotted from roughly half as far away on a clear night as at noon; before, it was the same distance.

  • It matters most in light gear. Armour adds more to how visible you are than daylight does, so a player in heavy armour is still easy to see at any hour.

Fixed β€” the README promised torches mattered

  • Shadows of Midgard has never measured light from torches, fires or forges, only from the sun and the moon, but the README said a held torch was the main reason you got spotted. The README and the LightPenalty description now say what the mod actually does: a torch in your hand counts only as one more item you are carrying.

No setting changed meaning. If you strengthened the shade or weather settings to make nights stealthier, try the defaults again first.

2.0.8 β€” Creatures Fight Back In The Dark

Fixed β€” creatures stopped attacking mid-fight and walked circles around you

  • An alerted creature no longer goes blind when you step past its shoulder. Every creature kept its 120Β° vision cone even while fighting you, so stepping to its side or behind it made it lose sight of you β€” and a Valheim creature will not swing at something it cannot see. It stopped attacking, wandered in circles around the spot where it last saw you, and soon gave up and walked away. The cone now applies only until a creature has been alerted to you; after that it sees all round, exactly as vanilla creatures do. A creature fighting one player still has its blind spot for anyone else sneaking up behind it.

  • Darkness no longer hides you from something you are already fighting. At night, in shade or in rain, your visibility could drop below the sighting threshold even face to face, with the same result. The new setting CloseQuartersRange (section 8 - Detection Ranges, default 4 m, server-synced) fixes this: once a creature is fighting you, it sees you within that distance of its body whatever the light, cover or camouflage. Walls, shut doors and mist still block it. It never sees you where a vanilla creature could not, and it only applies to a creature that already has you, so one that is merely searching can still be hidden from. Set it to 0 to turn it off.

Sneaking is otherwise unchanged: a creature that has not noticed you still has its cone, and light, cover, camouflage and noise work exactly as before. If you widened VisionConeHalfAngle or softened the shadow and weather settings to stop creatures losing you mid-fight, you can put them back.

2.0.7 β€” Valheim 1.0.16

Changed

  • Rebuilt for Valheim 1.0.16. Rebuilt against the 1.0.16 game assemblies (client Steam build 25527674 / dedicated server build 25527701, network version 40 β€” save formats unchanged) and BepInExPack Valheim 5.4.2350, unchanged. 1.0.16 removes only ObjectDB.GetAllFoodItems() and ButtonSfx.Start() from the assembly SoM compiles against, and SoM calls neither, so there is no source change and no behaviour change. Verified with a static reference check against both the 1.0.16 client and dedicated-server Managed folders (3,475 references, 23 Harmony targets, all resolved) and a headless boot on a real 1.0.16 Linux dedicated server, where the log reads [SoM/VanillaBind] 27/27 vanilla capabilities bound, every gate open and [SoM/Patches] 11 applied, none failed.

2.0.6 β€” Valheim 1.0.7

Changed

  • Built for Valheim 1.0.7. Rebuilt against the 1.0.7 game assemblies (client build 25185596 / server build 25185644, network version 39) and BepInExPack Valheim 5.4.2350, which the package now lists as its dependency in place of 5.4.2333. All eleven patch targets and all 27 capability rows in VanillaBind were checked against the 1.0.7 decompile: every one is present with the same signature, so there is no source change and no behaviour change. Verified on a real 1.0.7 Linux dedicated server, where the log reads 27/27 vanilla capabilities bound, every gate open and 11 applied, none failed.

2.0.5 β€” Fleeing Creatures Turn And Run, And Bosses Never Flee

Fixed β€” the moonwalk

  • A creature ordered to retreat no longer glides away backwards while staring at you. The retreat used to write the movement direction directly, and in Valheim a monster's body only ever rotates toward its look direction β€” which nothing was updating. The body stayed locked facing the player while the velocity dragged it away, and the animator, seeing motion opposite the facing, played the backpedal blend at speed: the moonwalk. Retreats are now routed through vanilla's own flee behaviour β€” the creature picks a reachable flee point (water and lava avoided), turns in place first, and then runs, exactly like a vanilla animal bolting.

  • Fleeing creatures actually sprint now β€” and are then deliberately slowed. The old code set a run flag vanilla recomputes and discards every physics frame, so a "sprinting" retreat quietly jogged. With the real run gait engaged, the new FleeSpeedFactor (section 2 - StealthBrain, default 0.6, server-synced) scales the retreat to a fraction of the creature's own run speed, so a wounded creature runs away convincingly but can still be chased down. Relative, like SearchSpeedFactor, so fast creatures still flee faster than slow ones.

Fixed β€” bosses and Blighted World Heart sub-bosses never flee

  • Bosses do not run. Vanilla bosses never flee only because their prefabs ship without flee parameters; SoM's health-based retreat logic didn't know that convention and would order a boss into a strategic retreat below half health. Any creature flagged as a boss is now refused by the flee evaluator outright.

  • Blighted World Heart sub-bosses are refused too, even though BWH deliberately clears the vanilla boss flag on them (to stop the native boss bar crashing): SoM recognises their marker component directly. BWH itself now also stamps its sub-bosses with SoM's networked stealth exemption at spawn, which removes them from SoM entirely on every peer β€” including a peer the sub-boss migrates to mid-fight, where the marker component does not exist. (That half ships with the next BlightedHeart build; the SoM-side refusal works on its own for the common case.)


2.0.4 β€” Stealth Telemetry, and a Draggable HUD Again

Added β€” the server now publishes per-player stealth statistics

  • A new export file, config/ShadowsOfMidgard/barrkbot_stealth.json, written by the server once a minute for the BarrkBOT Discord bot's generic scanner. Per player: sneak time and distance, times seen, times heard, alerts caused, engagements, and undetected kills β€” see BARRKBOT_CONTRACT.md in the source repo for the exact shape and the rules about what may never be renamed or summed. Totals cover the current server session; a restart resets them.

    Each client counts what only it can observe (its own player's pose; detection events on the creatures it owns) and publishes the counters as one rarely-written field on its own player's ZDO, riding replication that is already being paid for β€” no new RPC, no extra network surface. The server merges every reporter's counters per player and writes the file atomically (temp-then-rename), so the bot can never read a torn half-file as truth.

  • TelemetryExport (section 1 - Systems, default on, server-synced) turns the counting and the file writes off together. An already-written file is deliberately left on disk β€” delete it by hand if you opt out permanently, because a stale file that still parses reads as live data.

  • Nothing about gameplay changes: the counters observe the existing evaluation results and add no perception, no allocation on the hot paths, and no writes to any creature.

Fixed β€” the eye and the bar can be dragged again

  • The HUD widgets are draggable out of the box once more β€” in every menu, not just some. Two things were wrong, stacked. 2.0.1 put dragging behind AllowDrag, default off, so by default the widgets never accepted pointer input at all and the HUD read as stuck. And the machinery itself was fragile even with the switch on: it was built on Unity's drag events, and whether those ever reach a widget living inside the vanilla HUD depends on which GUI is open above it in the canvas stack β€” in testing the same build dragged fine with the pause menu up and did nothing minutes earlier in the same session. A drag that works in one menu and silently dies in another reads as haunted. (v1 sidestepped this by building its own canvas with its own raycaster β€” the same construction whose duplicate EventSystem caused the swallowed-clicks bug.)

    Dragging is now read straight from the mouse instead, so it depends on nothing being delivered: grab either widget while any menu has freed the cursor β€” inventory, container, map, pause β€” move it, release, and the position is saved. The widgets are no longer raycast targets at all, so they can never block or swallow a click, during play or in menus. The trade-off that justified shipping the switch off no longer exists.

    AllowDrag survives as a position lock, default on; turn it off to pin the HUD in place. Existing config files are migrated (schema v4): a stored false was the old default rather than a choice, so it is reset once, and a false you set from now on stays.

2.0.1 β€” Animals, and Four Things the First Testers Found

The first release shaped by somebody other than the author playing it. Everything below except the animal feature was found in a single external test session, and two of the four had been silently broken since the mod was written.

Added β€” the stealth model now covers animals

  • Deer, hares, chickens, boar piglets, wolf cubs, lox calves and the greydwarf gem obey stealth. These use Valheim's AnimalAI rather than MonsterAI, and every build of Shadows of Midgard before this one refused to register them β€” so a deer bolted from a player who was crouched, unlit, downwind and completely hidden. The most stealth-shaped creatures in the game were the only ones the stealth mod ignored.

    Animals reach the same sensing model as everything else: the same visibility, noise, hiding and camouflage terms, the same detection ramp, the same line-of-sight cast. What they do not get is the behaviour layer, and that is deliberate β€” an animal has no target to be handed, no ally to call and no attack to suppress. It flees or it does not. Driving one with the monster act layer would be inventing behaviour vanilla does not have.

  • A fleeing animal now settles the moment it loses you, not four seconds later. Vanilla's calm-down is a timer, not a re-check: once alerted, an animal keeps its target for a full m_timeToSafe (4 s) and flees the whole time, with no range test and no line-of-sight test. In a stealth mod that is the entire experience β€” you break line of sight, the model correctly says the deer has lost you, and the deer keeps bolting anyway. It now drops the target on the same tick, but only when SoM is confident: the measurement must be fresh, sight and hearing must both be negative, the detection ramp must be at zero, and the animal must not have been escalated past Suspicious. Anything else is left to vanilla.

    This can only ever make an animal less aware than vanilla would have made it. Nothing in the animal path ever gives a creature a target it did not already have.

  • DriveAnimals (section 1 - Systems, default on, server-synced) turns the whole animal half off and restores the older behaviour.

Fixed β€” the noise channel was dead, and had always been

  • Your noise was mathematically pinned at zero whenever you crouched in any weather. The two reductions subtracted: noise = movement + armor - crouch - weather. With the shipped defaults crouch = 0.6 and weather = 0.4 sum to exactly 1.0, which is exactly the ceiling of the two additive terms β€” so crouching in rain produced silence no matter what you did. Sprinting. In full metal. Carrying everything you own. Zero.

    The test log settles it: 243 of 243 sampled ticks read noise=0.00, with the sub-terms sitting at move=0.31 armor=0.57 crouch=0.60 weather=0.40. The hearing half of detection could not fire once in an entire session, and the HUD's noise bar never left its floor β€” which is how it was reported: "the bar is stuck."

    The reductions now attenuate: noise = (movement + armor) Γ— (1 - crouch) Γ— (1 - weather). The config file already described it this way β€” both entries read "Fraction of your noise removed…", and a fraction of a total is a multiplication. No setting changed meaning and no description had to be rewritten; the arithmetic was simply not doing what the file said.

Fixed β€” the eye could not survive its own tint

  • The gem was invisible until something noticed you. eye.png shipped as a 40Γ—40 stone tile with every pixel opaque and an average luminance of 103/255. Unity's Image.color is a multiply, so tinting that by Color.blue β€” the Unaware colour β€” produced a near-black square in the corner of a dark HUD. It only appeared when the tint changed to yellow or red. Reported as "the eye comes and disappears with detection", and that is exactly what it did.

    The icon is now alpha-masked to a disc with its levels lifted, and no rung of the colour ramp is a fully saturated primary any more β€” every one keeps a floor in all three channels so the artwork reads as itself. Unaware is a pale ice blue. The original flat asset is kept in the source tree.

  • The HUD widgets no longer swallow clicks. Both were raycast targets so they could be dragged, which meant they quietly intercepted pointer input in their patch of screen whenever an inventory or container was open. They bought nothing for it: Valheim locks the hardware cursor during play, so a drag can only ever be delivered while a menu is already open anyway. Dragging is now behind AllowDrag (section UI, default off); the four position values are editable by hand either way. Dragging also used to fight the pointer β€” the widget re-asserted its configured position every frame while you were moving it β€” which is fixed regardless of the switch.

  • The noise meter's backing plate is darker and nearly opaque, so the fill has something to read against and the border is visible. The gem no longer pops when a creature disengages.

Fixed β€” the armour catalogue could not be saved or trusted

  • The armour file was being written empty. A save of 663 classified items reached a tester's disk as a 24-byte stub containing nothing but {"DataVersion": 3} β€” the entire Armors array absent, with no exception and no warning, while the log cheerfully reported success. Every "Protected": true a user sets would have been lost on the next launch, and the whole catalogue re-derived from scratch every time.

    The file is now composed and parsed by Shadows of Midgard itself instead of by UnityEngine.JsonUtility, the save is verified by reading it back before it is reported as written, and a save that would replace a good file with an empty one is refused outright. A broken hand edit now reports where β€” the character offset and the character it did not expect β€” instead of a bare "could not read".

  • Silver gear was classified as cloth. So was most black metal. The material matcher looked for whole words only, and vanilla is inconsistent about its own capitalisation: AxeBlackMetal splits into black + metal and scored as Metal, while SwordBlackmetal, AtgeirBlackmetal, BattleaxeBlackmetal and ShieldBlackmetal do not split and scored as Cloth β€” the quietest material in the table. Four of the game's seven black-metal items. And nothing ever matched "silver" at all, in any version of this mod, so mountain-tier silver gear was five times quieter than the bronze it replaced.

    Fused compounds now match at a word boundary (with a three-character floor, so the false positives whole-word matching was introduced to kill β€” "hide" inside "Hidden", "root" inside "Rooted" β€” stay dead). silver and steel joined the metal list; fur and pelt joined the leather list. The armour data version is bumped to 4, so every catalogue re-derives once on the next launch. Entries you marked "Protected": true survive, as they survive everything.

Fixed β€” two of the four sensing terms had never been logged

  • Hiding and Camo were computed correctly and printed nowhere. Visibility and noise logged themselves; the method that was supposed to print all four had no caller anywhere in the mod. The eye's size is driven by Visibility Γ— (1 - Hiding) Γ— (1 - Camo), so a bug report about the eye could not be checked against two of the three numbers behind it. All four now print together.

2.0.0 β€” The First Release

The first build of Shadows of Midgard that has been run in Valheim, and the first that works. The stealth model is the one described in the README and has been stable for a long time; what changed here is that the engine underneath it can now actually reach the game.

Fixed β€” the one that mattered

  • The mod could not touch a single game object, and said nothing about it. Every direct read of a vanilla field threw FieldAccessException at runtime β€” Character.m_nview, BaseAI.m_character and everything reached through them. The player roster came up empty, the creature registry came up empty, and the discovery sweep died on its first pass. With no records to answer from, all ten Harmony patches correctly reported "no opinion" and fell through to vanilla.

    The result was the worst possible failure shape: a flawless startup log β€” every capability bound, every patch applied, no errors β€” attached to a mod that did nothing whatsoever. Cause: the project compiled against a pre-publicized copy of the game assembly, which satisfies the compiler but not the runtime. The missing half was [assembly: IgnoresAccessChecksTo("assembly_valheim")], which only the MSBuild publicizer emits. The build now publicizes the stock assembly at build time, like every other mod in the family.

Fixed β€” features that had never once run

Three subsystems were fully implemented, documented, and wired to nothing. Each failed silently, because the mod's entire design philosophy is "hold no opinion and let vanilla decide", and a component that is never called holds no opinion very convincingly.

  • Armour never affected anything. The classifier existed and the data store existed, but nothing ever triggered the classification pass, so the catalogue was permanently empty and every item scored as uncatalogued β€” which correctly contributes zero. Plate was exactly as stealthy as rags. Two triggers added, at ZNetScene.Awake and Player.OnSpawned.
  • Nothing ever called for help. Ally calls were queued into a ring buffer that had no drain. After the first few requests the buffer was permanently full and silently refused everything after. Now drained once per frame by the scheduler.
  • SoM_Ack was never written on any creature. The replication seam called only the digest half of the write, skipping the acknowledgement entirely β€” so Mists of Avalor and Dvergr Allies read ack == 0 and concluded SoM was not installed. Exactly the silent failure that key exists to prevent.

Fixed β€” packaging and tooling

  • The deploy script pointed at a profile that did not exist, warned about it in yellow, and then printed a success banner anyway. Every deploy since the test profile was renamed copied nothing while appearing to work. An explicit deploy that cannot happen is now a hard failure.

Changed β€” the startup log

  • A healthy launch is two lines instead of thirty-seven. The capability probe used to print all twenty-five rows on every launch, each ending in a word like Fatal or FeatureOff β€” the impact if that row ever failed, printed on rows that had passed. Under a header that mentioned failures, a clean run read convincingly as a total collapse. The table now prints only when it has something to say, and its header states outright that nothing failed when nothing did.
  • Any failure still prints everything, at its own severity, with no configuration required. Which rows failed together is the diagnostic, so a broken launch is now louder relative to the noise around it, not quieter.
  • ShowPatchSaga accordingly changed meaning β€” it now forces both full tables on a healthy launch β€” and defaults to off. Config schema migrates to v3 and resets it; a stored true was the old default nobody had turned off, not consent to the new behaviour.

Changed β€” the armour catalogue

  • Roughly a fifth the size, and no longer full of things you cannot wear. The pass catalogued anything vanilla's IsEquipable() accepted, which includes every monster attack in the game β€” a fuling's club and an abomination's swing genuinely are equipable weapon items. A real world produced 1182 entries, mostly Abomination_attack1, troll_log_swing_h and blob_attack_aoe, along with 69 warnings about name collisions between pairs of monster attacks.

    Items must now also carry an icon, which is vanilla's own test for "a player can hold this" β€” Humanoid.Pickup refuses an iconless pickup for players. No stealth number moves: an item that cannot be picked up cannot be equipped, so it was never scored. Armour data migrates to v3, which drops the stale entries; entries marked "Protected": true survive, as always.

Changed β€” the config file

  • Dead settings are now removed instead of being rewritten forever. A config file accumulated keys from older layouts, and BepInEx faithfully wrote them back on every save. One test profile carried 36 of them across nine sections, sitting next to the live settings under near-identical names β€” so editing the file by hand was a coin flip between changing something and changing nothing.

    The migration system now asks the file what nothing binds, rather than keeping a list of names to delete. This is deliberately not a version step: it needs no changelog entry and no maintenance, and it cleans up layouts that predate the versioning scheme entirely.


1.9.9 β€” The Shadows Rewrite β€” internal

A near-total rewrite, and the direct ancestor of 2.0.0 above. The stealth model is the same one, ported formula for formula; the engine underneath it is new, and most of this list is about defects that had been present since 1.0 and were only found by rebuilding around them.

Never released, and never run in Valheim β€” which is how the three unwired subsystems and the publicizer fault listed under 2.0.0 survived a rewrite that was otherwise looking for exactly that kind of defect. Everything below was found by reading the code; everything under 2.0.0 was found by launching the game once.

Fixed β€” the ones you will actually notice

  • Alerted creatures can no longer see through walls. Alertness used to make a creature sense you unconditionally, before any geometry was consulted β€” so once something noticed you, going indoors and shutting the door did nothing. It now re-checks line of sight on a bounded interval and loses you properly.
  • The mod now works on dedicated servers. The whole engine opened with "find the local player, and if there isn't one, sleep for a second". On a dedicated server there is never a local player, so the mod did nothing at all there for the entire 1.x line.
  • Multiplayer is genuinely multiplayer. Creatures now track each nearby player separately. A creature can be hunting one player while completely unaware of another crouched beside it β€” previously a creature had one awareness value shared between everybody.
  • Your gem now shows how aware the world is of you. It used to show the highest alertness of any creature about anything within 64 m, so a wolf chasing another player, or a mob in a boar fight, turned your gem red while you were perfectly hidden.
  • Alert state replicates again. Vanilla's alert flag was frozen off, which quietly broke the aggro roar, the alert animation, boss counting, the give-up timer, taming, Sneak XP, the enemy HUD icon agreeing between clients β€” and the backstab bonus, which is gated on the target not being alerted and so had become a free multiplier.
  • Being hit registers. Both damage hooks were wrong: one ran on the attacker's machine at the moment the packet was sent, before the hit was validated; the other targeted a method that Humanoid overrides without chaining, so it never fired for greydwarves, draugr, fulings, skeletons, dvergr or trolls β€” most of the game. SoM now subscribes where vanilla itself does.
  • Calling for help works. It never had. A reflection binding named the wrong declaring type, threw into a swallowing catch in a static constructor, and left ally-calling silently inert for the whole 1.x line. All reflection into vanilla is gone; these are ordinary compiled calls now.
  • Your memory survives your death. Creature memory was keyed to an id that changes every time you respawn, so every death orphaned every creature's memory of you. It is now keyed to your stable player id.
  • The stealth HUD no longer breaks the game's mouse input. The mod created its own Unity EventSystem during startup β€” before Valheim had built its UI, so the "only if one doesn't already exist" guard always passed. When the game later created its own, Unity disabled one of them, and the mod's usually won. That is the swallowed-clicks bug. The HUD is now a child of the vanilla HUD and creates no canvas, scaler, raycaster or event system of its own.
  • The eye icon reflects hiding and camouflage. It was showing raw visibility, so crouching in a bush β€” the entire point of the mod β€” did not shrink it. A stray division also capped the icon at 95 % of its authored size, so "fully exposed" rendered smaller than the art.
  • The mod no longer writes files into its own plugin folder. Loading the eye icon created directories and wrote a PNG next to the DLL, which failed on read-only installs and made the folder differ from what the mod manager put there.
  • Non-MonsterAI creatures can attack again. The attack gate returned "blocked" to mean "no opinion", silently disarming every attacker the mod did not model.
  • Memory leaks closed. Creature records, awareness rows and a per-creature alert table were never cleared on world exit and accumulated for the life of the process.

Changed

  • Configuration is server-synced properly. The admin lock entry was missing, which left the lock permanently disabled and allowed any client to broadcast its own settings world-wide.
  • The TOML config now migrates across schema versions instead of being silently re-read under new meanings. It backs up to .cfg.bak first and only re-defaults keys whose meaning actually changed.
  • Armour data moved to a versioned SoM_ArmorData.json with hand-edited entries protected across rebuilds.
  • The HUD anchors to the bottom-left corner. It was anchored to the middle of the screen, so the default position put the eye on top of your crosshair. Your saved positions are kept and the widgets will move β€” drag them where you want them.
  • Engaged is now a darker red than Alerted. Both rungs used to be identical red, distinguishable only by the pulse.
  • Creatures are evaluated on a time budget, in distance tiers, from a single update β€” no coroutines anywhere in the mod.
  • Detection now integrates over real elapsed time per creature-player pair, so a distant creature does not accumulate detection ten times slower than a near one.
  • Sensing patches run at normal priority and honour other mods' decisions instead of forcing their own answer with maximum priority and a second pass. The Harmony id is unchanged, so existing HarmonyBefore / HarmonyAfter ordering from other mods still resolves.

Added

  • SoMInterop β€” a supported, reflection-free way for other mods to read what SoM thinks about a creature, plus a SoM_Ack acknowledgement so a mod can verify SoM actually reached it.
  • Per-creature directives β€” other mods (or a spawner) can set sense-range multipliers, force alert, ignore line of sight, or apply a named AI profile, per creature, via ZDO keys.
  • Replicated awareness β€” a creature's detection state now survives an ownership handover, so it does not forget who it was chasing when you walk across a zone boundary.
  • A capability probe at startup that checks every vanilla member SoM depends on and logs a table. If Valheim changes underneath the mod, you get one clear line at startup and a clean fall-back to vanilla instead of an exception storm mid-raid.
  • A read-only degraded mode. If the probe refuses, SoM loads, keeps its compatibility surfaces answering and its config live, and lets the game run vanilla perception.

Compatibility

  • Mists of Avalor and Dvergr Allies need no changes. The StealthExemption hook, the AwarenessSystem / AwarenessData surface and the Harmony id are all unchanged and frozen.
  • Both can now optionally drop their reflection probing in favour of SoMInterop. See MIGRATION-for-mod-authors.md.
  • AnimalAI (passive wildlife) remains out of scope.

The Stealth Overmind Update β€” internal

Previously numbered 2.0.0. Renumbered because that version was never released and the number now belongs to the first public build above.

  • Massive performance boost. AI stealth calculations decoupled from the main game loop and moved to a background engine, removing frame drops during large encounters.
  • Dynamic AI reaction rates based on distance to the player β€” immediate up close, cheaper far away.
  • ServerSync integration. Gameplay settings sync from host to clients.
  • StealthExemption hook β€” a generic ZDO-based opt-out letting other mods exempt individual creatures, falling back to pure vanilla AI. Absolute-enforcement AI patching.
  • Architectural overhaul of the Stealth Brain into modular components: State, Senses, Combat, Movement, Fleeing.
  • Stealth AI pathing fixes β€” suspicious slowdowns and alerted engagement wired into creature actions.
  • Stealth UI decoupled from the debug system onto its own EnableStealthUI setting.
  • Event-driven memory safety replacing periodic cleanup; dead enemies purged immediately.
  • Config consolidation into a single unified format.
  • Fixed eye.png failing to load due to Unity reflection changes, which made the gem vanish.

1.1.0 – 1.9.0 β€” the 1.x line

Grouped by theme. The project was not under version control during this period and these changes cannot honestly be attributed to individual releases.

The Stealth Brain

  • Replaced the original single detection value with a ten-phase decision engine: detect, evaluate alertness, assess health, decide flight, find allies, plan movement, choose combat tactics, coordinate, pick an action, finalise.
  • Split into single-responsibility evaluators β€” StateEvaluator, SensingEvaluator, CombatEvaluator, MovementEvaluator, FleeEvaluator.
  • Added the four-rung alertness ladder β€” Unaware, Suspicious, Alerted, Engaged β€” with configurable thresholds and separate gain and decay rates.

Sensing

  • Vision cones with configurable half-angle and range.
  • Line-of-sight raycasting against the same layer mask vanilla uses.
  • Light and shadow: standing in light raises visibility, shadow lowers it.
  • Weather and fog reductions to both sight and hearing.
  • Grass, bush and biome-based hiding and camouflage, sampled from real world vegetation.
  • A noise model β€” movement, armour weight, crouching, weather masking.
  • Armour classification driving both visibility and noise, with a generated catalogue covering modded armour.

Behaviour

  • Search and investigate behaviour: creatures move to your last known position through vanilla's own pathfinder instead of beelining into walls.
  • Search-pace slowdown so a searching creature does not sprint.
  • Health-based fleeing with a strategic-retreat tier.
  • Group coordination β€” a creature that spots you can alert nearby allies within a radius.
  • Give-up and memory decay timers.

Multiplayer and compatibility

  • Ownership checks so AI is only driven by the peer that owns the creature.
  • The StealthExemption ZDO hook and the AwarenessSystem / AwarenessData public surface that Mists of Avalor and Dvergr Allies build against.
  • Compatibility work with Mists of Avalor's own AI patches.

Interface

  • The stealth gem: an eye icon whose colour tracks alertness and whose size tracks visibility.
  • The noise meter with a green-to-red fill.
  • Draggable, position-persisting HUD elements.
  • A debug overlay and per-system debug logging.

1.0.0

  • Initial public release.