Vanilla Fix
Small fixes for overlooked vanilla quirks: camp fires that burn out before you arrive, fish that trigger item merging, crafting stations that protect only 20 m after loading, and more. Every module can be toggled.Vanilla Fix
Small fixes for quirks of vanilla Valheim that rarely get noticed but quietly take something away from the game. Every fix is a separate module and can be switched on or off in the config file.
This mod was written entirely by an AI (Claude, by Anthropic), directed and play-tested by its publisher. Every behaviour it changes was first traced in the game's own code.
What it fixes
In plain words first - the details for modders and the curious are further down, under "Technical details".
On by default
- Unload After Travel - After a portal trip or a long sea voyage, the textures and models of the place you left are freed from video memory. The game itself only does this when you sleep, die or pause, so a few trips to other biomes can leave it running slower until you restart.
- Camp Fires - The bonfires and fires of a Fuling camp no longer go out just as you arrive, and fires that had already burnt out are lit again - as long as Fulings still hold the camp. Once you clear the camp, its fires are left to burn down normally.
- Ground Item Merging - The fish swimming nearby no longer make the items you dropped snap together into stacks. The game merges dropped items once more than 200 of them are loaded, and it counts every fish in the sea as one.
- Station Range - Workbenches, forges and artisan tables keep monsters from spawning across their full range as soon as they load. In the vanilla game they protect only 20 m until you walk up to them, so monsters can appear inside your base right after you arrive.
- Tomb Exit - The exit of the Smouldering Tomb (Hildir's quest) works from inside again. Normally its "Exit" prompt only flickers up at the very edge of the opening.
- Fire Warmth - The fire icon, and its warmth, no longer follows you around after you leave a fire. It happens when rain dampens a fire while you stand next to it and you then walk away: once the rain stops, the game still thinks you are by that fire, wherever you are. The leftover is cleared within a second.
- Travel Pause - Meads, Rested, the Forsaken power and its cooldown, and poison or burning stop counting down while a portal trip or a dungeon loads. The game already pauses your food and regeneration during loading, but not these. With a high simulation distance or a slower computer a portal trip can take 20-30 seconds, every one of them taken off your buffs - and letting poison run out unnoticed.
- Multi-Craft Count - While you hold Shift to craft five at once, the recipe's title says "x5" for things that are made one at a time, too. The game shows the total only for recipes that make several (a recipe for 2 says "x10"), although the requirements and the craft button already count five either way. Display only.
- Message Below Inventory - While the inventory is open, the message at the top left of the screen ("Skill improved", "Picked up ...") appears just below the inventory instead of behind it. Crafting stations keep the inventory open, so you used to hear a skill go up while cooking or crafting without being able to read which one. Display only.
- Mine Landing - In the Infested Mines, the planks at one doorway of the cave room with the wooden bridge no longer open a way out of the dungeon. They are meant to stay, but become breakable once the dungeon has been loaded again, and the gap under them drops you out of the dungeon, which floats 5000 m above the world. The module keeps an invisible floor exactly where those planks belong.
- Sound Voices - Eases sound effects cutting in and out where many creatures call, a problem that grows with the Simulation distance setting and is worst on Extreme, as in the Infested Mines. The game plays at most 32 sounds at once, and far loops that nobody can hear - in the Infested Mines, the lanterns on the ground 5000 m below - hold most of those 32. The module stops sounds far beyond their range (loops start again as you come back) and lets the game play up to 64 sounds at once.
- Grapple Release - Attacks in the air no longer stop working after you have used the grappling hook. If you jump to be pulled up while hanging on the hook and the pull is cut short - you block, attack or switch weapons on the way - the game clears the pull but keeps you marked as hanging on the rope: from then on every attack you start in the air, jumping or falling, is cut off right after it begins, until you restart the game. The module clears that leftover the moment no hook point is left.
- Raid Music - When a raid starts while you stand in a place with its own music - a Fuling camp, a Dvergr ruin, a trader - that music gives way to the raid music the way the biome music already does: it fades out as the raid music comes, and once the raid music has faded out it comes back from where it was. The game plays both at once.
- Location Music Echo - In an Ashlands ruined city the same music no longer plays several times over, seconds apart. Such a city is many ruins side by side, and each plays the same track from the moment you first came near it, so walking through the city layered copy upon copy - and the copies keep playing after you leave, so coming back brought the echo back. Now the copy you heard first goes on, at the volume of the ruin you are nearest to, and never starts over; the others play silently in step with it. The same holds wherever places with the same music meet.
Off by default
- Wave Depth - Waves react to the land you raise or dig: filling in the sea calms them the way a natural shore does.
- Sea Exploring - While sailing, the map reveals more open sea around you (80 m instead of 50 m), and less in fog (20 m).
- Head Pitch - Your character's head is raised by a set angle (10° by default). The head turns to where the camera looks, and since the camera sits behind and above you, your character keeps looking at the ground - most noticeable with VRM avatars. Only your own screen changes.
- Oven Door - The stone oven's doors open only when you come close to them (1 m by default) instead of whenever you pass up to 3.4 m in front of the oven. Only your own screen changes.
- Skill Breakdown - A weapon's tooltip shows what an attack costs in stamina, eitr or health before your skill takes its share off (up to a third at level 100); the module adds what it really costs you now, in brackets, the way the game already shows block armour: "Use eitr: 35 (28.1)". Hold Left Shift over a weapon or shield - in your inventory, a chest or the crafting panel - to see how much of every number in brackets comes from your skill and how much from your current effects: "Fire: 132 (33-73) skill +46, effects +0" instead of "Fire: 132 (79-119)". The bracket then shows the value at skill level 0 with no effects. Display only.
Every module is independent and can be switched on or off in the config file. The mod saves nothing of its own: remove it and you have an ordinary world.
What is on, and what is off
Every module that is on by default either makes the game behave the way its developers evidently intended, or changes nothing that affects difficulty, loot or progress. None of them contains any cheat.
Modules that change how the game plays, so that whether you want them is your call, are off by default.
Why
Two moments started this:
- You find a Fuling camp while exploring, its bonfire burning in the night. When you come back to raid it, the fire dies just as you walk up. Nobody put it out. The camp was generated while it was still hundreds of metres away - the higher the simulation distance setting, the farther, often beyond anything you can see - and its fires have been on the clock since that moment. But the game only updates a fire's fuel while a player is within about 100 m. Until then the fire keeps showing the fuel it was created with. The moment you come that close, all the time since the camp was generated is charged at once, and if that is more than the fire could burn (about 14 hours for a camp bonfire, 7 for a camp fire pit), the fire you were walking towards goes out in front of you.
- You queue a whole night of smelting and go to sleep. In the morning the ingots lie scattered around the smelter - the night's work made visible all at once, a small reward that is worth keeping. Then, a few seconds later, they snap together into stacks of 30. The game does that whenever more than 200 ground items are loaded, and every fish swimming in the nearby sea counts as one.
Nobody seemed to talk about these, so they are written down and fixed here. If one of these fixes is useful to you, take it: folding it into another mod is welcome (see License). The source is not published, so the technical details below say exactly what the game does, what each module does about it and what it touches - which should be all another modder needs.
Achievements
Vanilla Fix itself does nothing that affects achievements: it runs no console commands, creates no items, changes no world modifier and never touches the game's cheat flags.
One thing is not specific to this mod and worth knowing anyway. The developers ask mods to set Game.isModded, and
BepInExPack Valheim does that for every modded game. In Valheim 1.0 the game stops counting achievement progress while
that flag is set (Achievements.IsCheatedAtAll). So with any mod loaded through BepInEx - this one included -
achievements are withheld. It is the game's rule for modded play: this mod neither adds to it nor lifts it.
If you want to keep earning achievements while playing with mods, that is a separate problem with its own solutions: several mods on Thunderstore exist for exactly that purpose.
Installation
Use a mod manager (r2modman, Thunderstore Mod Manager), or install by hand: put VanillaFix.dll into
BepInEx/plugins. Requires BepInExPack Valheim; nothing else.
Configuration
BepInEx/config/mage38.vanillafix.cfg, one section per module, numbered 01 to 19. The file is re-read while the game
runs. The sections of 1.0.0 (numbered 1 to 9) are renamed on the first start of 1.1.0 or later, and their settings are
kept.
Multiplayer
There is no version check: players with and without the mod can play together. Each module acts on the machine it runs on:
- Unload After Travel, Tomb Exit, Fire Warmth, Travel Pause, Mine Landing, Sound Voices, Raid Music, Location Music Echo, Wave Depth, Sea Exploring, Head Pitch, Oven Door, Multi-Craft Count, Message Below Inventory and Skill Breakdown only change your own game. With Head Pitch, other players still see your character's head the vanilla way; with Oven Door, each player's doors open as their own setting says; Mine Landing's floor is there for the players who have the module, and Sound Voices changes only what each player's own game plays.
- Grapple Release acts on your own character only. It sets your animation state the way the game does, so the other players see you corrected too.
- Camp Fires, Ground Item Merging and Station Range act through whoever owns the objects or the zone - usually the player standing nearby. Install the mod on every player's game for consistent results. On a dedicated server the server itself does nothing; the players' copies do the work.
Compatibility
Since nothing is patched (see "How every module is built" under Technical details), other mods are not overridden, and if a game update changes something a module needs, that module switches itself off and says so in the log while the rest keep working. Tested with Valheim 1.0.21.
Technical details (advanced)
For modders and the curious: for each module, what the game does, what the module does about it and exactly what it touches.
How every module is built
The same rules hold for all of them:
- Nothing in the game's code is replaced. There are no Harmony patches and no compile-time reference to the game. A module reads the game's own data and calls the game's own methods through reflection. Other mods are not overridden.
- Nothing of the mod's own is saved. No files besides the config, no custom data in the world or on the character. The only saved values a module writes are ones the game keeps anyway, written the way the game writes them: a fire's fuel (Camp Fires) and explored map pixels (Sea Exploring). Remove the mod and you have an ordinary world.
- It fails closed. If a game update changes something a module needs, that module switches itself off and says so in the log instead of guessing, and the others keep working. An error inside a module is caught and logged, not passed on to the game.
- It is light. Modules read the lists the game already keeps (loaded stations, ground items, locations, water volumes, effect areas, pieces, sound effects) instead of searching the scene, on timers of a quarter of a second to two seconds. Where the game keeps no list (the rooms of a dungeon), Mine Landing asks the physics engine for the colliders within 32 m of you, and only while you are inside a dungeon. The only things done every frame are reading a single flag per loaded camp fire, checking whether you are teleporting, checking whether your grappling hook point is still there (and, only when it is not, two animation flags), checking whether the multi-craft key is held while the inventory is open, asking whether a raid's music is on (and, around a raid, setting the volume of the location music that plays), setting the volumes of location music only where copies of one track play at once and, with Head Pitch on, re-aiming your own character's head (with Skill Breakdown on, also comparing the text of an open tooltip with what it last wrote).
Unload After Travel (on)
In the game. Unused assets are freed (Game.CollectResources) only on respawn, pause and sleep, and even that is
skipped if it happened in the last 20 minutes. A portal trip never triggers it, so a quick visit to another biome leaves
that biome's textures in video memory; on a card that is nearly full they spill into system memory and the game stays
slower until restarted. The console command cr fixes it at once.
The module does the same by itself:
- a few seconds after a teleport of at least 1000 m, measured sideways (settable; 0 = every teleport). Height is ignored: the area the game keeps loaded is chosen by position on the map alone, so a dungeon door, which lifts you 5000 m but moves you only some 25-30 m sideways, leaves the same area loaded, and an unload there would free almost nothing;
- while on a ship, every 3000 m (straight line) from where the last unload happened,
- on a key, if you set one.
Each unload costs one short hitch.
It touches nothing in the world. It calls Game.CollectResources, the method behind cr.
Camp Fires (on)
In the game. A fire's fuel is updated by Fireplace.UpdateFireplace: every 2 s it subtracts the time since the fire's
last update (lastTime) divided by m_secPerFuel. Only the machine that owns the fire does this, and a player owns just
what lies within 1.5 zones (96 m) of the centre of the zone they stand in (ZNetScene.PointInsideActiveArea) - whatever
the simulation distance. The simulation distance decides something else: how far away zones are generated and objects
created. So three things happen at three distances:
- A few hundred metres out, the camp's zone is generated. Its fires get their starting fuel (camp bonfire 10 of 10,
camp fire pit 5 of 10; one unit lasts 5000 s) and their
lastTime. The clock runs from here. - Closer, the fires are created and drawn, but not updated: they show the fuel stored for them, however old it is.
- Within ownership range the first update subtracts everything since the last one - usually since the camp was generated - in one go. A fire that looked fine a second ago is suddenly empty.
The module looks at each of a camp's own fires separately. It keeps the fire near full, and lights it again if it has burnt out, while both of these are true:
- within 20 m of the fire there is a camp spawner that has not spawned yet (nobody has come close), or a Fuling that one of the camp's spawners spawned;
- the fire is not inside a player base. This is the same test the game uses to stop monsters from spawning
(
EffectArea.IsPointInsideAreawithPlayerBase): workbenches and other stations, and the fires, torches, beds and portals you place.
Otherwise the module leaves the fire alone: it burns down normally and can be refilled by hand.
A kept fire that has just come into your ownership gets its lastTime brought up to date before the game's own update
can charge it for the time it was out of range, so it does not flicker either. (Arriving by teleport right next to a
camp, the fires can be created already in your ownership, and then the game's update comes first: the fire goes out and
is lit again on the module's next check, a moment later.)
Objects appear a few per frame after a load, so a fire is not refilled while a piece with a player-base area within 45 m
of it has not been created yet or was created less than 2 seconds ago: the base test could miss such a piece.
So an untouched camp keeps all its fires. While you clear a camp, the fires near the remaining Fulings are the last to be let go. Fulings that chased you far away and never came back no longer count, and penning a Fuling in next to a bonfire does not buy an eternal fire once you build a base around it. Random Fuling packs wandering the plains at night are ignored on purpose. Covers Fuling camps and the torches of Hildir's plains fortress (configurable).
It touches two values the game stores for every fire, its fuel and its last-update time, and then calls the game's
own Fireplace.UpdateState so the flames match. Only fires that belong to the listed locations and were not built by
a player are written to, only by their current owner - the one machine the game itself lets update that fire - and
only while the rule above keeps them. Since nothing can be written outside ownership range, a fire that had already
burnt out before the mod was installed stays dark from a distance until you come within about 100 m of it once.
Ground Item Merging (on)
In the game. Every 10 s each ground item checks whether more than 200 ground items are loaded
(ItemDrop.s_instances.Count > 200, the number is compiled in) and, if so, merges once with items of the same kind
within 4 m (AutoStackItems). Catchable fish are ground items too, so near the coast the count is above 200 all the
time. Swimming fish do not merge with each other; they only inflate the count.
The module counts without fish (Ignore fish) and compares that with the threshold (default 200, adjustable). While the count is at or below it, nothing merges; once the items you dropped really pass the threshold, the game merges them on its own schedule.
It touches one runtime flag on loaded items, ItemDrop.m_haveAutoStacked, which only AutoStackItems reads and
which is never saved: setting it makes the game's merge return at once, clearing it lets the merge run. Switching the
module off clears every flag it set. (With a threshold below 200 it also calls the game's own AutoStackItems.)
Station Range (on)
In the game. Patch 0.217.14 announced that "enemy spawn block and item despawn now matches workbench build radius",
and 0.217.22 that "increased crafting station radius should now block spawn area". So a workbench's spawn protection
grows with its extensions, and an artisan table protects 40 m. The mechanism is CraftingStation.GetExtensions(), which
writes the build range into the radius of the station's player-base trigger (m_effectAreaCollider).
But GetExtensions() is only reached after you walk up to the station (or use it). Right after a station is loaded
- game start, or coming back from far away - it protects only the 20 m of its prefab, and raids or animals can spawn inside your base. Players have been puzzled by this for years: forum threads report creatures spawning well inside an artisan table's range, and the answer they usually get is that its protection was only ever 20 m - which contradicts the patch notes above. The behaviour has not been addressed so far.
The module calls the same GetExtensions() on every loaded station (the game's own list
CraftingStation.m_allStations) every 2 s, so the full radius applies as soon as a station is loaded.
It touches nothing itself: the radius is set by the game's method, to exactly the value the game sets once you walk up. It never makes a radius larger than the game itself does, and nothing is saved.
Tomb Exit (on)
In the game. The Smouldering Tomb, the Black Forest dungeon of Hildir's quest, cannot be left the normal way. From inside, the bright exit shows no "Exit" prompt and walking into it does nothing; the prompt only flickers up when you look past the edge of the opening at an angle.
A dungeon's exit is an invisible trigger with a solid slab behind it: looking at the slab gives the prompt, and walking
into the trigger leaves the dungeon too. The entrance room is built for an exit that sits 1 m inside it, which is where
the ordinary burial chambers have it. Dungeons whose interior is aligned with the zone grid
(Location.m_useCustomInteriorTransform) need their exit authored 1 m further forward to end up there, and the mountain
caves, Hildir's cave and the Mistlands entrances all have it that way. The Smouldering Tomb's exit was left where it
was, so slab and trigger sit 1 m behind the end of the room, out of reach.
The module moves that exit 1 m forward, to the place it has in every other dungeon of this kind. The spot where you arrive when entering moves with it.
It touches the position of one scene object in a loaded Smouldering Tomb (Interior/ExteriorGateway). Nothing is
networked or saved, and each player's game has its own copy of the exit. An exit that is already in the right place is
left alone, so the module does nothing once the game's data is corrected; switching it off puts the exit back.
Fire Warmth (on)
In the game. The warmth around a fire is an effect area (EffectArea, type Heat) that keeps a list of who touches
it: Unity's trigger messages add you when you step in and remove you when you step out. Every physics step the area
calls Character.OnNearFire for everyone on that list, without looking at the distance, and that keeps the fire icon
and its warmth on you. Unity sends no exit message when a trigger is switched off while you stand in it, and a fire
switches its warmth off while it is wet (or out of fuel): the warmth sits in the part of the fire that is shown only
while it burns dry. So, standing by an open fire when rain starts, then walking away: once the rain stops and the fire
dries, its warmth comes back with you still on its list, and you have the fire icon wherever you go - in the rain, in
the middle of a forest - until that fire unloads or you walk through its warmth again. Areas that give a status effect
to whoever stands in them use the same list and can stick the same way.
The module checks once a second, in the game's own list of effect areas (EffectArea.s_allAreas), whether an
active area of these kinds still counts you although you are clearly outside it (outside its bounding box plus 1 m).
If so, it takes you off that area's list - what the missed exit message would have done. A leftover is gone within a
second; standing by a real fire is never affected.
It touches one runtime list per such area (m_collidedWithCharacter), removing only your own character, and only
while you are certainly out of the area. Nothing is saved, and the areas work as before for everyone else.
Travel Pause (on)
In the game. A portal trip takes at least 8 seconds and then waits until the area around the destination has
loaded (Player.UpdateTeleport); a dungeon entrance takes 2 seconds plus the load. For that time the game holds you
still on purpose: Player.UpdateStats returns at once while you teleport, so your food timers and the regeneration of
health and stamina stop, and damage is ignored. Two things were left out: status effects (SEMan.Update, run from
Character.CustomFixedUpdate) and the Forsaken power cooldown (Player.UpdateGuardianPower). So meads, Rested and an
active Forsaken power lose as many seconds as the load takes - more with a slow disk or a high simulation distance than
with a fast one - and poison or burning quietly runs out, since its damage is ignored meanwhile.
The module takes a snapshot when a teleport starts and writes it back every frame and every physics step until the teleport ends: the elapsed time of every status effect with a duration and what is left of its work (poison and burning damage left, healing ticks left), the Forsaken power cooldown, and health, stamina and eitr, so that a healing tick that still fires during the load gives nothing extra. The rules of the trip become the same for every load time, as the game already intends for food. Hold harmful effects (on) and Hold Forsaken power cooldown (on) can be switched off separately. Effects that start during the load, such as the weather at the other end, are left alone.
It touches runtime values of your own character only (StatusEffect.m_time and the work-left fields of poison,
burning and healing effects, Player.m_guardianPowerCooldown, health, stamina, eitr), and only while you are teleporting.
Tick timers are deliberately left running, so nothing fires twice. Nothing is saved beyond what the game saves anyway.
Multi-Craft Count (on)
In the game. Holding Shift (or pressing the left stick on a gamepad) at a crafting station crafts five at once
(InventoryGui.m_multiCraftAmount): the requirements are multiplied and the button reads "Craft x 5". The title above
them shows the total only when the recipe makes more than one item (Recipe.m_amount > 1, then "name x total"), so a
recipe for 2 says "x10" while a recipe for 1 keeps its bare name. Upgrades cannot be crafted five at once and are not
affected.
The module runs after the game has updated the crafting panel each frame (InventoryGui.UpdateRecipe, from
InventoryGui.Update). If the game is crafting five at once - the same test the game itself makes: the multi-craft key
held, no upgrade selected - and the selected recipe makes one item, it writes the title in the game's own format
("name x5"). It changes the title only while it is exactly the item's name as the game has just written it, so a title
set by another mod is left alone.
It touches the text of the recipe title on your own screen and nothing else. The game writes the title again every frame, so letting go of Shift or switching the module off brings the bare name back at once. Nothing is saved.
Message Below Inventory (on)
In the game. Short messages such as a skill going up or an item picked up are shown at the top left
(MessageHud.MessageType.TopLeft; only the very first level of a skill goes to the centre of the screen). Their text
and icon sit under TopLeftMessage, and the inventory (Inventory_screen) is the next element in the same GUI, so it
is drawn on top of them and covers them completely. Crafting stations open the inventory along with the crafting panel,
so a skill that goes up while you cook or craft plays its sound, but its message stays hidden.
The module runs after the game's own updates each frame. While the inventory is open (InventoryGui.IsVisible()),
it moves the message down so that its top sits just below the lowest open inventory panel: your own inventory
(InventoryGui.m_player), or the chest below it while one is open (m_container). The panels' corners are measured
every frame, so a different GUI scale or a mod that changes the size of the inventory is followed. When the inventory
closes, the message goes back to where the game put it.
It touches the position of the top-left message on your own screen and nothing else. Switching the module off puts the message back at once. Nothing is saved.
Mine Landing (on)
In the game. One room of the Infested Mines, dvergr_stone_open01 - a cave with a chasm crossed by a wooden bridge
and a stone doorway at each end - has a landing of three 6 m planks at one of its doorways (dvergrtown_wood_beam (6)
to (8)), level with the floor of the next room. In the room prefab these planks, and the three at the other doorway,
have no Destructible: unlike the beams of the bridge, they are meant to stay. But DungeonGenerator.PlaceRoom copies
a room's networked parts out of the room as they are only when the dungeon is first generated, and the saved object
keeps nothing but the prefab name. The next time the area loads, ZNetScene rebuilds the planks from the registered
prefab dvergrtown_wood_beam - the beam of the bridge, with 300 health - so from then on they can be broken, and they
drop wood. Under that landing, between the top of the cave wall and the edge of the next room's floor, is a gap of 0.4
to 0.6 m that leads under the next room: out of the dungeon, whose rooms float 5000 m above the world. Without a feather
cape the fall is fatal. The other doorway has the stair's own floor under its planks and stays closed. No other
networked part of any room of this dungeon changes this way after a reload.
The module gives every loaded dvergr_stone_open01 an invisible box with exactly the size and place of those three
planks. Where the planks are still there it lies inside them and changes nothing; where they have been broken it is the
floor they were, so worlds in which they are already gone are covered too. The planks themselves are left as the game
has them. A room is given the box only when it has the expected size and its stone doorway sits where expected, so a
room that a game update has changed is left alone, and logged. A dungeon's rooms are children of its DungeonGenerator,
which no game list holds, so the module finds them from the colliders within 32 m of you, four times a second, and only
while you are inside a dungeon (above 3000 m, the game's own Character.InInterior test).
It touches nothing of the game's own: it adds one collider object (VanillaFix_LandingFloor) under each such room,
in your own game only. Nothing is networked or saved, so the floor is there for the players who have the module;
switching it off removes the boxes.
Sound Voices (on)
In the game. Unity really plays at most 32 sounds at once: the game keeps the engine's default (AudioManager, 32
real voices and 512 virtual ones) and never changes it. Sounds beyond that are kept silent ("virtual") until a voice is
free. Music, ambience, wind and the sea are priority 0 and always keep theirs; every sound effect - creatures, your own
skills and footsteps, pieces - is priority 128, and they share the rest by how loud they are. Many sounds use
logarithmic rolloff, which stops getting quieter at the sound's max distance instead of going silent, so a loop far
out of its range still counts as a sound being heard and holds a voice. How many are loaded depends on the Simulation
distance setting (Graphics): Classic, the default, keeps creatures and objects running within two zones (64 m each)
around you, as the game did before 1.0; Extreme keeps them running within six, roughly five times as many zones, so
many more creatures and loops hold voices. The figures below were measured on Extreme. The Infested Mines float 5000 m
above the Mistlands, and the small locations there carry Dvergr lanterns (dverger_demister, max distance 8 m). In one mine, 22
of those lanterns 5000 m below and three fire loops 300 m away held a voice each all the time, and with music and
ambience only four were left for everything else. Creature sounds have no limit of their own
(ZSFX.m_maxConcurrentSources 0) and nothing culls them by distance, so with some 30 creatures loaded - every Tick and
Seeker calls every 5 or 6 seconds, Seekers also buzz while flying - sound effects kept cutting in and out, most of all
while casting. Measured in that mine: with the far loops stopped, the sounds close by still took up to 39 voices while
casting two long staff skills and drinking several potions in a row.
The module does two things, each with its own setting:
- Stop far sounds (on): four times a second it stops every 3D sound effect that is farther from you than four times
its own max distance (
Far factor), which frees its voice. A stopped loop starts again when it comes back within 80 % of that distance. If the game switches the loop's object off meanwhile - a fire put out - the module lets go of it, and the game starts it again itself when it is switched back on. Sounds the game starts and stops itself (ships, grappling points) are left alone. Muting would not do: Unity keeps a muted sound on its voice. - Voices (64): at start, before the main menu plays anything, the number of sounds Unity really plays at once is
raised to 64 (
AudioSettings.Reset). 32 leaves the game's value; mixing 64 instead of 32 costs little on today's computers. It is read at start only, so restart the game after changing it. If another mod also sets the number of voices, set it to 32 here and leave it to that mod.
It touches Unity's audio configuration (the number of real voices, at start) and whether the far sound effects it
stops are playing (AudioSource.Stop and Play). The sounds are read from the game's own list (ZSFX.Instances); the
only search of the scene is a single one at start, to start again any loop that is already playing when the voices are
raised (normally none). Nothing is networked or saved, and only what your own game plays changes. Switching the module
off starts again every loop it stopped; the number of voices stays until the game is started again. With Log on, it
writes every minute how many voices were in use at most and which far sounds are stopped.
Grapple Release (on)
In the game. The grappling hook's projectile leaves a point where it lands (GrapplingPoint, or
GrapplingPointSecondary for the secondary attack, which lowers you on the rope). GrapplingPoint.Activate sets an
animator bool on you through ZSyncAnimation - grappling, or repell for the secondary point; a pull that brings you
close switches to hanging, repell (Deactivate) - and gives you the hidden status effect GrapplingHook (no fall
damage, a fall speed limit) with no end (m_ttl 0). Only GrapplingPoint.Break undoes this: the bool of the point's
current mode back to 0, the effect given an end 2 s later (3 s for the secondary point), and the point destroyed.
Break runs from the point's own update - when you block, attack, unequip the hook or get more than 60 m (secondary
70 m) away - and only while the point's network object is valid. Jumping while you hang more than 5 m from the point
pulls you up to it (Character.Jump -> GrapplingPoint.Pull), and Pull switches the point to pulling, setting
grappling but leaving repell at 1. If that pull is cut short (block, attack, switching weapons), Break clears only
grappling and destroys the point: repell stays 1 with nothing left to clear it. A second, rarer way: the point is
not saved (ZNetView.m_persistent off) and has no clean-up of its own, so when its area unloads,
ZNetScene.RemoveObjects invalidates and destroys it and Break never runs. The player's animator has two transitions
that can start from any state, -> Grappling [grappling & falling] and -> Repell [repell & falling], and the game
sets falling once you have been off the ground for 0.1 s, rising too (Character.UpdateMotion). So with a bool left
at 1, every moment in the air throws you back into that state: an attack started in the air plays its first 0.1-0.2 s
(the stamina or eitr is spent and its start sound plays) and is cut off before its hit or projectile. On the ground
nothing shows. Restarting the game clears it.
The module checks every frame whether one of your hook points is alive (GrapplingPoint.m_localGrappler). While
one is, it does nothing. When none is but one of those bools is still 1, it does at once what Break would have done:
the bools back to 0 through your ZSyncAnimation. There is no frame where that is legitimate: Break destroys its point
at the end of the frame, and when it hands you over to the secondary point it activates that one within the same
call. Should a bool be 1 again right after being cleared, the module tries only every 0.25 s from then on. Four times a
second it also looks for the GrapplingHook effect still on with no end and gives it the end time of the hook's point
prefab (ResetTime, m_ttl = m_ttlSE, m_maxMaxFallSpeed = 0).
It touches two animator bools of your own character, set the way the game sets them, and the end time of one status
effect on you, found by its name GrapplingHook. Other effects - such as the feather cape's SlowFall, which also
takes away fall damage - are never touched. Nothing is saved.
Raid Music (on)
In the game. MusicMan plays one track at a time on its own AudioSource. Every frame UpdateCurrentMusic asks,
in this order, for raid music (HandleEventMusic -> RandEventSystem.GetMusicOverride, the m_forceMusic of the active
event, which is the raid whose area you are in), MusicVolume areas, sailing, triggers and the biome or time-of-day
music. Each track's settings are in MusicMan.m_music: the raid tracks (CombatEventL1 to L4, JotunInvasion)
fade in over 5 s, the biome tracks over 10 s, and all of them loop (the biome ones with continuous music on) and
resume. StartMusic only queues a track; UpdateMusic does the switching. When a raid starts, the biome track fades
out over the shorter of the two fade times (5 s), is stopped, and its position is saved; only then does the raid track
start and fade in. When the raid ends, the raid track fades out over its 5 s and is stopped; only then does the biome
track start again - from where it was stopped if that was less than two clip lengths ago, otherwise from its start -
and fade in over its own 10 s. Most location music does not go through MusicMan at all: the location music
prefabs (Music_FulingCamp, Music_GreydwarfCamp, Music_DvergrTower, Music_Haldor, Music_Hildir,
Music_SealedTower and the rest; the frost caves use a MusicVolume instead) carry a MusicLocation, which plays its
own AudioSource and sets its volume every frame in MusicLocation.Update from your distance alone - nothing there
looks at raids. Those sources play into the mixer group Music_ontop, which sends to a Duck Volume effect on the
Music group: location music normally pushes the biome music down. While a raid's music is on
(MusicMan.m_randomEventMusic), UpdateCurrentMusic turns that send down to -80 dB (the exposed mixer parameter
Music_ontop_ducking) so that the raid music is not pushed down. The Music_ontop group's own volume is not exposed
and nothing lowers it, so the location music keeps playing at full volume over the raid music.
The module does to the location music what MusicMan does to the biome track a raid replaces. It asks
RandEventSystem.GetMusicOverride() every frame - the same question HandleEventMusic asks.
- When that starts returning a track, a gain goes from 1 to 0 over that raid track's own fade time, read from
MusicMan.m_music(5 s). At 0 it notes where each playing location music source is (AudioSource.time) and when. - When it returns nothing again, the gain first finishes going to 0, then stays there until
MusicManhas stopped the raid track (MusicMan.m_currentMusicis no longer it; at most 15 s). Then each looping location music source is put back where it was noted, if that was less than two clip lengths ago, or else at its start, and the gain goes back to 1 overFade in time(3 s, the fade time of the game's ownlocation_*tracks inMusicMan.m_music). One-time stingers, which do not loop, are not moved. - The biome track keeps playing under the location music, pushed down by it, and
MusicManfades it out over at most the raid track's fade time before the raid track starts. Since the game stops the pushing down within about a second of the raid music being on, that biome track would otherwise be heard for a moment while both fade out (in vanilla too, but hidden by the location music at full volume). So while the location music fades out, the module writesMusic_ontop_duckingback to 0 dB, its value outside raids, every frame afterUpdateCurrentMusichas written its own - until the raid track isMusicMan's current track or the location music is silent. From then on the game's value is left alone, so the raid music itself is never pushed down.
Every frame, after every Update, the volume MusicLocation.Update has just set on each playing location music source
is multiplied by the gain, smoothed the way MusicMan smooths its own fades. MusicLocation.Update sets that volume
afresh every frame while its source plays, so nothing builds up; sources that are not playing, and everything while
your character is gone after a death, are left alone. A muted source is not stopped, since MusicLocation.Update
would start it again from its beginning while you are in range: it plays on at volume 0, and being put back where it
was makes up for that. With no raid and the gain at 1 nothing is touched, and nothing is done but that one question.
The sources are looked up once a second, only from a raid's start until the gain is back at 1, without searching the
scene: location music that is part of a loaded location is found among that location's children
(Location.s_allLocations, within 200 m), and location music that is a networked object of its own (Fuling and Greydwarf camps, the sealed tower,
...) among the objects of the zones around you (ZDOMan.m_objectsBySector -> ZNetScene.m_instances), its prefabs
picked out once per world from ZNetScene.m_namedPrefabs. A one-time stinger (MusicLocation.m_oneTime) that starts
during a raid plays unheard and still counts as played; the five among the location music prefabs only start when no
enemy is within their radius (m_notIfEnemies), which during a raid is rarely so.
It touches the volume of location music AudioSources on your own machine, the playback position of the looping
ones once per raid, and for the few seconds of the fade-out one exposed mixer parameter (Music_ontop_ducking), set to
the value the game itself uses outside raids. Nothing else. Nothing is networked or saved. Switching the module off leaves every volume to
MusicLocation.Update again on the next frame. With Log on, it writes when location music is faded out for raid
music and when it comes back, and which, and how long the biome music was kept down.
Location Music Echo (on)
In the game. A place's own music is a MusicLocation playing its own AudioSource (see Raid Music). Every frame
MusicLocation.Update moves its volume towards 1 - SmoothStep(r/2, r, distance) at 1 per second - r is m_radius
plus, with m_addRadiusFromLocation, Location.GetMaxRadius - and sets the source's volume to that times the source's
volume at Awake times MusicMan.m_masterMusicVolume. When that volume is above 0 and the source is not playing, it
starts the source from its beginning. Nothing stops a looping source: it plays on at volume 0 after you leave and is
heard again, from where it has got to, when you come back. Every location music source is 2D (spatial blend 0), so
where a copy stands changes nothing but that volume. An Ashlands ruined city is not one location: it is AshlandRuins
and FortressRuins pieces, often with a CharredTowerRuins1, in neighbouring zones 64 m apart - a city can be dozens
of them - and all three play Ashlands - Ruins 3, the first two within 72 m (40 + 32). Each piece starts its copy
when you first come that close to it, so the copies run seconds or tens of seconds apart, and coming and going brings
them back that way. The smaller ruins with Ashlands - Ruins 1 (CharredRuins1 to 4) do the same in threes and fours.
The module, every frame after every Update, groups the playing 2D location music sources around you by their clip.
In a group of two or more:
- One copy is heard: the one heard so far, as long as it plays; for a group met for the first time (the module switched
on, or arriving among the copies), the loudest. It stays the copy heard whichever piece you are nearest to, so the
track never starts over, and it gets the loudest volume
MusicLocation.Updatehas set on any copy of the group this frame - the volume of the nearest piece. The other copies are set to volume 0. - A copy that starts later, or is found playing, is moved to where the heard copy is (
AudioSource.timeSamples), so all copies play in step. It counts as in step only once its position reads back within 0.25 s of the heard copy's: a position written in the very frameMusicLocation.Updatestarts a copy can be dropped (the clips are streamed), so it is read back the next frame and written again if need be, then every 0.2 s until it holds. When the heard copy goes away - its location unloaded, or left 250 m behind you deep in a city - a copy read back in step takes over from the same place. Once a second, copies in step are checked again and moved if more than 0.25 s apart (after a raid, Raid Music puts sources back where they were, and starts one it had not seen from its beginning).
Different tracks are left alone: between two places with different music both are heard, each faded by its own
distance, as in the game. Where copies of one track overlapped, the music is now as loud as the nearest place, not the
sum of them. A one-time stinger (MusicLocation.m_oneTime) heard twice at once is heard once; the copy that joined late
still counts as played (MusicLocation.Update marks it once past three quarters of its length while you are in range).
MusicLocation.Update sets a playing source's volume afresh every frame, so nothing builds up; sources that are not
playing, and everything while your character is gone after a death, are left alone. During a raid the volumes read here
already carry Raid Music's fade.
The sources are found as Raid Music finds them, without searching the scene: location music that is part of a loaded
location among that location's children (Location.s_allLocations, within 250 m, asked once per location), and
location music that is a networked object of its own among the objects of the zones within 80 m
(ZDOMan.m_objectsBySector -> ZNetScene.m_instances; a zone's objects are read again only when their number has
changed). Both are looked up once a second, and at once when a location is loaded or unloaded, so that pieces loaded
together around you on arrival are put in step from their first frame.
It touches the volume and the playback position of location music AudioSources on your own machine. Nothing else.
Nothing is networked or saved. Switching the module off leaves every volume to MusicLocation.Update again on the next
frame. With Log on, it writes when copies are moved into step and how far apart they were, how close they stay
afterwards (and how many position writes had to be repeated), and when another copy takes over.
Wave Depth (off)
In the game. Shallow ground calms the waves: the wave height is scaled by a depth factor taken from the four corners
of each zone's water (WaterVolume.m_normalizedDepth). Those corners are read once, when the zone loads and before its
terrain edits are applied, so the water only ever sees the ground as it was generated: filling in the sea does not
change the waves.
The module compares each loaded water volume's four factors with the terrain as it is now and, when they differ,
writes the new factors and calls the game's SetupMaterial(), so the waves you see and the waves that move ships and
wet your feet both follow raised and dug terrain. Raised terrain weight counts only part of each raise if a fully
calm sea next to your build feels wrong.
It touches runtime fields of the loaded water volumes (m_normalizedDepth, m_oneDepth). Nothing is saved; without
the module a zone simply loads with the generated values again. It is off by default because it changes how the sea
behaves next to anything you build up.
Sea Exploring (off)
In the game. The minimap reveals a circle of Minimap.m_exploreRadius (50 m) around you, the same on land, at sea
and in any weather.
The module, only while you are on a ship:
- Open water +60 %: the ocean, and any ground at or below sea level such as shallows and river mouths, is revealed within 80 m instead of the usual 50 m. Land is revealed as usual.
- Fog -60 %, in every direction: while the weather is foggy (Misty), the explore radius drops to 20 m, land and sea alike. The map should not see further through the fog than you can.
It touches the map only through the game's own Minimap.Explore(x, y), so what it reveals is saved like any explored
map (and stays revealed). In fog it lowers m_exploreRadius and puts the original value back when the fog lifts, you
leave the ship or the module is switched off. It is off by default because it changes how exploring plays.
Do not use together with another mod that changes the explore radius while sailing.
Head Pitch (off)
In the game. Your character's head follows where you look, and where you look is where the camera looks
(Character.GetLookDir). Every frame the game smooths that direction (CharacterAnimEvent.m_headLookDir) and has the
Animator's look-at IK turn the head toward a point along it, at half weight (m_lookWeight 0.5); attacks switch the look
off. The third-person camera sits behind and above your character, so with your character's front at the centre of the
screen the camera - and with it the look - points down, and the head bows toward the ground. Avatar mods that copy the
game's pose onto the avatar every frame copy the bow as well, where an expressive face makes it stand out.
The module adds one small component to your own character, next to its Animator. Right after the game has aimed the head, it aims it again at the same point raised by Head pitch offset (10° by default, -30° to 30°). The setting is in degrees of the head itself: the target is turned twice as far, since the head turns about half of it. The game's weights are kept, so attacks and sitting behave as before, and the body follows only slightly (about 1° for 10°). Tested with EnhancedValheimVRM 1.4.1.
It touches nothing but where your own character's head aims on your screen. The look target the game sends to other
players (s_lookTarget) is left alone, so they see the vanilla head. Nothing is saved, and switching the module off
removes the component. It is off by default because how far to raise the head is a matter of taste.
Oven Door (off)
In the game. The stone oven (piece_oven) has a trigger sphere of radius 2 m with a ProximityState: as soon as a
player's collider touches the sphere, the doors swing open, and they close when the last player leaves it. The sphere's
centre is 1.4 m in front of the doors, so it reaches 3.4 m out. Every client opens the doors on its own screen; nothing is
sent over the network. No other piece in the game uses this.
The module moves the centre of that sphere to the middle of the doors and sets its radius to Open radius (1 m
by default, 0.05 m to 5 m), so the doors open when you come within that distance of them, right up to standing against
the oven. Shrinking the radius alone would leave a small ball floating in front of the oven, out of reach once you stand
close. This is done once on the oven's prefab, so that ovens loaded or built later get it, and once on the ovens already
loaded (Piece.s_allPieces); again when you enter a world or change the setting, and nothing in between. The sphere is
never switched off, since Unity sends no "left" message for a disabled trigger and a door could stay open; 0.05 m keeps
the doors shut in practice.
It touches only that sphere's centre and radius. Nothing is saved, and switching the module off puts the game's own back. It is off by default because how close is close enough is a matter of taste.
Skill Breakdown (off)
In the game. The tooltip of a weapon, tool or arrow lists each damage type as "Fire: 132 (79-119)": the item's
damage, and in brackets the range a hit rolls in at your skill (HitData.DamageTypes.GetTooltipString). The range is
the damage times 0.25-0.55 at skill level 0, rising evenly to 0.85-1.0 at level 100; the top end reaches 1.0 at level 75
(Skills.GetRandomSkillRange). The level used is your saved level plus the levels that status effects add, rounded down
(Skills.GetSkillLevel, SE_Stats.m_skillLevel), such as some trinkets at full adrenaline. Shields and weapons also
show "Block armour: 48 (61)", where the bracket adds half the Blocking skill factor (ItemData.GetBlockPower). Nothing
tells you how much of a bracket is your skill and how much is an effect. Damage bonuses that are applied only when a hit
lands, such as a percentage added to attacks, are not part of the tooltip at all, and not shown here either.
The costs - "Use stamina", "Use eitr", "Use health" (a number, or a percentage of your current health) and "Draw
stamina" - are shown as the item's bare values, but an attack takes less: 0.33 times the skill factor comes off
(Attack.GetAttackStamina, GetAttackEitr, GetAttackHealth, ItemData.GetDrawStaminaDrain), a third at level 100.
The stamina of a swing also follows equipment and status effects that lower attack stamina (m_attackStaminaModifier,
SE_Stats.m_attackStaminaUseModifier).
The module adds to each cost line, in brackets, what an attack costs you now, after your skill and your current effects: "Use eitr: 35 (28.1)". These numbers come from the game's own methods named above, called on a copy of the item's attack with you as the attacker; the world's own stamina and eitr rates (world modifiers) are left out, as the damage ranges leave out the world's damage rates.
While you hold Key (Left Shift by default), every number in brackets - damage ranges, block armour and costs - shows its value at skill level 0 with no effects, followed by what your saved skill level adds or takes off and what everything else adds or takes off on top, in the light blue the skills dialog uses for bonus levels: "Fire: 132 (33-73) skill +46, effects +0", "Use eitr: 35 (35) skill -6.9, effects +0". The skill part is computed from the game's formulas; for costs, "effects" is simply the rest of the game's own number, so it also covers equipment, and any bonus a later version of the game adds. When the two ends of a damage range move by different amounts (above level 75 the top end stops at the item's damage, and rounding can differ by one), both amounts are given in the order of the range, low end first: "Spirit: 10 (2-6) skill +6/+4" means the low end gains 6 and the high end 4. Costs are shown to one decimal. The words are in Japanese when the game is, in English otherwise.
It covers the tooltip in your inventory and in an open chest, and the item description in the crafting panel, at the
quality being crafted. Each line is changed only when it is exactly the line the game writes, computed the same way (the
damage lines are also compared with the game's own GetTooltipString); a line that does not match, because the game or
another mod writes it differently, is left as it is, with one warning in the log for damage or block armour.
It touches the text shown in the tooltip and in the crafting panel's description, on your own screen, and nothing else. The game writes a tooltip's text only when it changes, so the module writes its own again when you press or let go of the key, and every half second while a tooltip stays open, since effects come and go; the crafting panel is written by the game every frame anyway. Holding the key does nothing else in the inventory. Nothing is saved, and switching the module off puts the game's text back. It is off by default because it is extra information not everyone wants.
License
MIT. This software was generated entirely by an AI. Whether, and to what extent, copyright exists in AI-generated material is decided by the laws of each country; where those laws limit or deny such rights, they take precedence over the license.
