Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Rist
A character level beside the skills, carved into runestones you choose and deepen.
CHANGELOG
Changelog
Notable changes to Rist. Format follows Keep a Changelog, and the mod uses semantic versioning.
[1.0.0] — 2026-08-16
First release. There is no 0.1.0 entry below it because 0.1.0 was never published — it was this same work under a version that had not yet been judged ready, and folding it in beats inventing a release nobody could have installed.
The line this sits on
It adds a level beside the skills. It never reads, writes, rescales or reskins a vanilla skill.
Valheim has skills but no character level. Rist adds one that runs alongside them. Vanilla skills are read and never written.
Levels and picks
- Every character level earns one pick, spent on any runestone in the catalogue to raise it a rank, up to five.
- Picks bank. Nothing expires and the panel waits.
- No drawbacks. A runestone is a reward or it is nothing. A cost attached to a reward makes a pick something you can regret, and regretting a permanent choice is a reason to stop playing rather than a reason to think harder.
Free choice, not a dealt hand
An earlier version dealt three runestones per level, seeded so that quitting on a bad offer and returning re-offered the same three — otherwise the pick is theatre, since anyone can reroll until they get what they want.
Choosing freely deletes that problem rather than defending against it: no roll to reroll, no
offer to store in the ledger or on the wire, no second window with its own visibility rules.
What it costs is the tension of a hand you are dealt. A free choice means the strongest runestone
is always available, so balance now lives in the runestones themselves and in MaxRank.
The panel carries the weight for this: choosing between nineteen runestones is only a real choice if each stone says both what it is worth now and what the pick would make it.
Every fifth rank is a capstone
Taking a runestone the whole way grants something the earlier ranks did not hint at, rather than one more slice of the same number. It is what makes depth a decision against breadth instead of a rounding error — five ranks of the same small bonus is the same reward as five different small bonuses, so without this there was no reason to ever finish anything.
Capacity is a capstone, not a runestone of its own
This began as an inventory problem — late-biome kit eats the grid and leaves nothing for what you went out to fetch. The cheap fix is more rows, and that is exactly what Hoard argues against.
It was a runestone once, called Deep pack, and it was the strongest thing in the catalogue: taken first, every time, which is not a choice. A row now sits at the bottom of Ox-backed and of Sure hand, so capacity is the reward for taking a hauling runestone all the way rather than a purchase anyone makes on their first pick.
The panel
A fifth tab on the compendium bar, beside the raven and the trophy, opens a full screen of runestones — one per rist, each with its own outline, rock and marks, all seeded off the card id so a rist looks the same in every session and on every machine. A rank cuts one more mark into the rim, and the mark holds the level that bought it.
Three designs came before it: a draft window dealing three at random, a board of tiles, and that same board dressed in the game's own wooden sprites. The last is why this one exists. Borrowing a window frame is imitation, and it read as a different game no matter how close the sprites got. A runestone imitates nothing.
The bar
A fifth bar under stamina, cloned from one of the game's own so it carries the same frame, fill and trailing fill. It follows the stamina bar rather than sitting at fixed pixels, so "below stamina" stays true at any resolution and HUD scale, and it flashes while a pick is waiting.
Verdigris, because the HUD already spends red, yellow, blue and orange between its four bars and a fifth in any of those reads as one of them.
What the server decides
The ledger is held by the server and keyed to the platform identity plus the character, never to the character file — a character file sits on the player's own disk, so nothing that decides rewards may live there. The client reports only that a skill went up; every number is derived server-side.
Three ceilings bound what a report can be worth, because skills live on the client and a report is therefore a claim that cannot be verified, only bounded:
- Reports per minute, which caps how many claims are accepted.
- A one-level step, which caps how much a single claim may say. Skills level one at a time and fire one callback each, so a report naming a level far above the baseline this server watched that skill reach did not come from playing. This also protects the baseline, which would otherwise adopt the forged level and give every later claim an alibi.
- XP per minute of connected time, which is the only cap that does not depend on believing the client at all. It banks three minutes so an honest burst — clearing a crypt levels four skills at once — is paid in full.
Nobody is disconnected and nothing is ever taken away. Rist used to refuse the connection, and a levelling mod deciding who may play means a bug in an XP system locks people out of a server. Refusing at the door moved to Threshold, where it is the whole job and is done openly.
Cloning vanilla UI, and what it does not hand over
Both the bar and the compendium tab are clones of things the game already draws, which is what
makes them match. What a clone does not bring is its own lifecycle: it has never run
Awake, OnEnable or LateUpdate, because the donors sit inactive.
GuiBar puts something essential in each of those, so the bar failed three times over - the
tint reached nothing, the width was discarded and re-derived from the donor's, and setting a
value stored a number without drawing it. All three are ways of not reaching one line. The
fill is written directly now, and the trailing bar lagged by hand.
The tab had the same shape of problem read from the other end: its gold is not in any sprite
or any Image.color but in the Button's normalColor, applied to whichever graphic is its
targetGraphic. Building a layer alongside it could never match; swapping the sprite on the
graphic already being tinted inherits size, shadow, gold, hover and press at once.
The rule both arrive at: clone for geometry and sprites, drive the state yourself.
Core is optional
Rist installs and runs on its own. Core is a soft dependency; installing Rist no longer
installs Core with it, and manifest.json no longer lists it.
Solo, nothing is given up — including the extra inventory rows, which now have a Rist-owned
implementation used only when Core is absent. On a server, three things are lost, and the
README section "Core is optional, and here is exactly what that costs" sets them out: the
cards.txt hash check, the host-authoritative curve, and the arbitration of
Inventory.m_height between mods. Rist logs a warning at startup naming all three.
The row fallback deliberately copies Core's Player.Load widening rather than simplifying it.
That is not a nicety: without it every item in a granted row is destroyed by loading the game,
silently, because AddItem drops any saved position outside the current grid and the rows are
not applied until after the load. It is patched in only when Core is absent, so the two owners
can never both write the field or both widen the grid.
Mechanically, as elsewhere in the suite: every Ezomic.Core call now sits in its own
[MethodImpl(MethodImplOptions.NoInlining)] method behind a Chainloader.PluginInfos check,
because the JIT resolves a method's assemblies when it first compiles that method — an inline
call would drag Core in before the check could prevent it. Verified by decompiling the built
DLL: Suite.* and InventoryRows.* appear only inside those isolated methods.
A character arrives worth what its skills are worth
CreditExistingSkills, on by default, pays a joining character for the skills it already
holds — turn up carrying skills worth twenty levels and you arrive at twenty levels with the
picks to spend. XP is the skill level reached, so a skill at N has already produced N(N+1)/2;
summing that over every skill is exactly what the character would hold had all of it happened
here, which also makes it safe to re-run on every login and impossible to double-pay.
Off, only XP gained on this server counts and every character starts at level 0. Config syncs from the host, so a server picks one rule for everyone on it.
There is no untrusted state. A character above the baseline was briefly marked untrusted and paid nothing "until they line up again", which it could never do — the withholding returned before the baseline was updated, and the login check only adopted a new baseline when it had found nothing wrong, so the baseline froze while the player's real skills climbed away from it.
Keeping a well-travelled character out of a world altogether is a door policy, and the door is Threshold's.
Known limits
- Never played at the shipping curve. The pick path was exercised against a deliberately
cheapened one so it could be reached without grinding.
LevelBaseXpandLevelExponentare back at60and1.5, and how often a stone lights up at those numbers is still a guess. - Runestone balance is untested by definition. With free choice the strongest runestone is always available, and nothing has been played hard enough to find out which one that is.
- Multiplayer has never had a second player. The version gate, config sync and catalogue hash all work host-side and against a dev server, but no remote client has connected.