You are viewing a potentially older version of this package. View all versions.
StandardVibeware-SunkenCryptTimer-1.1.8 icon

SunkenCryptTimer

Client-only HUD countdown + ground rings for dungeons on servers running Venture Location Reset: 'resets in 2d 14h' near any entrance, workbench-style ring at VLR's check radius. No server changes needed.

Date uploaded 2 weeks ago
Version 1.1.8
Download link StandardVibeware-SunkenCryptTimer-1.1.8.zip
Downloads 70
Dependency string StandardVibeware-SunkenCryptTimer-1.1.8

This mod requires the following mods to function

denikson-BepInExPack_Valheim-5.4.2351 icon
denikson-BepInExPack_Valheim

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

Preferred version: 5.4.2351

README

SunkenCryptTimer

A client-only Valheim mod for players on servers that run Venture Location Reset (VLR).

It answers two questions at a glance:

  • "When does this dungeon reset?" — a HUD countdown ("Sunken Crypt — resets in 2d 14h") whenever you are near a dungeon entrance: sunken crypts, burial chambers, troll caves, frost caves, goblin camps, dvergr mines, Hildir's dungeons, Ashlands fortresses, and more.
  • "How close is too close?" — a workbench-style ground ring around each nearby instanced dungeon at the radius VLR's player-activity check uses for it (45 m for sunken crypts), so you can see the footprint where player-made structures or a tombstone would block the dungeon from ever resetting.

No server changes, no Harmony patches, no world-data writes: the mod only reads the VV_LastReset stamps VLR leaves on each dungeon's world object — those sync to every connected client automatically.

Requirements

Component Needed? Notes
Valheim on your client Built/tested against Valheim 1.0.16 (Unity 6 build). Older versions untested.
BepInEx on your client Tested with denikson-BepInExPack_Valheim 5.4.2351 (BepInEx 5.4.22+).
Venture Location Reset on the server The mod displays VLR's reset data; without VLR on the server there is simply nothing to show. Any recent 1.x.
VLR on your client optional If installed (it usually is on modded servers), the countdown uses the server's live reset intervals, synced through Jotunn. If not, set FallbackResetDays to match your server.
Jotunn not required This mod does not reference Jotunn or any other package.

The mod does not need to be installed on the server (installing it there is harmless but does nothing — servers have no HUD). Other players don't see your rings or your timer unless they also run the mod.

Installation

With a mod manager (r2modman / Gale / Thunderstore Mod Manager): search for "SunkenCryptTimer" and install. Done.

Manual: copy Plugins/SunkenCryptTimer.dll from the package into your BepInEx profile's BepInEx/plugins/ folder.

Usage

Timer

Walk near any dungeon VLR manages (default: within 80 m of the entrance). A single line appears at the top-center of the HUD:

  • Sunken Crypt - resets in 2d 14h — the dungeon still holds its loot.
  • Sunken Crypt - reset ready (regenerates on approach) — the timer has elapsed. VLR only regenerates a dungeon when a player walks within 100 m of it after the timer expires, so it will pop fresh the moment someone (maybe you) shows up.

Timers keep running while the server is empty (the world clock advances 24/7 while the server process runs), and the countdown is computed from live world time — log back in after a week and the label already reflects it.

Zone rings

Each nearby instanced dungeon (crypt, burial chamber, troll cave, ...) gets a ground ring cloned from the vanilla workbench build-circle: thin rotating dashes hugging the terrain. Its radius is the distance VLR checks for player activity inside that dungeon type — 45 m for sunken crypts, computed per type from the game's own dungeon data (hypot(zoneSize.x/2, zoneSize.z/2), matching VLR's GetDungeonRadius).

Rules of thumb the ring makes visible:

  • Build or die inside the ring's area (or within the small entrance zone around the door) and that dungeon stops resetting until the pieces/tombstones are removed.
  • A base on the ground outside the ring is safe and never blocks resets.
  • Terrain shaping (raising/leveling) never blocks a reset, but VLR reverts terrain within ~12 m of the entrance each time the dungeon resets.
  • Rings show while you're within 100 m (VLR's own trigger distance), hide while you are inside the dungeon, and are capped at the 4 nearest features. Ground locations (meadow farms, villages, fuling camps) intentionally get no ring.

Configuration (BepInEx/config/lan124.SunkenCryptTimer.cfg)

All settings are client-side; changes apply immediately (no restart).

Section Key Default Meaning
General Enabled true Master switch
General DebugLogging false Verbose scanner logging
HUD ScanRadiusMeters 80 Show the timer within this distance of an entrance
HUD UpdateIntervalSeconds 1 Rescan rate
HUD HudOffsetY 170 Label offset from the top of the screen (px)
Timers FallbackResetDays 10 Assumed interval when VLR's config can't be read — set to your server's actual interval
Rings Enabled true Draw ground rings around nearby instanced dungeons
Rings ShowRadiusMeters 100 Show a ring while within this distance of the entrance

How it works

VLR stamps every dungeon it manages with VV_LastReset (the in-game day it last reset) on the dungeon's LocationProxy world object (ZDO). ZDOs sync to every connected client, so this mod:

  1. scans nearby LocationProxy objects for the stamp (throttled, default 1 s),
  2. resolves the effective reset interval from VLR's own live config on the client (which Jotunn keeps synced with the server's settings) or falls back to FallbackResetDays,
  3. displays lastReset + interval − now, mirroring VLR's exact day-math, and
  4. (rings) clones the vanilla CircleProjector marker used by the workbench and sizes it per dungeon type from the game's location prefab data, confirmed against the live dungeon generator.

The mod never writes world data, owns no networked objects, and patches nothing.

Building from source

Requires the .NET SDK (6+) and a local Valheim install + BepInEx profile:

./build.sh -p:GameDir=/path/to/Valheim -p:ProfileDir=/path/to/BepInExProfileRoot

GameDir is the folder containing valheim_Data/; ProfileDir is the folder containing BepInEx/core. Output: SunkenCryptTimer/bin/Release/SunkenCryptTimer.dll. ./package.sh additionally builds the Thunderstore zip (requires zip). For the full release/upload workflow see PUBLISHING.md.

Version compatibility

Version Status
Valheim 1.0.16 (Unity 6000.0.75f1) tested, daily use
Valheim older / newer patch levels untested; the mod only uses stable APIs plus two reflective reads (EnvMan.m_totalSeconds, Hud.m_instance) — likely fine, not guaranteed
BepInEx pack (denikson) 5.4.2351 tested
Venture Location Reset 1.1.0 / 1.1.1 tested; interval mapping mirrors VLR's GetResetTime (re-check when VLR adds location types)
Jotunn any / absent not referenced

Known limitation: the per-type reset-interval table in src/ResetTimer.cs only takes effect when VLR's per-type override mode is on; otherwise VLR's global ResetTime applies to everything (as on most servers).

License

MIT

CHANGELOG

CHANGELOG — valheimmer / SunkenCryptTimer

2026-10-06 — v1.1.8: match status-effect text size, sit below the effect row

  • Field feedback on v1.1.7's right-side label: font too large, and the fixed 170 px top offset overlapped the minimap. The label now copies its font size from the status-effect template text (the small text on the Rested/Wet icons) and repositions itself every update just below the live bottom edge of the status-effect row, right edges aligned - so wrapped rows of effect icons push the timer down instead of it overlapping anything. HudOffsetY is now only a fallback offset for the rare case the row can't be located.

2026-10-06 — v1.1.7: right-side label, friendly ground-feature names, no rings on graves

  • HUD label moved from top-center to the top-right corner (user preference; the top-center spot collides with event/notice text). Right-aligned, anchored 20 px from the right edge; word wrap on with the rect width clamped to the canvas, so long lines ("Odin's Tomb (-435,-142) - resets in 9d 3h") wrap to extra lines growing downward instead of running off the screen. HudOffsetY now means the offset below the top edge on the right side - raise it if it overlaps the minimap or right-side UI mods.
  • Field log showed VLR stamps nearly every zone location on the Stormer server (Grave1, InfestedTree01, SwampHut1/2/5, SwampRuin1, StoneTower1, StoneTowerRuins09, Dolmen01, ShipSetting01, Runestone_, WoodHouse2/4/5, Crypt4, ...), so the timer label was firing for all of them with raw prefab names ("Grave1 (-435,-142) - resets in ...") - and kept cycling the whole way home through meadows, which is why the text "kept updating after leaving the swamp". Ground features now get friendly names (Grave, Infested Tree, Swamp Hut, Swamp Ruin, Stone Tower, Ruined Tower, Shipwreck, Dolmen, Runestone, Abandoned House, Goblin Camp); unknown Runestone_/StoneTowerRuins* variants collapse by prefix.
  • Ring bug from the same log: ground features (Grave1, InfestedTree01, SwampHut2, SwampRuin1) each got a phantom 45.3 m ring - the same radius as crypts. ZNetScene.GetPrefab does not resolve their location prefabs, so the radius fallback borrowed a neighbouring crypt's dungeon generator (dense swamp = always one within the 120 m bound). Location prefabs now resolve through ZoneSystem's own location registry (GetPrefab kept as secondary), so ground features correctly resolve to "no ring"; the generator fallback only remains for genuinely unresolvable names ("Dungeon" placeholder, DG_*).

2026-10-06 — v1.1.6: fix duplicate HUD labels (v1.1.5 field regression)

  • First field session with v1.1.5's clone-based label showed timer text twice (red top-center + brown right side, sometimes running off the right screen edge) with copies persisting long after leaving the dungeon. Session log: "HUD label ready" fired 34 times in one session (should be once per world), sources alternating between a HUD mod's 'Name' and 'TimeText' panels, each creation followed by "Can't remove CanvasRenderer because TextMeshProUGUI depends on it".
  • Cause: the label was created by cloning some other panel's text as its SIBLING and stripping non-TMP components (including the CanvasRenderer TMP needs). Parenting into foreign panels means the label inherits their color (brown/red), anchors to the panel's rect instead of the screen (off-screen), and dies or gets orphaned when those panels rebuild/pool — orphaned copies are unreachable by Hide(), hence the persistent text.
  • Fix: only the FONT is taken from a donor label (event-name font if loaded, else any actively rendering label). The label itself is built from scratch as a direct child of the HUD canvas root — true top-center-of-screen anchoring, no component stripping, no CanvasRenderer errors. Style (size + color) comes consistently from the event-name element instead of whatever panel donated the font. Stale copies from earlier HUD rebuilds are swept by name on recreation so only one label can ever exist.

2026-10-06 — v1.1.2: fix invisible HUD text + missing rings on dedicated servers

  • First real-server test (Stormer dedicated server, Valheim 1.0.16) showed neither feature; session-log instrumentation added in-between builds pinpointed both causes.
  • HUD text invisible: the font copied from the vanilla event-name label was a non-null reference to a not-yet-loaded font asset (Unity 6 lazy loading) — TMP renders nothing and logs "no Font Asset assigned". Fonts are now trusted only when their atlas texture is resident; otherwise the HUD is scanned for any actively rendering label's font, then TMP's default asset, retying each update until one loads (so late font assignment self-heals).
  • Rings never created: dungeon names resolved to the generic "Dungeon" placeholder because the LocationProxy ZDO's s_location string reads empty on dedicated servers (worked on the local test server — why the smoke test passed). Names now come (via reflection) from the proxy's private m_instance — the spawned location GameObject, whose name is SunkenCrypt4(Clone) — with the ZDO field and a zone-system position match as fallbacks.
  • Ring lifecycle now logs at Info level once per stage (zone rings: template ready, ring radius: <prefab> <r>m, ring created: ..., tracked dungeon: '<name>' (via <source>)), so field failures are diagnosable from LogOutput.log without enabling DebugLogging.
  • Buildable on any machine: dotnet build -p:GameDir=... -p:ProfileDir=... (first non-124 build; dotnet SDK 9 installed on Machine B for this).

2026-10-06 — GitHub publish + Thunderstore packaging

  • Public repo created: https://github.com/altpersona/SunkenCryptTimer (MIT license, gh CLI authenticated by user over device flow, SSH remote via uploaded ed25519 key). Private/local files kept out via .gitignore: DEV_NOTES.md, TODO.md, deploy.sh, setup-local-server.py, .claude/, build artifacts (leak scan: server IP/local paths only ever lived in those).
  • Release v1.1.0 published with assets SunkenCryptTimer-v1.1.0.zip (Thunderstore format: manifest+README+icon+CHANGELOG at root, DLL under Plugins/) and the raw DLL.
  • README rewritten user-facing (requirements matrix, install, usage incl. VLR approach-trigger semantics, config table, build-from-source, version compatibility).
  • package.sh (builds the zip), tools/make_icon.py (256×256 icon: timer ring + arched crypt door), manifest description trimmed to 204/250 chars, website_url set to the GitHub repo. Thunderstore upload itself is the remaining user step (TODO.md).

2026-10-05 — v1.1.0: zone rings + deep VLR research

  • Research session (all verified against VLR master source, the server-synced config, and raw game asset dumps):
    • Stormer server VLR config: ResetTime=10 in-game days (~5 h real; 1 day ≈ 30 min), OverrideResetTimes=false → ALL dungeon types use 10, incl. Sunken Crypts (SunkenCrypt4; the Crypt2/3/4 names are black-forest burial chambers).
    • Reset does NOT happen in the background: fires when a modded player approaches ≤100 m horizontal (VLR RESET_RANGE; watcher gives up >200 m; ~5 s after zone load, 1 s polls, ONE SHOT per zone load). Reset executes on the approaching client (VLR adds its watcher only on non-dedicated instances, needs ZDO ownership).
    • Blocks ("player activity", horizontal-only checks): player pieces/tombstones/players within exteriorRadius = 12 m (ground) or dungeon radius = 45.25 m (hypot(64/2,64/2) of DG_SunkenCrypt.m_zoneSize=64×64, at interior altitude y≥4000) of the crypt center. Skip defers, never cancels; timer keeps counting.
    • Terrain: raising land does NOT block resets, but VLR's TerrainReset.ResetTerrain reverts all terrain mods within the 12 m ground radius on each reset.
  • v1.1.0 changes (user decisions: 45 m ring only, instanced dungeons only, same plugin, visible within 100 m):
    • New SunkenCryptTimer/src/ZoneRing.cs — ring manager (see DEV_NOTES architecture).
    • src/Tracker.cs — FindNearestTrackedDungeon → CollectTrackedDungeons (collects all); NearbyDungeon gained Position/Uid, now internal; HUD label logic extracted to UpdateHudLabel() unchanged.
    • Plugin.cs — Rings/Enabled (true), Rings/ShowRadiusMeters (100) config; PluginVersion 1.0.0 → 1.1.0. Same bump in SunkenCryptTimer.csproj (<Version>) and SunkenCryptTimer/manifest.json (version_number, description updated).
    • README.md — zone rings section, config rows, layout line.
    • Verified: build 0 errors; deploy to Gale profile; local server smoke test clean (Loading [SunkenCryptTimer 1.1.0], 0 fatal). In-game visual check pending (user).