
UnlockChecklist
Tag locked skills, skins and survivors from the character-select loadout panel, then read the unlock requirements back as a checklist in game. Client-side and read-only.UnlockChecklist
Tag locked skills, skins and survivors in the character select, then read the unlock requirements back as a checklist in game.
Status: v0.1.0 — feature complete
Open the loadout panel on the character select screen and click a locked skill.
Press K in a run to see what you tagged and what each one needs. Entries tick
themselves off as you unlock them.
The mod is client-side and read-only with respect to game state: no netcode, no grants, no patches that change behaviour. The only thing it writes is its own tag list.
How it works
Tagging
Locked entries in the vanilla loadout panel are already click-dead. AddButton
sets MPButton.disablePointerClick on anything you have not unlocked, so
MPButton.OnPointerClick returns without doing anything — the button exists to
show you a lock icon and a tooltip, nothing more. That makes plain left-click a
free gesture on exactly the entries this mod cares about, with no vanilla
behaviour to override.
The click is picked up by an IPointerClickHandler on the button rather than
through onClick. The event system dispatches to every handler component on
the object, so the marker hears a click the button itself has decided to ignore.
Right-click works everywhere, locked or not, and is dispatched because
MPInputModule.GetMousePointerEventData builds a full right-button event from
the left button's raycast.
Both gestures are individually switchable in the config.
Finding out what a button stands for
Two postfixes, on LoadoutPanelController.Rebuild and
SurvivorIconController.Rebuild. A postfix only adds, and runs after every
other mod's patches on the same method, so whatever they did to the rows is what
gets decorated. Both bodies are wrapped: seven mods in this profile patch
LoadoutPanelController, and an exception thrown from here would surface as
their bug.
Survivors are easy — SurvivorIconController.survivorDef is public.
The loadout panel is not. Its buttons live in a private List<Row> of a private
nested type, so reading them needs reflection on rows, Row.rowData,
RowData.button and RowData.defIndex. That list is used rather than walking
the transform children because it carries each button's defIndex, which stays
correct when another mod reorders or filters a row. If those fields ever
disappear, loadout tagging switches itself off and says so; survivor tagging and
the console commands carry on.
Nothing in a row says which skill slot it belongs to — only its position
does. Rebuild adds one row per GenericSkill that is not
hideInLoadoutSelect, in component order, then exactly one skin row. That
enumeration is mirrored to turn a row position back into a slot. If the panel is
not that shape, some other mod has added or removed rows and every position past
that point means something else, so the panel is skipped with a warning rather
than decorated with a wrong mapping. ExtraSkillSlots adds GenericSkill
components to the body prefab, so it adds rows and slots and the shape still
matches.
Reading the requirement back
UnlockableDef.getHowToUnlockString() is what the game's own tooltip prints,
and it is the fallback here. Where the unlockable is backed by an achievement —
which is nearly all of them — AchievementManager.GetAchievementDefFromUnlockable
gives the name and description separately, which is better: the name is already
shown as the row title, so only the description needs to be the requirement
line. The achievement icon comes from the same place, falling back to the skill
or skin icon when it has none.
What is stored
Only the UnlockableDef name. Which survivor owns it, what the requirement is,
which icon to draw and whether it is done are all resolved live from the
catalogs, so a tag cannot go stale against a changed modpack.
A name that no longer resolves is kept and shown as unresolved rather than dropped — temporarily disabling a mod should not silently delete your list.
That last point needs one piece of care. UserProfile.HasUnlockable(string)
answers true for a name it has never heard of. Passing tag names straight
through would mark everything from an uninstalled mod as completed, and
Clear completed would then delete it. The def is resolved first instead, and
an unresolved one counts as not unlocked.
The list lives in BepInEx/config/com.bdub.UnlockChecklist.tags.txt, one name
per line, safe to edit by hand.
The checklist window
K by default, rebindable, and it works in a run, in the lobby and in the main
menu. The hotkey stands down while the console, the chat box or any other text
field has focus.
It is a standalone overlay canvas rather than something parented into the HUD or the character select hierarchy. The same window then opens on every screen with no per-screen special cases, and nothing another mod does to those hierarchies can reach it.
Bounding
Three things keep rows inside the mask, all of which bit once:
- A fresh
RectTransformis 100x100, and on a stretched rectsizeDeltais not the size — it is added to whatever the anchors span, with the pivot deciding which edges the surplus hangs off. Left at the default on the scroll content, that put 50px of every row past each side of theRectMask2D.CreateChildRectnow zeroes it, and the content rect states it again because that is the rect the mask clips against. - Labels do not wrap. Every one is a single line in a rect of fixed height,
so wrapping would push a second line somewhere too short to show it and slice
the glyphs.
ConfigureTextturns wrapping off and setsEllipsisfor all of them; the row title additionally auto-sizes down to 15pt before truncating, sinceSurvivor - Skill (Slot)gets long on modded content. - The list is sized to a whole number of rows. Whole rows are dropped until it fits rather than capping the total height — shaving an arbitrary number of pixels is what leaves a row sliced through the middle, which reads as broken instead of scrollable.
Outstanding entries come first, completed ones after them, struck through with a
DONE badge. Unlock state is re-checked once a second while the window is open,
so an achievement that fires mid-run ticks itself off without rebuilding the
list under you. Rows are only rebuilt when the tag set actually changes.
Console commands
| Command | Effect |
|---|---|
uc_ui |
Open/close the checklist window |
uc_list |
List everything tagged, with its requirement |
uc_tag <name|search> |
Add something. Accepts Skills.Loader.Thunderslam or thunderslam |
uc_untag <name|search> |
Remove something |
uc_find <search> |
Search unlockables without tagging |
uc_clear [done] |
Empty the list, or only what is completed |
Searches that match more than one thing print the candidates instead of guessing. These are the fallback the UI hooks do not have: if a game patch or another mod breaks the character-select decoration, the list stays fully usable.
Open the in-game console with Ctrl + Alt + `` (left Ctrl/Alt) once; after that plain `` orF2` toggles it.
Configuration
General holds ToggleKey, the two tagging gestures
(TagOnLeftClickWhenLocked, TagOnRightClick) and ShowCompleted.
Display holds MarkTaggedInMenu (the orange corner pip and tint) and
AppendTooltipHint.
The tooltip hint is appended to tooltips the game has already filled in, which
is only ever locked entries. Rebuild rewrites that text from scratch every
time, so it appends once rather than accumulating. Its wording is deliberately
stateless — the string is baked at rebuild time but read on hover, so anything
naming the current tag state would be wrong the moment you clicked.
Known limitations
- Only what the loadout panel shows can be tagged: survivors, skill variants and skins. Items, equipment, artifacts and logbook entries have unlockables too, but no screen this mod hooks displays them.
- Entries with no
UnlockableDefare not taggable. There is no requirement to put on a checklist. Most modded survivors ship no unlock at all. - Tags are shared by every local profile. RoR2 supports several; a checklist is a note to the person playing, not to the save file.
- Unlock state is read from local profiles, like everything client-side. In multiplayer you see your own progress, not anyone else's, and nothing is synced — each player's checklist is their own.
- Achievement progress is not shown, only the requirement text. RoR2 does not expose a general progress figure for an achievement.
AchievementDef.GetAchievedIcon()cannot resolve an addressable icon synchronously, so a few modded achievements fall back to the skill or skin icon.
Installing
Install with a mod manager (r2modman / Thunderstore Mod Manager), or drop the
DLL into BepInEx/plugins/.
Credits
Written by bdub, with Claude (Anthropic) as a coding assistant.