fix: align picker position counter with the items-list filter - #18
Open
pangin wants to merge 1 commit into
Open
Conversation
syncItemIndices() counts every owned item in ItemRegistry when restoring
the picker cursor on open, but cycleSlotItem() and getSlotItemInfo() —
the two functions that BUILD the visible items list — both filter out
items whose id is not present in the renderer's name array (i.e. whose
texture/swatch was not loaded). When the two sets disagree, every item
in ItemRegistry that appears after the missing one is off by one in the
picker, and the LAST item in the registry goes out of bounds — at which
point getSlotItemInfo's defensive `pos >= len(items)` guard falls back
to position 0 ("none"), so the equipped accessory visibly disappears
after closing and reopening the picker.
The drift can be triggered by any mismatch between ItemRegistry and the
loaded *Names slices: a packaged asset bundle that omits a .png, a
runtime texture-load failure, or a future registry entry whose asset
hasn't been added yet. Add the same "loaded" filter to
syncItemIndices() so all three functions agree on counting.
Tests cover the happy path (no drift), the drift case (one mid-registry
item missing from hatNames — verifies both off-by-one mid-list and
out-of-bounds at the end), unowned items not advancing the counter,
and the no-equipment branch resetting to position 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Potential issue
The modal accessory picker has three functions that each construct or address into the same logical "items list" for a slot —
[none, owned-and-loaded items in ItemRegistry order, unowned items]:cycleSlotItemidx >= 0inhatNames/faceNames/ etc.)getSlotItemInfosyncItemIndicesItemRegistryWhen the two sets disagree — that is, the profile owns an item whose id is in
ItemRegistrybut not in the renderer's loaded*Namesslice —syncItemIndicesadvancesidxfor it whilecycleSlotItemandgetSlotItemInfodo not. The picker cursor lands one position past where the item actually appears in the visible list.Concrete consequences for
SlotHatwith one mid-registry hat missing fromhatNames:ItemRegistryis rendered as the NEXT hat in the items list (off-by-one).ItemRegistryyieldspickerItemIndex[slot] == len(items), which tripsgetSlotItemInfo's defensivepos >= len(items)guard and silently falls back to position 0 ("none").The visible symptom is: after equipping a high-registry-position accessory and closing/reopening the picker, the slot displays as empty.
When can the drift happen?
Any time
ItemRegistryand the renderer's*Namesslice diverge. Examples:.png(e.g. an older release whose asset list lagged behind a newItemRegistryentry).ItemRegistryentry added in code before its asset is committed.In each case, the current code drops the equipped accessory's visual state on the next picker open, which is hard to attribute and easy to misread as a save/load bug.
Fix
Add the same "loaded" filter to
syncItemIndicesso all three functions agree on counting. The filter is a singlenameSetlookup and short-circuits identically to the existing two call sites.No change for the happy path (all owned items also loaded) —
nameSetlookup is true for every owned item, so position counting is identical to before.Tests
renderer_picker_modal_test.go(new file) constructs a minimalRenderer(no raylib resources touched) and verifies:hatNameswhile the profile still owns it. Every hat after the missing one (and the last hat in particular) resolves to its correct visible position, no longer overshooting.ItemRegistry; cursor must land at position 1 (just after "none"), not advance for unowned earlier items.currentHat == -1clears any stalepickerItemIndexvalue.Tests compile cleanly; full
go testrequires raylib's DLL on PATH (package init loads it), which the project's CI provides.