API
For mod authors · API 1.1.0 · in Path of Valheiman since 5.0.8

Make your mod work with Path of Valheiman

One supported, versioned API: read progression and stats, show or keep PoV's equipment, grant tree and profession XP, keep your skills out of the freeze, leave your creatures alone, listen to events. No reflection into PoV's internals, no breakage on our updates.

Quick start Reference DLL (727 KB) Plain Markdown for AI assistants Semantic versioning

#For AI assistants

The same text as plain Markdown is at valheiman.com/api.txt. Give it to your assistant as the reference. Short version: the namespace is Valheiman.Api in PathOfValheiman.dll (BepInEx GUID treextr.pathofvalheiman). Use a soft dependency, call from the main thread, check Pov.Has(feature), and never reflect into PassiveTree.*.

The supported way for other Valheim mods to work with Path of Valheiman (PoV): read the character's progression and stats, show PoV's equipment and items in your own interface, keep PoV's cells in your own inventory, give tree or profession XP for your own activities, keep your skills out of PoV's skill freeze, and leave your creatures alone.

Everything is in the namespace Valheiman.Api inside PathOfValheiman.dll. Web version: https://valheiman.com/api

Availability: API 1.0.0 since Path of Valheiman 5.0.8, 1.1.0 since 5.0.9. Check Pov.Has(...) at runtime and your mod keeps working with older PoV versions.

#Rules in one screen

  • Versioning. Semantic versioning. Within major version 1 nothing is removed and nothing changes meaning; minor versions only add. Check Pov.AtLeast(1, minor) and Pov.Has("feature").
  • Only Valheiman.Api is a promise. Everything else in the DLL (the PassiveTree.* namespaces) is PoV's inside and changes without notice. Do not reflect into it; ask for what you need instead.
  • Soft dependency. Your mod must load without PoV. Keep PoV calls in a class of their own, touched only when PoV is installed (see Quick start).
  • Main thread. Call everything from Unity's main thread.
  • Never throws for missing state. Without a local player or world, calls return empty values: 0, null, false, empty lists.
  • Local vs world. Character data (progression, equipment, XP) is the local player's, kept on their client. Creature data lives in the creature's ZDO: readable on every peer; PoV writes it only on the ZDO owner. World settings belong to the host. Calls that must run on every machine say so.
  • Hosting. Several parts of PoV's interface can be handed to your mod (...Host(yourGuid)): PoV keeps the logic and data and stops drawing that part. null gives it back. The last host wins.

#Quick start

  1. Reference the API: download PathOfValheiman.ref.dll (a small reference assembly with the public types only) from https://valheiman.com/api-assets/PathOfValheiman.ref.dll, or reference the real PathOfValheiman.dll. Set Private=false so it is not copied into your package.
<Reference Include="PathOfValheiman">
  <HintPath>libs\PathOfValheiman.ref.dll</HintPath>
  <Private>false</Private>
</Reference>
  1. Declare a soft dependency and keep the calls behind a check:
[BepInPlugin("my.mod", "My Mod", "1.0.0")]
[BepInDependency("treextr.pathofvalheiman", BepInDependency.DependencyFlags.SoftDependency)]
public class MyMod : BaseUnityPlugin
{
    public static bool Pov => Chainloader.PluginInfos.ContainsKey("treextr.pathofvalheiman");
    void Update() { if (Pov) PovBridge.Tick(); }
}

// Only this class mentions PoV types: it is never loaded when PoV is absent.
static class PovBridge
{
    public static void Tick()
    {
        if (!Valheiman.Api.Pov.Has("progress")) return;
        int points = Valheiman.Api.Progression.FreePoints;
    }
}
  1. Listen instead of polling where you can: Pov.Changed and the Events class.

#Features

Pov.Has(name) is true for each feature this PoV provides.

FeatureWhat it gives
progress, statslevel, XP, points, tree nodes; stat values by name
eventscharacter loaded, level up, node taken/removed, stats changed, loot rolled
grantgive tree XP or profession XP from your mod
craftingthe craft tree (professions)
buildthe character's path, ascendancy class and masteries
skills.exemptkeep your mod's skills out of PoV's skill freeze
equipment, equipment.cells, equipment.keysequipment cells, their items, hotkeys
equipment.hostyour mod draws the equipment
equipment.offyour mod turns PoV's equipment system off
storageyour inventory mod keeps PoV's storage cells
pursecoins kept in their own cells
looks, looks.portraittransmog; a turning character portrait for your window
trinketsextra trinket seats
rows, rows.hostextra backpack rows from the tree; your mod sizes the inventory
rarity, rarity.host, tooltipitem rarity and colours, tooltip affix rows; your mod draws rarity frames
items.detailsaffixes with ids, tiers and roll ranges; implicits; corrupted, identified, item level; sockets and sigils; influences
chestscrafting from nearby chests
quickstack, quickstack.hostbag tidying; your mod provides the buttons
shieldthe energy shield
hud, hud.claimPoV's HUD pieces; your mod places them
creatures, creatures.leave-alonecreature rarity, stars, modifiers, levels; opt your creatures out
difficultythe world's PoV preset and Valheim combat modifier
textPoV's texts in the player's language

#Pov

Pov.Guid          // "treextr.pathofvalheiman"
Pov.Version       // "1.1.0" - the API's version, not the mod's
Pov.AtLeast(1, 0) // this API is 1.0 or newer within major 1
Pov.Has("storage")
Pov.Changed += areas => { /* "equipment", "keys", "purse", "looks", "progress", "host" */ };

Pov.Changed fires on the main thread at most once per frame with the areas that changed.

#Progression and stats

Progression.Level; Progression.Xp; Progression.XpToNextLevel;
Progression.FreePoints; Progression.SpentPoints;
Progression.HasNode("node-id"); Progression.Nodes;      // taken node ids
Progression.TreeOpen;                                     // the tree window is open
Stats.Get("XpGainPct"); Stats.Has("TidyPack"); Stats.Names;   // unknown names read 0
Shield.Enabled; Shield.Current; Shield.Max; Shield.Regenerating;
Build.Path;              // dreng, skitalets, koldun, ratnik, trickster, warden - or null
Build.PathTitle;         // in the player's language
Build.AscendancyClass;   // or null
Build.Masteries;         // category -> chosen options

#Events

Events.CharacterLoaded += player => { };
Events.LevelUp        += level  => { };
Events.NodeTaken      += nodeId => { };
Events.NodeRemoved    += nodeId => { };
Events.StatsChanged   += ()     => { };   // read Stats again
Events.LootRolled     += items  => { };   // PoV rolled kill loot for the local player (rarity and affixes set)

A listener that throws is logged and does not stop the others.

#Grant XP from your mod

Grant.TreeXp(120f, XpKind.Kills, "my.mod");   // counted like PoV's own XP of that kind
Grant.ProfessionXp("smith", 40);              // one craft-tree profession
Crafting.Professions;   // miner, smith, armorer, woodcutter, builder, shipwright, sailor, hunter, herder, cook,
                        // brewer, beekeeper, farmer, forager, fisher, pathfinder, delver
Crafting.TotalXp; Crafting.HasNode("craft-node-id");
  • XpKind: Combat, Kills, Crafting, Gathering, Mining, Building, Travel, Map. Each kind has its own world multiplier. Peaceful kinds (Crafting, Gathering, Mining, Building) go to the craft tree, as PoV's own do.
  • At most Grant.MaxPerCall (5000) per call. Local player only: call it on the client whose player did the thing.
  • Typical use: an enchanting table, a new gathering activity, a quest reward.

#Skills: keep yours out of the freeze

PoV replaces skill levels with its tree: with the world setting on, every skill is frozen. A skill mod can keep its own skills working as without PoV:

// In Awake, on every machine:
SkillFreeze.Exempt(mySkillType);   // true; false for a Valheim skill (those stay PoV's)
SkillFreeze.Frozen;                // the freeze is on in this world
SkillFreeze.IsExempt(type);

An exempt skill gains experience and reports its real level. It also keeps its level on death (PoV skips the vanilla death loss while the freeze is on).

#Equipment

PoV keeps equipment in cells. By default they are hidden rows of the player's own inventory, under the backpack. Never compute where they are: the first row is remembered per character, other mods change the inventory height, and an inventory mod may keep the cells itself (see Storage). Use the API.

Cell ids (stable): helmet, chest, legs, cape, belt, quiver, trinket, utility, utility.wisp, utility.wishbone, trinket.1, trinket.2, trinket.hand (the extra trinket seats talents open; worn seats like trinket, not storage), food.0..2, potion.0..2, arrow.0..2, tool.0..7, purse.0..7, key.0..7. Later minor versions may add cells; treat unknown ids gracefully.

Equipment.On;              // the system is on
Equipment.TurnedOffFor;    // the other equipment mod that keeps the slots instead, or null
foreach (CellInfo c in Equipment.Cells) { c.Id; c.Kind; c.Index; c.Title; c.Worn; }
Equipment.ItemIn(player, "food.0");
Equipment.IsOpen(player, "potion.2");    // food/potion/arrow cells depend on the belt and quiver worn
Equipment.Accepts("tool.3", item);
Equipment.Put(player, "food.1", item);   // worn cells: wears the item; false if refused
Equipment.TakeOut(player, "helmet");
Equipment.GridPosition(player, "key.0"); // where the cell's item lies; (-1,-1) if none
Equipment.IsCellPosition(player, pos);   // sorters and stackers: leave these positions alone
Equipment.BackpackRows(player);
Equipment.FoodCells(player); Equipment.PotionCells(player); Equipment.ArrowCells(player);
Equipment.KeyLabel("tool.0");            // "Alt+1"
Purse.Open(player); Purse.Coins(player); Purse.Empty(player);
Trinkets.Seats; Trinkets.OrdinarySeats; Trinkets.Worn(player);

Turn it off. Equipment.TurnOff("my.guid") in Awake: PoV's equipment system is off as if the player switched it off (rows, window, tool bar and purse stay away; cells a save still holds go back to the backpack). TurnOff(null) gives it back from the next character load. If you only want to keep the items yourself, Storage.Register is better: PoV's rules and effects then keep working.

Draw it yourself. Equipment.Host("my.guid"): PoV keeps the cells and rules, removes its window and HUD bars and stops hiding its rows in the backpack grid. Equipment.Host(null) gives it back.

#Storage: keep PoV's cells in your inventory mod

For inventory mods that manage slots themselves. Your mod keeps where the items lie and draws them; PoV keeps every rule and effect: what a cell accepts, how many food/potion/arrow cells are open, the hotkeys, the utility items' effects, the purse's death and weight rules, transmog, the tree's extra rows.

class MyStorage : IStorageProvider
{
    public string Id => "my.guid";
    public int Contract => Storage.Contract;                                // 1
    public ItemDrop.ItemData ItemIn(Player p, string cell) { ... }          // never moves anything
    public bool Put(Player p, string cell, ItemDrop.ItemData item) { ... }  // move it, or change nothing
    public bool TakeOut(Player p, string cell) { ... }                      // into the backpack; false if no room
}

string refused = Storage.Register(new MyStorage());   // in Awake, before a character loads; null = accepted
Storage.Changed("food.0");                            // once after every committed change
  • Storage.Cells lists the cells you keep: food.*, potion.*, arrow.*, tool.*, purse.*, key.*, utility, utility.wisp, utility.wishbone. Armour, belt, quiver and trinket stay the game's own equipment.
  • Items stay real items of the player's own inventory. Return those very items, not copies.
  • Check Equipment.Accepts(cell, item) and Equipment.IsOpen(player, cell) before placing. Your own slot counts and filters do not override PoV's rules: an item a cell does not accept is ignored there (PoV reads the cell as empty).
  • The choice is made when the character loads and holds for the session. With a provider active, PoV never attaches its rows, never reads item positions as cells, and hands you Rows.Host and Equipment.Host. Size the backpack with Storage.ExtraRows.
  • Death and weight: PoV keeps purse items out of the grave and removes their weight itself, through ItemIn. Read Storage.KeptOnDeath and Storage.Weightless to show it, but do not apply it again.
  • Coins and keys picked up are not sorted into their cells by PoV; place them yourself if you like.
  • If your provider throws, PoV logs it and pauses the cells for the session (Storage.Suspended). It never falls back to its own rows mid-session.
  • Unregister takes effect from the next character load. Nothing is converted: PoV rows in an old save are not moved into your slots.
  • Local player only, never on a dedicated server.

#Looks (transmog)

Looks.CanChange(item); Looks.Choices(item); Looks.IsOpen(look); Looks.Current(player, item);
Looks.SameLook(a, b); Looks.HiddenLook;
Looks.WhyNot(player, item, look, canHide);    // null when allowed, else a translated reason
string refused = Looks.Apply(player, list);   // (item, look, canHide) tuples; one Rune of Guise each
Looks.Reset(player, item); Looks.Runes(player); Looks.RuneIcon;
using var portrait = Looks.CreatePortrait(rawImage);   // Tick(player) each frame, Turn(deg), ShowBackground, TryOn

#Items and tooltips

Rarity r = Items.RarityOf(item);   // Common, Magic, Rare, Unique, Legendary
Items.RarityTitle(r); Items.RarityNameColor(r); Items.RarityFrameColor(r);
Items.Signature(item);             // changes when anything PoV keeps on the item changes: redraw then
string type = Tooltip.Describe(item, out var rows);   // AffixRow: Name, Effect, Tint, Socket, EmptySocket, Warning
Items.HostRarityFrames("my.guid"); // you draw rarity frames on inventory cells; PoV stops

Everything PoV keeps on an item, as data (feature items.details):

Items.IsCorrupted(item); Items.IsIdentified(item); Items.ItemLevel(item); Items.IsJewel(item);
foreach (AffixInfo a in Items.Affixes(item))       // empty while the item is unidentified
{
    a.Id;            // stable affix id from the loot catalogue, or the unique line's mechanic id
    a.Place;         // Prefix, Suffix or Unique (unique items and jewels)
    a.Tier;          // "T1", "T2" ... or ""
    a.Value;         // the rolled number, as the tooltip shows it
    a.Min; a.Max;    // the tier's roll range, already scaled for the item's slot
    a.Text;          // the tooltip line in the player's language
    a.Local;         // works on the item itself (weapon damage, armour) instead of the character
    foreach (AffixStat st in a.Stats) { st.Stat; st.Form; st.Value; st.Subjects; }   // PoV stat names, see Stats.Names
}
foreach (ImplicitInfo i in Items.Implicits(item)) { i.Id; i.Value; i.Min; i.Max; i.Text; }   // e.g. belt food/potion slots
foreach (SocketInfo s in Items.Sockets(item)) { s.Kind; s.Sigil; }   // "runic" / "corrupted" / "divine", sigil prefab or null
Items.Influences(item);                              // influence ids

#Inventory and conveniences

Rows.Extra;                 // extra backpack rows the tree gives
Rows.Host("my.guid");       // you size the inventory; PoV stops changing its height
Conveniences.CraftFromChests; Conveniences.ChestReach(station); Conveniences.ChestCircleShown = true;
QuickStack.Available; QuickStack.StackKey; QuickStack.SortKey;
QuickStack.HostButtons("my.guid");   // you show the buttons; PoV's hotkeys keep working

#HUD

foreach (HudPiece p in HudPieces.Pieces()) { p.Id; p.Title; p.Rect; p.ClaimedBy; }
HudPieces.Claim("level", "my.guid");   // PoV stops moving and fading it; null gives it back

Piece ids: level, buffs, monolith, shield, gauges, craft-queue, tasks (more may come).

#Creatures

Creatures.RarityOf(c);      // Magic, Rare, Unique from stars; Common when PoV's creature rarity is off
Creatures.Stars(c);
Creatures.ModifiersOf(c);   // ids: swift, furious, armored, warded, burning, ...
Creatures.Modifiers;        // every modifier: Id, Title, About
Creatures.Level(c); Creatures.BiomeLevel(Heightmap.Biome.Swamp);
Creatures.IsPovBoss(c);     // PoV's own monolith bosses
Creatures.DamageFactor(c);  // how much PoV multiplies its damage

Leave your creatures alone. A boss overhaul or elite-creature mod that designs its creatures itself:

// In Awake, on every machine (server and clients):
Creatures.LeaveAlone("Bonemass", "my.guid");

PoV then gives that prefab no rarity, stars or modifiers and does not scale its health or damage. Creatures.LeftAloneBy(prefab) tells who asked.

#Difficulty

Difficulty.Preset;          // 0 vanilla creatures, 1 easy, 2 normal, 3 hard, 4 merciless
Difficulty.PresetTitle;
Difficulty.VanillaCombat;   // Valheim's Combat modifier: 0 very easy ... 4 very hard, 5 none of these
Difficulty.MonsterPower(c); // altar monster power for this creature (1 = none)

#Texts

Texts.Get(key, englishFallback): a PoV text in the player's language.

#Recipes

  • A skill mod (new skills): SkillFreeze.Exempt(yourSkill) in Awake. Optionally also Grant.TreeXp for the activity, so it feeds the tree too.
  • A boss or elite-creature overhaul: Creatures.LeaveAlone(prefab, guid) for each creature you design.
  • An inventory/equipment UI mod: either draw PoV's cells (Equipment.Host + Equipment.*) or keep them yourself (Storage.Register). Use Equipment.IsCellPosition in sorting and stacking.
  • A HUD mod: HudPieces.Pieces() and HudPieces.Claim, then place the rects as you like.
  • A loot mod: read Items.RarityOf, Tooltip.Describe, Creatures.RarityOf / ModifiersOf for drop chances; Events.LootRolled to react to PoV's drops.
  • A crafting station or profession mod: Grant.ProfessionXp(profession, xp).

#Missing something?

Ask on the PoV Discord (https://discord.gg/XZsYRF2YtQ) with what you need. New members come in minor versions.

#Changelog

  • 1.1.0: trinket seat cells trinket.1, trinket.2, trinket.hand. Known issue in PoV 5.0.9: Pov.AtLeast(1, 1) returns false there (Pov.Version says "1.1.0"); to detect the trinket cells, check Equipment.Cell("trinket.1") != null. Fixed in the next update.
  • 1.0.0: first release.