Neocor-SetUpSkills icon

SetUpSkills

Makes the hard-coded player skill numbers configurable: bow draw time, crossbow reload, damage curve, stamina costs, stealth, jump, XP rate and more. Server can push its config to clients.

Last updated 2 weeks ago
Total downloads 282
Total rating 1 
Categories Tweaks Tools Server-side Client-side
Dependency string Neocor-SetUpSkills-0.1.0
Dependants 0 other packages depend on this package

README

Set Up Skills

Puts every hard-coded player-skill number in Valheim into a config file. Built against Valheim 1.0.x and BepInEx 5.4.23.

The example that motivated the mod: a bow takes 2.50 s to draw at Bows 0 and 0.50 s at Bows 100, and nothing in the game lets you change that. With this mod:

[Bows]
DrawTimeMultAtSkill0 = 1.2     ## 2.5 * 1.2 = 3.00 s at skill 0
DrawTimeMultAtSkill100 = 0.4   ## 2.5 * 0.4 = 1.00 s at skill 100

Config file: BepInEx/config/neocor.SetUpSkills.cfg. Edits take effect on the next reload — the file is watched, so on a server you can retune without restarting it.

📖 WIKI.md is the full configuration guide: every setting explained in plain language, the vanilla numbers in tables, ready-made presets, and troubleshooting. Every setting also carries its own explanation as a comment inside the generated .cfg.

Install

The mod must be on every client. Valheim computes all skill effects on the machine that owns the player: Character.GetSkillFactor() returns 0 for anything that is not the local Player, and the methods that consume it (Player.UpdateAttackBowDraw, Player.CheckRun, ItemDrop.ItemData.GetDrawStaminaDrain, Attack.FireProjectileBurst, ...) only ever run there. Damage is rolled on the attacker's client and only the result is sent over the wire. A server-only install would change nothing.

Where What
Client Drop SetUpSkills.dll into Valheim/BepInEx/plugins/.
Dedicated server Install the BepInEx pack for Valheim, drop the same DLL into its BepInEx/plugins/, and start the server with start_server_bepinex.bat — the stock start_headless_server.bat does not load Doorstop.

Installing it on the server as well is optional but recommended: the server then hands its config to every client that has the mod, so everyone plays on the same balance. See Server sync. Players without the mod can still join; they simply keep vanilla values.

How the settings are layered

Layer B — endpoint knobs (the usual way in)

Each of these replaces one vanilla constant. The defaults are the vanilla values, so an untouched config file is bit-for-bit vanilla.

Section Setting Vanilla What it controls
Bows DrawTimeMultAtSkill0 / AtSkill100 1.0 / 0.2 Draw time as a multiple of the weapon's m_drawDurationMin (2.5 s for every bow).
Bows DrawTimeSecondsAtSkill0 / AtSkill100 0 / 0 Absolute draw time in seconds, ignoring the weapon's base. Both must be > 0 to take over from the multipliers.
Crossbows ReloadTimeMultAtSkill0 / AtSkill100 1.0 / 0.5 Reload time as a multiple of m_reloadTime.
Combat DrawStaminaReductionAtSkill100 0.33 Stamina drain removed while drawing, at skill 100.
Combat DrawEitrReductionAtSkill100 0.33 Same for eitr.
Combat AttackStaminaReductionAtSkill100 0.33 Stamina cost of an attack removed at skill 100.
Combat AttackEitrReductionAtSkill100 0.33 Eitr cost of an attack.
Combat AttackHealthReductionAtSkill100 0.33 Health cost of an attack (blood magic).
Damage MinFactorAtSkill0 / MaxFactorAtSkill100 0.4 / 1.0 Damage multiplier before the random spread.
Damage RandomRange 0.15 Half-width of the damage roll. 0 gives flat damage.
Blocking BlockPowerBonusAtSkill100 0.5 Extra block power as a fraction of the base.
Run StaminaMultAtSkill0 / AtSkill100 1.0 / 0.5 Run stamina drain.
Run SpeedBonusAtSkill100 0.25 Extra run speed (+25 %).
Sneak StaminaMultAtSkill0 / AtSkill100 1.0 / 0.25 Sneak stamina drain.
Sneak StaminaExponent 0.5 Shape of the sneak stamina curve. Vanilla is a square root; 1.0 makes it linear.
Sneak StealthBaseAtSkill100 / StealthLightWeightAtSkill100 0.2 / 0.4 Visibility in darkness and the weight of ambient light, at skill 100. Lower is stealthier.
Dodge StaminaMultAtSkill0 / AtSkill100 1.0 / 0.5 Dodge stamina cost.
Jump ForceBonusAtSkill100 0.4 Extra jump force (+40 %), up and forward.
Building StaminaReductionAtSkill100 0.5 Build stamina cost removed at skill 100.
Building PlaceDurabilityReductionMultiplier 1.0 Scales the tool's own m_placementDurabilityMax.

Layer A — per-skill curve ([Curve])

Three values per skill, for all 24 skills:

[Curve]
Bows.Level0Factor = 0      ## what the skill is worth at level 0
Bows.Level100Factor = 1    ## ...and at level 100
Bows.Exponent = 1          ## 1 = linear, >1 = back-loaded, <1 = front-loaded

This remaps the skill factor itself, so it reaches everything that skill touches — including the effects driven by values stored in the prefabs, which Layer B cannot reach: swim stamina, fishing stamina, farming harvest radius, crafting speed and bonus-yield chance, placement durability, and riding.

Layer A and Layer B compose. Layer A decides how fast the benefit accrues with level; Layer B decides where it ends up. Setting Bows.Level100Factor = 0.75 with default Layer B gives a draw time of Lerp(2.5, 0.5, 0.75) = 1.0 s at skill 100 — the same result as the example at the top, but it also scales bow damage by the same token. Use Layer B when you want one effect, Layer A when you want the whole skill.

Layer C — experience ([Experience])

Setting Vanilla Effect
<Skill>.GainMultiplier 1.0 XP gain per skill. Stacks with the world modifier.
CurveExponent / CurveScale 1.5 / 0.5 The XP wall: Pow(floor(level + 1), 1.5) * 0.5 + 0.5. Lower exponent means faster levelling.
DeathLossFactor -1 Fraction of every skill lost on death. -1 keeps the game's own value (0.05); 0 loses nothing.

The level cap stays at 100. Raising it would mean rewriting Skills.Skill.Raise and the level / 100 divisor, which touches save data, so it is deliberately out of scope.

Server sync

With [ServerSync] ServerIsAuthority = true (the default, only read on a server), the server sends its whole config to each client as it connects, over a routed RPC named SetUpSkills_Config. The client applies it in memory only — its own .cfg on disk is never written to, and the local values come back the moment it disconnects. Editing the config file on a running server reloads it and re-pushes to everyone already connected.

A client never sends config anywhere, and a server never accepts config from a client.

How it is patched

Target Kind Why
Skills.GetSkillFactor postfix Layer A curve.
Humanoid.GetAttackDrawPercentage prefix Needs a different base, not just a different endpoint.
ItemDrop.ItemData.GetWeaponLoadingTime prefix Same.
ItemDrop.ItemData.GetDrawStaminaDrain / GetDrawEitrDrain prefix Two-line methods.
Attack.GetAttackEitr(Character, ItemData) prefix Two-line method.
Skills.GetRandomSkillFactor / GetRandomSkillRange prefix Roll and its tooltip, kept in step.
ItemDrop.ItemData.GetBlockPower(int, float) prefix Two-line method.
Player.GetPlaceDurability prefix Scales a prefab field, not a constant.
Skills.RaiseSkill, Skills.Skill.GetNextLevelRequirement, Skills.OnDeath prefix Experience.
Attack.GetAttackStamina, Attack.GetAttackHealth, Character.Jump, Player.CheckRun, Player.GetRunSpeedFactor, Player.OnSneaking, Player.UpdateStealth, Player.GetDodgeStaminaUse, Player.GetBuildStamina transpiler The skill constant is unique inside the method, so swapping just that literal leaves every other piece of vanilla logic running. Much more robust across game updates than a rewrite.

Each patch is installed on its own. If a game update renames a method or moves a constant, that one effect falls back to vanilla and a warning appears in the log — the rest keeps working. Set [General] IsDebug = true to see the computed values and which transpilers hit.

[General] Enabled = false returns every knob to its vanilla default without unloading anything, which is also what happens to transpiled code (it cannot be un-patched at runtime, so it looks up the vanilla constant instead).

Building

With the .NET SDK:

dotnet build SetUpSkills\SetUpSkills.csproj -c Release

Override the game path with -p:ValheimPath="C:\Path\To\Valheim" if it is not on G:.

Without an SDK, build.ps1 finds a C# compiler on its own and package.ps1 produces the Thunderstore zip:

powershell -ExecutionPolicy Bypass -File SetUpSkills\build.ps1
powershell -ExecutionPolicy Bypass -File SetUpSkills\package.ps1

That fallback path can land on C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe, which only understands C# 5. The source currently stays inside that subset — no string interpolation, no nameof, no ?., no expression-bodied members — so both build routes work. Drop the restriction only if you also stop using build.ps1.