|
Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
|
← All months (index) · 192 commit(s) this month.
First Gap-5 coverage-fill pass: tests/db/tst_db_coverage_fill.cpp exercises the Examples / TmHmsDB / MusicDB / TrainersDB accessor one-liners that no existing test happened to call (read-only, zero source changes). Building it surfaced a key reachability limit now documented in plans/testing.md: the db/savefile shared libs only export their DB_AUTOPORT-marked classes, so unexported classes (e.g. Examples, HiddenCoinsDB) are only reachable from a headless test via QML meta-object property() reads (used here for the Examples accessors) — a chunk of the raw coverage "miss" is export-/data-gated, not genuinely fillable. Full ctest 72/72 green. (Test-only; no VERSION change.)
Two new GUI suites (real app shell, headless offscreen). tst_visual_regression grabs every home-reachable screen via the offscreen software renderer (the screenshooter's path) and requires a non-trivial spread of distinct colors — catching the class tst_qml_screens can't: a screen that LOADS clean yet RENDERS BLANK (broken binding, offscreen content, dead provider). Deliberately NOT a pixel-perfect baseline diff (the project treats pixel assertions as a trap during UI polish, and won't commit baseline PNGs); the maps WIP screen is excluded (intentionally unbuilt). tst_acceptance states the two user-level promises as Given/When/Then scenarios over the GuiApp harness: an edited value survives save→close→reopen, and browsing every screen mutates zero save bytes ("Save File Integrity Is Sacred", as acceptance). Full ctest 71/71 green. (Test-only; no VERSION change.) This completes the test-TYPE breadth (the remaining program is the coverage-to-100% push.)
New tests/mvc/tst_model_tester.cpp — wraps every QAbstractItemModel subclass in Qt's own QAbstractItemModelTester, which validates the framework contract (index/parent/rowCount/columnCount/ data/roleNames consistency, valid-vs-invalid index handling) AND the change-signal protocol (beginResetModel/endResetModel, dataChanged, layoutChanged bracketing) that hand-written tests miss. Covers each standalone select/list model freshly constructed, every live Bridge-wired model over the populated BaseSAV fixture, and the dynamic models (Pokedex sort-reset, PC storage box switch, box selector) with a tester attached across the mutation. All models pass clean — no contract violations, now permanently guarded. 5 cases, guiless. Full ctest 69/69 green. (Test-only; no VERSION change.)
New tests/savefile/tst_signals.cpp — the suite's first dedicated signal/slot coverage. The rest of the suite asserts VALUES; this asserts the notification layer every QML binding depends on: that mutating a fragment emits exactly the right change signal (correct count, and that bound-clamped no-ops still honor their always-emit contract), and that the internal connect() wiring actually fires — verified by spying the DOWNSTREAM signal (PokemonMove ppUpChanged/ppChanged → ppCapChanged). Also covers PlayerBasics (badgeSet, the targeted randomizers emit exactly one signal, reset() fans out to every field signal). 9 cases, guiless. Full ctest 68/68 green. (Test-only; no VERSION change. Broader model dataChanged/layoutChanged emission is covered structurally by the QAbstractItemModelTester pass.)
Follow-up to the static-analysis pass. The PokemonMove constructor, when filling an empty slot (move == 0), calls randomize() — which assigns a random 0-3 PP-Ups — and was meant to then reset that to 0 so a brand-new move starts clean. The original ppUp = 0 assigned the constructor PARAMETER (which shadows the member), so it was a no-op and new moves silently kept a random PP-Up count. Now writes this->ppUp = 0. Scoped to move construction only — the deliberate "randomize everything" actions are unaffected. Confirmed intended with Twilight. Full ctest green. VERSION 0.14.1-alpha → 0.14.2-alpha (PATCH).
Added the project's missing static-analysis / linting layer. New repo-root .clang-tidy (a curated, DEFECT-focused check set — clang static analyzer + the high-signal bugprone/performance/unused checks, with the noisy/style families and three deliberate-project-idiom checks documented-OFF), .cppcheck-suppressions, scripts/lint.ps1 + scripts/lint.sh (run clang-tidy over build/compile_commands.json, cppcheck if present, and qmllint — qmllint is informational-only because it can't resolve the project's C++-registered QML types under the no-qt_add_qml_module design; cppcheck is informational too until a validation pass), and a lint GitHub Actions workflow. CMAKE_EXPORT_COMPILE_COMMANDS is now forced ON. clang-tidy runs file-parallel (a serial sweep of ~140 Qt TUs is ~25 min on a 2-core runner) and the gate is clean (143 TUs, 0 findings).
Running it surfaced real defects, now fixed: PokemonBox::expLevelRangePercent() did integer division (curExp / expEnd, both var32) before widening to its float return — the fractional percent was always truncated to 0 until the very top of a level (fixed to float division + a divide-by-zero guard; regression-guarded by a new mid-window assertion in tst_pokemonbox); an unguarded toType1->ind deref in PokemonBox::update()'s type2 else-branch (now guarded, matching the type1 path); a dead store in PokemonMove's ctor (ppUp = 0 assigned the shadowing parameter, a no-op — removed); a signed/unsigned char compare in PokemonStorageSet; an int-multiply widening in WorldScripts::reset(); plus cheap const-ref loop fixes (fontpreviewprovider, eventdbentry, scripts, spriteSet) and a missing switch default in PokedexModel. One latent finding is suppressed + flagged for review: ~ItemMarketEntry() can call the pure-virtual _whichType() (UB) on the unusual path where whichType() was never cached during the object's life — a lifetime refactor of that UAF-historied area, deferred to Twilight. Full ctest 67/67 green. See notes/plans/testing.md → "Static analysis / linting". VERSION 0.14.0-alpha → 0.14.1-alpha (PATCH).
Informed-consent + glance polish on the Pokémon editor. New reusable fragments/general/ConfirmPopup.qml (centered alert modal — title, body, Cancel/Confirm — matching the storage "Boxes Formatted" warn) with ask(title, body, action, label). One shared popup is threaded (confirmAsk) PokemonDetails → DetailsPages → MovesTab → PokemonMoveSel, so every destructive action now confirms first: whole-mon Re-Roll, Reset, Max Out, Correct Data, Evolve / De-Evolve, the move bulk ops (clear-all-but-first, re-roll all, make all valid), and per-move Delete / Validate. Non-destructive / individual-field actions (Heal, per-move randomize, per-field re-rolls) are unchanged. The Evolution group is hidden entirely when the species can neither evolve nor de-evolve, and each arrow stays disabled when that direction isn't possible. The GlancePane sprite now shows a status-condition badge (sleep / poison / burn / freeze / paralyze) in its upper-right when afflicted (same decode + assets as the storage grid), and the level field fits 3 digits ("100"). QML-only; tst_qml_screens + tst_gui_moves + full ctest (67/67) green; screenshot-verified. VERSION 0.13.1-alpha → 0.14.0-alpha (MINOR — a notable UX/safety feature set).
Follow-up to the previous entry: the mon-wide action groups (Data [Reset | Max Out | Correct Data] and Evolution [← fish →]) moved OUT of the Moves-tab body and INTO the DetailsPages header (accent) bar, right of the General / DV-EV / Moves tabs — so they're visible on every tab, which is the right home for mon-wide actions. Styled light for the accent bar via a new HdrBtn (light icon + light hover wash + light divider), light-bordered groups; the fish centrepiece is a non-interactive Button icon so it tints light reliably. QML-only; tst_qml_screens + tst_gui_moves + full ctest (67/67) green; screenshot-verified. VERSION 0.13.0-alpha → 0.13.1-alpha (PATCH).
Bundle of editor changes (Twilight's spec). The preview pane (GlancePane: sprite + stats) is ~10% wider — the PokemonDetails split went 62/38 → 58/42. Validate no longer leaves a gap: the per-move validate now calls a new PokemonBox::correctMoveAt(ind) that runs correctMove() (which can clear a duplicate/invalid move) and then compacts the list so the later moves slide up — no hole. The old footer Toolkit menu (Max Out / Correct Data / Reset / Evolve / De-Evolve) is gone; its actions moved into a new actions section below the move rows in the Moves tab: a Data group [Reset · Max Out · Correct Data] (icons down-left-and-up-right-to-center = reset, up-right-and-down-left-from-center = max out, file-circle-check = correct) and an Evolution control [← fish →] (de-evolve / evolve arrows flanking the FA fish). The footer is now Re-Roll + Heal (AppFooterBtn3 → AppFooterBtn2). New icons downloaded + added to app.qrc (FA already credited). New test box_correctMoveAtCompacts; full ctest (67/67) green; screenshot-verified. VERSION 0.12.5-alpha → 0.13.0-alpha (MINOR — a feature + a screen restructure).
The action icons were squished — forced into 18×18 when their FA viewBoxes differ (dice 640×512, file-circle-check 576×512, trash-alt 448×512). Now each is sized to its own aspect at a common height (dice 22×18, file 20×18, trash 16×18) so the glyphs aren't compressed (the dice especially reads wider), and each button carries a uniform Layout.preferredWidth: 36 so the group stays even and lines up with the top bar. Applied to both the per-row reveal and the top-bar bulk group. QML-only; full ctest (67/67) green; screenshot-verified. VERSION 0.12.4-alpha → 0.12.5-alpha (PATCH).
Small alignment/polish on the row tool reveal: the slid-in tool group's rightMargin now matches the top bar's (10), so the per-row [validate | random | delete] buttons line up in the same columns as the top-bar bulk buttons above them (they were ~6px off). Removed the panel's hairline left edge/border effect — the slid-in panel is now just the plain row-coloured backing hiding the value box. QML-only; tst_gui_moves + tst_qml_screens + full ctest (67/67) green; revealed state screenshot-verified. VERSION 0.12.3-alpha → 0.12.4-alpha (PATCH).
Another polish round on the row tool reveal. The slid-in panel no longer uses a solid accent surface with big stretched (fillWidth) light icons — that read out of place and the leftmost button was wider than the rest. It now uses the row colour as the backing (it just hides the value box behind it, with a hairline left edge) and the tools are the exact same bordered icon group as the tab's top bar (dark icons, even SegBtn widths) so they look native. Validate is now the file-circle-check FontAwesome-free icon (downloaded + added to app.qrc; FA already credited) on BOTH the per-row reveal and the top bar. Reordered to a consistent validate · random · destructive across both: the per-row group is validate / random / delete and the top bar is validate / random / clear-all. QML-only + 1 asset; tst_gui_moves + tst_qml_screens + full ctest (67/67) green; rest + revealed states screenshot-verified. VERSION 0.12.2-alpha → 0.12.3-alpha (PATCH).
Polish on the Moves-tab row tools from review. The hover reveal used to only PARTLY cover the value box (and covered it from a whole-row hover); now a small chevron handle (angle-left) sits in a reserved right-edge lane — the value box stays fully visible/editable at rest — and hovering the handle (or the panel) slides in a SOLID accent-colored tool panel that completely covers the value cluster, with light (white) icons, reading clearly as a revealed tool collection. Icon fixes for consistency: the top-bar clear-all-but-first is now a trash (trash-alt, matching the per-row delete) instead of a broom; make-valid (per-row + top-bar) is now a wand (magic) instead of check-double, which is already the select-all icon. Still an overlay over a clipped row, so no reflow. QML-only; tst_gui_moves + tst_qml_screens + full ctest (67/67) green; rest + revealed states screenshot-verified. VERSION 0.12.1-alpha → 0.12.2-alpha (PATCH).
Fixes from in-app review of the Moves tab. The PP / PP-Ups views were desyncing — flipping between them (or hitting "max") could mix the two fields. Root cause: one shared text box wrote pp or ppUp depending on the view, and the maximumLength flip on toggle truncated the text into a cross-field write. Fixed by construction: two independent text boxes (one per view, each bound to and writing ONLY its own field), swapped by a StackLayout. The row action buttons (randomize / make-valid / delete) now slide in from the right on hover (an overlay over an opaque row-matched backing — never reflows the row; the row is clipped so they're hidden at rest), reclaiming the row real-estate the user wanted. Dropped the min/reset arrow (kept only the →| max). Added a dedicated tst_gui_moves GUI suite that drives the live editor: toggling the view never mutates data, the PP/PP-Up boxes are independent + write through, and model changes reflect in the right box — plus a model-level move_ppAndPpUpIndependent test. Full ctest 67/67 green (the new GUI test included); both views + the hover reveal screenshot-verified. VERSION 0.12.0-alpha → 0.12.1-alpha (PATCH).
Big functional pass on the Moves tab (Twilight's spec). Fixed-width columns (type chip 58px, PP editor, actions) so move name + type no longer jitter row-to-row. A tab-level PP / PP-Ups segmented toggle (top bar) flips what every row's editable box edits: current/max PP, or current/max PP-Ups (max 3, typeable) — one view for the whole tab, so it stays consistent as moves are dragged. Each row's value box is flanked by the arrow-to-line |← / →| buttons (set to min 0 / max cap), reusing the DV/EV icons. The ⋮ overflow menu is gone; in its place a per-move connected action group — randomize this move (dice), make valid (check-double), delete (trash). Delete compacts the list (no gaps; later moves slide up) via new PokemonBox::deleteMoveAt(). A top bar carries the view toggle plus three bulk actions: clear-all-but-first (broom → new clearMovesButFirst()), randomize all (dice), make all valid (check-double). All icons already in the FA set + qrc (no downloads). New tests box_deleteMoveAndClearButFirst; tst_qml_screens + full ctest (66/66) green, both views screenshot-verified. VERSION 0.11.0-alpha → 0.12.0-alpha (MINOR — a substantial feature set).
Follow-up to the Moves-tab redesign — in-app review showed the tab rendering empty, which a zero-warning screen-load test can't catch (it only checks for QML warnings, not that rows are visible). Three distinct bugs, found with the headless screenshooter:
Brought the Moves tab up to the General/DV-EV style language and made moves reorderable by drag. The four type-colored pills became zebra rows in one white grouped panel (scrollbar-lane reserved); each filled row keeps its type identity via a colored left accent strip + a faint type chip, with the move combo / PP field / / maxPP / ⋮ overflow menu inline (every prior action preserved — PP-Up submenu, per-move restore/re-roll/correct, All-Moves bulk ops). A left grip handle (filled rows only — empties stay parked at the bottom per game move-list compaction) drags a row to reorder: the row content reparents to the window overlay so the ghost floats free, a dashed insertion caret marks the drop slot (top edge = before, bottom = after, so the lower half of the last move appends), and the mutation is deferred a tick — the same mechanics as the Bag list drag. Backed by a new PokemonBox::reorderMove(from, to) (Q_INVOKABLE) that anchor-splices the filled move slots' (id/pp/ppUp) values among the fixed slot objects — the slot QObjects keep their identity so QML's movesAt() pointers stay valid, PP rides with its move, and only the move bytes the reorder touches change (byte-fidelity preserved, confirmed by tst_gui_fidelity). PokemonMoveSel/MovesTab were made fully null-safe on a transient move (the Repeater delegate reads through boxData during build/teardown). New regression test box_reorderMove; tst_qml_screens + tst_gui_fidelity + full ctest (66/66) green. VERSION 0.10.1-alpha → 0.11.0-alpha (MINOR — a whole-tab redesign + a new feature).
Round of refinements on the new DV/EV tab. The Max/Reset action combo swapped the vertical double-chevrons for horizontal "arrow-to-line" icons — |← (reset to 0 / minimum) on the left, →| (max) on the right, dice re-roll between — ordered low→high (two new hand-authored FA-style SVGs, arrow-left-to-line / arrow-right-to-line, added to app.qrc). Fixed the segmented-control "Market" flat-edge — an active end segment's accent fill now follows the group's rounded corner (per-corner topLeftRadius/… on the fill, Qt 6.7+; added a last flag beside first). Future Shiny moved its label above its row, and now shows only on the DV tab (shininess is derived from DVs, so it's meaningless under EVs). EV Max/Reset already shared the DV combo, so both kinds get the new icons identically. QML-only + 2 assets; tst_qml_screens + full ctest green, screenshot-verified. VERSION 0.10.0-alpha → 0.10.1-alpha (PATCH).
Redesigned the DV/EV tab to match the General tab's language (one white grouped panel of zebra-striped rows, connected "combo" controls, no loose buttons or ⋮ menus). The DV/EV toggle is now a segmented [DV | EV] switch, and the old per-kind overflow menu became a connected [Max | Re-Roll | Reset] icon combo (double-up / dice / double-down) that retargets to DVs or EVs by the active mode. Future Shiny dropped its checkbox and ⋮ menu for a segmented [Shiny | Non-Shiny] control plus a dice re-roll button: the selection is reactive — bound to boxData.isShiny, so dragging the DV bars into a (non-)shiny combination flips it too; clicking a segment forces that state, and the dice re-rolls a fresh combination within the current state. New inline SegSel component (a selectable text segment whose active is a data binding, not a checkable toggle, so it never drifts from the underlying value). QML-only; tst_qml_screens + full ctest green, screenshot-verified. VERSION 0.9.1-alpha → 0.10.0-alpha (MINOR — a whole-tab redesign, like the General tab).
The redesigned (slimmer) panes left the details header overflowing: the General/DV-EV/Moves tabs stretched edge-to-edge with big gaps, and the species/level/status top bar ran off the narrower right pane. Gave the TabButtons a content width so they sit snug instead of spread, and narrowed the species picker (font*14 → font*10; Gen 1 names are ≤10 chars). QML-only. VERSION 0.9.0-alpha → 0.9.1-alpha (PATCH).
A big iterative redesign of the Pokémon details General tab, capped by a real bug fix. Fixed type combos showing the wrong type (Charizard read as Ghost/Fighting): TypesModel has a "-----" placeholder at row 0, so a type at store index i is at model row i+1, but valToIndex returned i — selecting every type one row early. One-line fix (ret = i + 1); the mon data was always correct (verified by a save-load test). The redesign: per-field actions are now connected segmented "combo" groups (Nickname/Type/OT get [randomize | clear], Nature gets randomize) instead of loose icons / ⋮ menus; rows are grouped into one zebra-striped panel with muted labels (Bag/Market feel); the sprite pane is narrower (~38%); Exp sits between Lv. N / Lv. N+1 markers with a hover value; Catch Rate became a "Catch Difficulty" Easy↔Hard slider (markers above the bar, byte on hover); Type clear resets to the species' DB default via a new PokemonBox::correctTypes() (the multi-bool update() is unreliable from QML). Also fixed a latent GlancePane overlap (the stats had no width, so the sprite overlapped them) by giving the stat groups a real implicitWidth. New regression tests: tst_models (valToIndex row correctness), tst_pokemonbox (correctTypes → Fire/Flying), tst_storage (loaded-mon data). Full ctest green. VERSION 0.8.15-alpha → 0.9.0-alpha (MINOR — a whole-screen redesign, not a small fix).
Brought the Pokémon details editor — one of the last screens carried over from the 2020 build — up to the conventions the later screens established. The General tab's ⋮ overflow menus (Nickname / OT Name / OT ID) no longer sit under the Material scrollbar: the scrollable ColumnLayout now reserves the 16px scrollbar lane (availableWidth - 16), and the Exp slider's one-off rightMargin: 25 was replaced by that shared lane. Added the standard textH/comboH height knobs so the tab's text fields and combos line up at consistent row heights (the self-sizing nickname NameDisplay was left alone). Cleaned up GlancePane — removed a dead commented-out width block and collapsed two identical-branch ternaries on the HP slider's to and track colour to plain calls (behaviour unchanged) — and fixed a "Corect Move" → "Correct Move" typo in the move overflow menu. QML-only; tst_qml_screens green (pokemonDetails clean) and full ctest 66/66. VERSION 0.8.14-alpha → 0.8.15-alpha (PATCH).
Accuracy and reach pass on the item tooltips. Corrected the locations: every TM now names where it's actually obtained — the nine Celadon TM-counter buys, the eight Gym-Leader gifts, the three Celadon Game Corner prizes (Dragon Rage 3,300 / Hyper Beam 5,500 / Substitute 7,700 coins), the Celadon-roof drink-for-TM girl (Ice Beam / Rock Slide / Tri Attack), and the ground finds by route/dungeon (pulled from the map data — maps.json — and the pokered scripts). Fixed the errors Twilight caught: Potion is no longer called the cheapest heal (the vending Fresh Water is cheaper and heals more), the Master Ball no longer claims it "can't be sold," and the PP restoratives / Moon Stone / Exp All got real find spots instead of "in the field." Wired the tooltip beyond the Market: it's on the item picker (SelectItem) now — both the selected combo and each dropdown row — so it appears on the Bag/Items screen and anywhere an item is chosen, tied to the item rather than the screen (ItemSelectModel gained infoForInd / the itemSelectInfo role). Full ctest 66/66. VERSION 0.8.13-alpha → 0.8.14-alpha (PATCH).
Completed the per-item tooltip content. Every one of the 152 item entries now has an authored info blurb — what it is and how it's obtained in the US English Red/Blue game: the medicines, balls, stones and vitamins with where they're sold; the key items with who gives them and where; the TMs (move + effect, with the nine Celadon Dept. Store purchases and the eight Gym-Leader gifts named) and the HMs; and the glitch entries explained at the code level (badges / Pokédex / Coin as stray bag items, the unused ITEM_xx slots, the elevator FLOOR_xx markers). Also centered the "i" in the tooltip's title badge (it was sitting low and to the right). Content-only in items.json plus a one-line QML alignment fix; full ctest 66/66. VERSION 0.8.12-alpha → 0.8.13-alpha (PATCH).
First slice of the per-item detailed tooltips. Items can now carry an authored info blurb — a new optional field in items.json, surfaced through ItemDBEntry::getInfo. The Market exposes it via a new InfoRole / dataInfo (market entries gained an infoText(); store and player rows return their item's info). A new DetailToolTip component overhauls the help-mode tooltip: a small info badge + a bold title (the item name) over a wrapped body, shown only when the header "?" toggle is on and the row actually has info. It's wired onto the Market list rows. Authored the first 11 entries — the Special items (Nugget, Rare Candy, Max Revive), the Vending-machine drinks, the priced Glitch items (Safari Ball, the unused ITEM_32 "PP Up 2", the Game-Corner Coin — each explained at the code level), and two Normal examples (Potion, Ultra Ball). Remaining items' info, the auto-derived facts (teaches / found-at from the existing cross-refs), and wiring the tooltip into the other item screens come next. Full ctest 66/66. VERSION 0.8.11-alpha → 0.8.12-alpha (PATCH).
Standing rule from Twilight: plain "Poke" is a typo — it must always carry the accent (Poké / Pokémon / Pokédex / Poké Mart / Poké Ball / Poké Doll / Poké Flute), in and out of the app. Swept every user-visible occurrence: the screen titles (router.cpp — Pokédex, Pokémon, Pokémon Details), the Home tiles and the Market venue label (HomeIconsModel, Pokemart.qml → "Poké Mart"), ~20 in-app help tooltips across the Pokémon-details / trainer-card / tileset screens, and the four item display names in items.json (Poké Ball, Poké Doll, Poké Flute, Pokédex). Code identifiers, route ids, QML filenames and the uppercase internal item name keys are deliberately left alone. One data follow-on: maps.json referenced the ground-pickup Poké Ball by its readable name — which is also the link key (MapDBEntrySpriteItem::deepLink resolves the item through the shared name index) — so that reference was updated in lockstep (the mismatch was caught by tst_sprite_data), and tst_bridge's title assertion was updated. The README now also states the editor targets the US English release of Red & Blue. The app's own name ("Pokered Save Editor") was left as-is pending Twilight's call. Full ctest 66/66. VERSION 0.8.10-alpha → 0.8.11-alpha (PATCH).
The Sell view listed every item in the player's Bag/Storage, including ones that can't be sold. It now lists only items with a discernible price — Item::canSell() (i.e. price >= 0). Items with no price at all (Town Map, the HMs, badges, the key items) drop off the sell list instead of sitting there as dead rows. Free items stay sellable: a price-0 item like the Master Ball still counts (you can sell it for nothing) — it just isn't buyable (the buy gate already requires a price > 0, so free items never appear in the Normal/Vending/Special buy groups). Code-only, in buildPlayerItemList. tst_market_model + tst_qml_screens green. VERSION 0.8.9-alpha → 0.8.10-alpha (PATCH).
Refines the earlier split after a UX pass with Twilight. The store list groups priced items by how the real game sells them, while every priced item stays fully buyable and sellable in the editor (it infers a buy price, a half-value sell price, and a coin/money equivalent):
Items with no price anywhere (pokered prices.asm 0/absent — Master Ball, Moon Stone, the PP restoratives, the key items) are still listed in none of these. Buyability comes from marts.asm + vending_prices.asm, carried as item-index sets in code (no JSON touched). The Game Corner header stays "Inventory". ItemMarketModel only; tst_market_model + tst_qml_screens green. VERSION 0.8.8-alpha → 0.8.9-alpha (PATCH).
The Pokemart's catch-all "Normal Items" section is now two sections that match how Gen 1 actually works. Normal Items are the ones a reachable Poke-Mart really stocks — you can buy and sell them. Unbuyable Items are priced (so still sellable) but sold by no mart: Nugget, Rare Candy, Max Revive, the vending-machine drinks, and every TM that isn't on a Celadon shelf. Buyability is taken straight from pokered data/items/marts.asm (the union of every reachable mart's stock — the two unused clerk texts are ignored, since their stock is never purchasable and is sold elsewhere anyway). An item with no price in any currency (price 0 / absent in prices.asm — Master Ball, Moon Stone, the PP restoratives, the key items) is listed in neither section: there is no way to buy or sell it, so it isn't shown. The Game Corner's "Pokemon Inventory" section is renamed "Inventory" and gets the same Normal/Unbuyable split. C++-only in ItemMarketModel (the mart stock list lives in code, no JSON touched); full ctest green (66/66). VERSION 0.8.7-alpha → 0.8.8-alpha (PATCH).
Small follow-ups: the Market now opens on Buy + Pokemart (ItemMarketModel::isBuyMode defaults to true). The exchange ±delta superscript no longer shifts the balance number — it's overlaid at the number's top-right (the number's box is sized to itself; the delta is anchored, out of the layout) so the number stays put as the delta appears / changes length. Renamed the Game Corner section "In-House Exclusives" → "Pokemon Inventory". Dropped a clunky explanatory line from the release-notes template (release.yml). QML + a tiny C++ default/string; full ctest green (66/66). VERSION 0.8.6-alpha → 0.8.7-alpha (PATCH).
Polish on the Market screen: (1) the converter's two sides are now an even 50/50 split (both columns Layout.preferredWidth: 1 + fillWidth; the Money side was wider because its bigger number drove the implicit width). (2) The ±change is now a small superscript at the top-right of each big balance (a Row of [number][tiny delta] with the delta anchored to the number's top) instead of a line floating above. (3) The two shop panes line up again: moving the strips to one full-width header had left the right pane with a tall accent "Cart" bar the left pane lacked — the right header is now a tint section-header (CART ×N) matching the left list's section headers (same height/style). QML-only; tst_qml_screens green. VERSION 0.8.5-alpha → 0.8.6-alpha (PATCH).
Fixed the exchange rate. The money exchange was inverted — it gave ~20 coins per ₽1 instead of costing ₽20 per coin. The rate (gameCorner.json price: 20 = ₽20 per coin, the basis for the whole Game Corner/Mart economy) was always right; the C++ used it backwards. ItemMarketEntryMoney now treats the traded unit as one coin (onCart = coins): buying N coins costs buyRate·N money, selling N coins returns sellRate·N (the half-back policy). Added moneyDelta()/coinsDelta(); rewrote onCartLeft/canCheckout/checkout and the model's exchangeMoney/CoinsAfter around them. No JSON changed (data was correct).
Redesigned the converter to be immediately obvious: MONEY on the left, COINS on the right, each with a big live balance, a +/- change above it, and a button right under it — "+ Coins" (buys a coin, near coins) and "+ Money" (sells a coin, near money) — with the per-coin rate under each (₽20 / coin, ₽10 / coin). The two buttons drive one net axis (new exchangeAdjust(±1) / exchangeNet model API; new exchangeBuyRate/exchangeSellRate props); they auto-repeat on hold and disable at the money/coin caps. (Replaces the spend→get lanes.) Tests: exchangeMode_swapsMoneyForCoins (now ₽20/coin)
Rewrote release.yml's Compose release notes step so a GitHub Release actually describes that deployment instead of pointing users elsewhere. New structure, top→bottom: what the app is → prerelease note → "What's new in this release" → "How to get it" (downloads) → Links (Pages docs, full changelog, issues), with the auto "What's Changed" commit list appended below. The "What's new" section is pulled from our living changelog automatically: it finds the previous release tag by ancestry (git describe, robust to the version reset) and extracts the entries added to notes/version/ since then (already plain-English). A hand-written notes/releases/<version>.md overrides the prose for a big release. The release job now checks out with fetch-depth: 0 + fetch-tags. CI-only (no app change, no version bump). Existing stray/old release bodies can be backfilled on request.
Reworked the Exchange experience so it stops feeling bolted onto the shopping cart. The two segmented strips moved to a single full-width header, and the body now switches: Buy/Sell shows the two-pane shop; Exchange shows a focused converter card — both balances (Money ⇄ Coins) shown large with a live resulting balance under each, and the two swap lanes ("₽ → ⭘" and "⭘ → ₽") each with a -/amount/+ stepper and a live "= you get" readout. Checkout still commits. New inline StepPill component (shared by the shop rows and the converter lanes). New model role dataMoneyDir (1 = Money=> Coins, 0 = Coins=>Money) so the lanes label themselves. The old dual-currency receipt variant in the right pane is gone (the converter replaces it). VERSION 0.8.3-alpha → 0.8.4-alpha (PATCH). Full local ctest green (66/66, incl. the empty new-file GUI path).
Two small UI fixes and a version-policy correction. UI: the screen's header title is now "Market" (router.cpp screen registration; the Home tile already was, and the venue tab stays "Pokemart"); the cart pane is a bit wider (0.33 → 0.37 of the screen).
SemVer reset (Twilight's call). The MINOR number had been bumped too eagerly (every feature → 0.8→0.9→0.10), running it "way too late". Policy going forward: PATCH is the default for almost everything (the PATCH number is not capped at 9 — let it run to 2-3 digits), and MINOR is reserved for genuine milestones only. The line is reset back to the 0.8 series: VERSION 0.10.1-alpha → 0.8.3-alpha (continuing from the released v0.8.2). The already-published v0.9.0 / v0.10.0 / v0.10.1 pre-releases on GitHub are stray higher numbers (same work, now folded into the 0.8 line) — they can be deleted on request to tidy the release list. Guidance updated in VERSION + reference/versioning.md. Full local ctest green (66/66).
Small Pokemart UI tweaks (Twilight): (1) the Home tile label is now "Market" (the venue tab stays "Pokemart"; HomeIconsModel.qml only). (2) Exchange moved from the action strip to the venue strip: action is now [Buy | Sell] (disabled while Exchange is selected), venue is [Pokemart | Game Corner | Exchange]. (3) Fixed the segmented control's selected fill not following the rounded ends — clip is rectangular, so the end segments' fill now uses per-corner radius (topLeftRadius/… , Qt 6.7+) to match the strip's radius. QML-only; tst_qml_screens green. VERSION 0.10.0-alpha → 0.10.1-alpha (PATCH).
The Pokemart cart now holds buys and sells together in a single currency. Within a venue (Pokemart = money, Game Corner = coins) you can cart purchases AND sales at once; the Buy/Sell strip only filters the left VIEW, it no longer splits or clears the cart. The receipt itemizes everything with + for sells, - for buys, nets to one signed total, and shows the balance after. Exchange stays its own (separate- currency) mode — money<->coins can't net into a single-currency total.
Model work: ItemMarketEntryStoreItem/PlayerItem no longer mode-gate on the buy flag (compat Either/Either); direction is intrinsic, carried as a plain cartSignVal member (sell +1, buy -1). totalWorth() is now the signed net and moneyLeftover() = currency balance + net. The build creates BOTH sell and buy rows into one list, tagged ViewBuy/ViewSell; a new ItemMarketViewModel (brg.marketViewModel) slices the left list to the active view and re-filters on the Buy/Sell toggle WITHOUT a rebuild (so the cart survives). New roles dataViewTag / dataCartSign. Footer is a single Checkout button.
Use-after-free fix (found via the GUI tests' new-file path). The entry aggregates (totalWorth/canAnyCheckout/totalStackCount) swept the global static instancesCombined/instances registries, which accumulate rows across every model + every per-test app. Building store rows in every mode (this change) plus the per-test app teardown made an aggregate read a freed entry (0xfeeefeee, crash from a QML binding). Fixed by sweeping the current model's own live list via a new ItemMarketEntry::activeList (set by ItemMarketModel::buildList) instead of the cross-app statics. Tests: tst_market_model unifiedCart_buyAndSellNetTogether; the GUI nav/fidelity suites (the empty new-file path) now pass. Full local ctest green (66/66). VERSION 0.9.0-alpha → 0.10.0-alpha (MINOR).
Replaced the static mode title and the Buy/Sell + Currency footer buttons with two connected, single-select segmented control strips in the left header: action Buy / Sell / Exchange and venue Pokemart / Game Corner (venue disabled in Exchange). The footer is now a single Checkout button (AppFooterBtn1). The segmented control is an inline component SegStrip styled for the accent bar (selected = light fill + accent text; connected, no gaps).
Exchange is now its own mode/list instead of a confusing "Money Exchange" section wedged atop the Game Corner buy/sell lists. Model work:
Tests: tst_market_model exchangeMode_swapsMoneyForCoins (build = 3 rows; carting Money=>Coins lowers money by the spent amount, raises coins, and checkout applies it); the helper resets exchange mode defensively. Full local ctest green (66/66). VERSION 0.8.2-alpha → 0.9.0-alpha (MINOR — new feature).
The Layout.horizontalStretchFactor 5:3 split wasn't landing where intended (the receipt looked ~20% wide). Switched to an explicit width: the receipt pane is now Layout.preferredWidth: paneRow.width * 0.33 (fillWidth: false) and the list takes the rest (~67%) — a deterministic 1/3 cart. VERSION 0.8.1-alpha → 0.8.2-alpha (PATCH).
Made the left shopping list look like a real list instead of centered labels/textboxes/buttons. Each item is now a clean left-aligned row – name | owned (sell) | unit price | an inline -/qty/+ stepper "pill" as the row's action – with a whole-row hover highlight (accent-tinted) and subtle zebra striping, faint separators, and "msg" rows restyled as proper uppercase section headers with a tint bar. The qty field is borderless inside a rounded stepper pill (which turns white on row hover). Dropped the old per-row running cart total (the receipt itemizes + totals it better). Unsellable rows show a muted "Can't sell" in place of the stepper. The receipt pane was narrowed (panes are now 5:3 via Layout.horizontalStretchFactor, giving the list more room). QML-only; tst_qml_screens green (pokemart loads clean). VERSION 0.8.0-alpha → 0.8.1-alpha (PATCH).
Reworked the Pokemart screen from a single scrolling list into two panes: the shopping list on the left (unchanged — the centered -/amount/+ stepper rows, where you build the cart) and a store-style receipt on the right. The receipt itemizes the cart instead of summing it: money on hand, then one buy/sell line per carted item (xQty @ unit and a signed line total), a divider, the Total, the Balance after, and any not-enough-money / no-space warning right by the total. Empty cart shows a "Your cart is empty." placeholder. The floating summary box and the old bottom status bar are gone (folded into the receipt). Footer (Buy/Sell · Checkout · Currency) unchanged.
Backed by a new ItemMarketCartModel (mvc/itemmarketcartmodel.*, brg.marketCartModel) — a QSortFilterProxyModel over ItemMarketModel that keeps only rows with cartCount > 0 and drops the "msg" section headers, inheriting the source role names so the receipt delegate reads the same data* roles. It re-filters live (the market model emits per-row + model-wide dataChanged on every cart edit), so rows enter/leave the receipt as their count crosses zero; the model-wide totals stay on ItemMarketModel. Wired in bridge.{h,cpp} + CMakeLists.txt. Test: tst_market_model cartModel_filtersToCartedRows. Full local ctest green (66/66). VERSION 0.7.6-alpha → 0.8.0-alpha (MINOR — new feature).
Did the error red properly instead of a magic literal. Added errorColor to Settings right beside the primary palette (Q_PROPERTY + signal + QColor errorColor = QColor("red")), exposed as brg.settings.errorColor. It's a fixed, theme-independent red — setColorScheme() deliberately leaves it alone so a recoloured palette never turns "error" into the accent (and it's not derived from primaryColor, which is the pink #d81b60). Pokemart's four red spots (moneyColor, the cart-amount field, the two summary-box signs) now read brg.settings.errorColor instead of the literal "red". Look unchanged (still red). Other screens that still use a literal "red" can migrate to this token over time. Full local ctest green (66/66). VERSION 0.7.5-alpha → 0.7.6-alpha (PATCH).
Follow-up to the Pokemart modernization below: that pass had swapped the error color from the literal "red" to brg.settings.primaryColor. Twilight pointed out primaryColor is pink, not red — and a theming pass is planned, so screens shouldn't bake a palette role in as "the red". Reverted all four spots in Pokemart.qml (moneyColor, the cart-amount field, and the two summary-box signs) back to the literal "red". VERSION 0.7.4-alpha → 0.7.5-alpha (PATCH).
First modernization pass on the Pokemart/Game-Corner shop screen — one of the oldest, buggiest, half-finished screens. Look and behavior are unchanged; the rebuild is internal only. Pokemart.qml delegate was rebuilt off auto-layouts: the fragile Rectangle { width: 1; height: 50 } row frame and its sibling-anchor chain became a clean centered cart stepper with the name/price/owned-count anchored to its left and the running cost to its right. Stripped the pre-2026 cruft: dead getTo() code, the unused curName(), the never-true === "phony" ternary branches, a duplicated useSigning check in moneyStr, the four mode-toggled header Texts (now one headerText()), the four manually-juggled footer status/error Texts (now one statusText() with explicit priority). The error color stays the literal "red" — deliberately not primaryColor (which is pink, and theming is coming, so screens shouldn't bake in a palette role for a plain red). The per-delegate isLast breathing-room hack became a ListView.footer. Fixed the long-noted stuck-hover Checkout button at its root in FooterButton.qml: a footer button disabled while hovered kept hovered == true (Qt stops sending hover-leave to a disabled item) and stayed highlighted — hoverEnabled: enabled forces it false the instant it disables. VERSION bumped 0.7.3-alpha → 0.7.4-alpha (PATCH). Green: tst_qml_screens (pokemart loads clean), tst_market_model, tst_bridge.
Per Twilight, the screenshot pipeline no longer auto-generates animated GIFs — those will be added manually, one at a time. Removed the three frame-sequence capture routines (tileset_anim, typing, tab_cycle) from screenshooter.cpp; the capture scripts (capture_screenshots.{ps1,sh}) and CI (pages.yml, release.yml) no longer install Pillow or auto-assemble GIFs, and Pages no longer copies *.gif. The tool now produces flat screens/*.png + editor/*.png only. scripts/make_gifs.py (the Pillow assembler) is kept for manual GIF assembly, and Pillow stays credited (note reworded to reflect manual use). Notes updated (screenshots.md, deployment.md, CLAUDE.md). Tooling/CI + a small Credits-note change — no app-logic or save-format change.
The README's intro blockquote rendered wrong as the Doxygen home page: no italics (literal underscores) and a **leaked literal </blockquote>** that the following --- turned into a stray <h1> (also polluting the nav/auto-TOC). Root cause: CI used ubuntu's Doxygen 1.9.1, which has these Markdown bugs; the local toolchain is 1.17.0, which renders fine — so a local doxygen test misleadingly passed while Pages stayed broken. Fix: install Doxygen 1.17.0 from the official binary in CI (both pages.yml and the release docs job), replacing apt's 1.9.1, so CI matches local. Also hardened the source — the intro blockquote is now single-line with *...* emphasis (renders on 1.9.1 and 1.17 and on GitHub alike). Doxygen-markdown gotcha noted in reference/documentation.md.
.github/workflows/pages.yml deploys ONE GitHub Pages site on every push to main (not a release, not the git repo): the Doxygen docs home is the root (…github.io/pokered-save-editor-2/) and the UI images live at /screenshots/<name> (+ a gallery). So the docs are browsable online and the README can embed always-current images, with zero repo-size growth and no third party. The docs main page is the README (USE_MDFILE_AS_MAINPAGE, untouched); Screenshots + GitHub are added as custom Doxygen top-nav tabs by generating DoxygenLayout.xml (doxygen -l), injecting two <tab type="user"> entries, and building with that layout. It captures headless (screenshooter under xvfb; screens/*.png
The release pipeline now works end-to-end — v0.7.2-alpha is published with all six artifacts (Windows portable zip + Inno installer, Linux AppImage + tar.gz, docs zip, screenshots zip). Linux / docs / version passed first try; the shakeout was all on Windows + one script path:
Also wired GitHub into the default workflow (CLAUDE.md): the remote tests CI must be green before FF main (not just local ctest), the release run is watched after, and the issue tracker + PRs are checked as part of prepping main for shipment (event-based, no scheduled ping) with a "now or later?" prompt — never auto-acting. VERSION unchanged (CI/deploy infra).
Added .github/workflows/release.yml to build and publish release artifacts (the deploy half; tests.yml stays test-only). It triggers on push to main and releases only when the tag v<VERSION> doesn't exist yet (i.e. VERSION was bumped since the last release; -alpha/-beta/-rc → prerelease), with a workflow_dispatch dry-run mode. Each release attaches: Windows portable .zip + Inno Setup installer .exe, Linux .AppImage + portable .tar.gz, Doxygen docs .zip, and a UI screenshots/GIFs .zip (the screenshooter under xvfb). Toolchain mirrors tests.yml (Qt 6.8.3 install-qt-action; llvm-mingw on Windows, gcc on Linux), built Release. New files packaging/windows/installer.iss + packaging/linux/PokeredSaveEditor.desktop; new doc notes/reference/deployment.md; Inno Setup + linuxdeploy credited. CI/deploy infra — VERSION unchanged. Not yet exercised on GitHub (first-run shakeout expected, like tests.yml).
The screenshots kept looking "too big" because they were captured at 1130×740 — but that's only the fresh-profile default; the app is designed for a small rectangle (its own saved window is ~751×480). The tool now captures at a fixed 750×480 (rounded), overridable with PSE_SHOT_SIZE="WxH", and resizes before start() so the QML lays out once at the final size (a late resize had left Home's greyed Maps/Options tiles unrendered). Shots now match the app's actual window. Tooling only. See notes/reference/screenshots.md.
Follow-up fixes after review. The offscreen platform (software AND default-RHI, both tested) drops all MultiEffect/layered content (Credits cards, Home's greyed disabled tiles), which made screens look washed-out/"wrong size"; only a real GPU window renders them, so the tool now runs on the native platform and shows the view frameless/Qt::Tool off-screen (no desktop flash) — and capture_screenshots.sh runs under xvfb-run so CI still renders effects headlessly (offscreen+software is the last-resort fallback). Fixed the quick-edit popup rendering bottom-right: a plain QQuickView leaves QQuickOverlay at size 0×0, so anchors.centerIn: Overlay.overlay popups don't center — fixOverlay() sizes the overlay to the window. HiDPI grabs are downsampled to the logical 1130×740. The keyboard hover now hovers a real glyph pill (so the TilePreview tooltip shows). Removed the Pokédex hover shot (not needed). Tooling only. See notes/reference/screenshots.md.
Screenshots were missing MultiEffect/layered content (Credits showed only its header+background, Home was missing its greyed disabled tiles) and looked the wrong size — because the offscreen + QT_QUICK_BACKEND=software path silently drops those items. The tool now renders through a real GPU-backed window shown frameless/Qt::Tool/off-screen (so it draws exactly like the app's QQuickWidget without flashing on the desktop); the .ps1 no longer forces offscreen (kept as the CI fallback). HiDPI grabs are downsampled to the logical 1130×740 (a 150% display was yielding 1695×1110; QT_SCALE_FACTOR doesn't override screen DPR). The typing GIF now drives the popup's name field (found by its "Enter a name" placeholder, not the tileset combo) so the textbox owns the value and the preview follows. Bag's View All is opened by clicking the real footer button (the old shown- property poke matched the wrong item on Bag). Tooling only; no app change. See notes/reference/screenshots.md.
The capture pipeline's GIFs didn't animate. Fixes (tooling only, no app change): the static name_anim sequence is replaced by tileset_anim, which drives the full keyboard's TilesetDisplay curFrame 0–7 over an Overworld/outdoor tileset (a plain name preview's frames are byte-identical — only tileset water/flower tiles move); the typing sequence now drives the name editor's field text + str per step so the live GB-font preview builds up letter-by-letter (synthesized key events never reached the modal popup field); make_gifs.py playback durations were slowed (tab cycle 260 → 850 ms etc.); and the tool now wipes its output dir each run so renamed sequences leave no stale files. Re-captured at the new 1130×740 default (the harness already drives that size). See notes/reference/screenshots.md.
Three small UI/UX tweaks. (1) The Rival screen's grey card was 360×300 while the Trainer Card's was 500×250; the rival card now uses height 250 to match the Trainer Card (width unchanged at 360, per Twilight: match the height only, rival only). (2) In the Pokémon storage View All drawer, the per-count hover tooltip used the default top-left popup placement and floated away from the number — it now anchors centered under the hovered count (x: (parent.width - width) / 2, y: parent.height + 4). (3) The default window size is now 1130×740 instead of 640×480: the first-run fallback in MainWindow::loadState() and the mainwindow.ui initial geometry, plus the two test-harness sizes (tests/helpers/guiapp.h, tests/qml/tst_qml_screens.cpp) that also drive the screenshooter — was 1024×768. Full ctest green (66/66). PATCH bump.
Added an automated, headless way to capture UI screenshots and animated GIFs of the app — no manual grabbing, no GPU, no real window, and it never touches save data. A new C++ tool projects/tests/tools/screenshooter.cpp (CMake target screenshooter, not a CTest test) boots the real UI offscreen on the shared GUI harness, loads BaseSAV.sav, and grabs every top-level screen, the Pokémon/Bag "View All" drawers, the three Pokémon-editor tabs, both text-editor modes + the keyboard tileset, hover states, and animation frame sequences — via QQuickWindow::grabWindow(). scripts/make_gifs.py (Pillow) assembles the frame folders into GIFs; scripts/capture_screenshots.ps1 / .sh build + run + assemble, writing to tmp/screenshots/ (already git-ignored). Two offscreen gotchas handled: QT_QPA_FONTDIR is set so UI text renders (not tofu), and QT_QUICK_BACKEND=software makes grabWindow() produce real pixels with no GPU (CI-friendly). Wired into the Default Workflow to run by default after a fast-forward of main. Pillow credited (Tools Used). Tooling/test only — no app-code change, so VERSION is unchanged. See notes/reference/screenshots.md.
Polished a rough round of Twilight's edits to the README into clean prose without changing their intent. Tightened the new tagline ("A Pokémon Red & Blue save editor written in C++/Qt/QML") and intro, simplified the reboot blockquote to "rebuilt from scratch in Qt/C++", fixed a status-section typo, and reworked the convictions list – the new Beautiful UI/UX conviction, plus reworded Graceful failure and No longer a web app entries. Rewrote the Assisted custom-code execution feature bullet and the Synthetic Natural randomization vision from first-person notes into readme prose. Build/test/contributing/architecture sections unchanged.
Added assets/references/ as the official home for reference material used while working on the project – project-folder references and source clones like the pret/pokered disassembly or the GitHub sources credited in credits.json. The folder is tracked via a .gitignore that ignores its contents (* but keeps !.gitignore), so the material stays local-only and restorable while the folder itself lives in the repo – the same "tracked folder, ignored contents" pattern as assets/staging/. Nothing here is a build input. (First entry written under the new inline-changelog policy: it ships in the same commit as the change, with no hash marker.)
b0cd23a - Twilight
With the changelog now living under notes/, the version-info name was redundant. Renamed notes/version-info.md to notes/version.md and the folder likewise, updating every cross-reference and relative link. As a subdirectory, notes/version/ no longer collides with the root VERSION file (the original repo-root version/ collision is why it was once renamed away). VERSION untouched.
199eb7e - Twilight
Relocated the changelog (version-notes.md + version-notes/) from the repo root into the notes/ tree as version-info, fixing all cross-links and dropping the now-redundant explicit Doxyfile INPUT lines (the recursive notes/ entry already covers them). Historical mentions of the earlier version/ to version-notes/ rename were kept and annotated. VERSION unchanged.
6bc16e5 - Twilight
Swept the notes for personal/maintainer name references and gendered pronouns, moving to a neutral, professional, shared-project tone.
6811597 - Twilight
Rearranged the UI/UX progress image and refreshed the project-status text in the README.
61a9f4e - Twilight
Fixed a doubled word, tidied grammar, and removed a dead Screenshots table-of-contents link.
aacf00d - Twilight
Toned down promotional language across the docs, stated the project's actual goal plainly, and archived the original 2020 README as a historical artifact.
96fbb92 - Twilight
Full README rewrite covering status, features, build/test instructions, contributing, and the randomization modes.
286eb47 - Twilight
Added a tmp/ scratch folder for build/run artifacts – tracked as a folder but with its contents gitignored.
6eeba9c - Twilight
The repo-root icons folder is just a gitignored staging area for importing art (distinct from the app icon tree), so it was renamed assets/staging to match its purpose. Notes updated; no build inputs affected.
7357782 - Twilight
Reorganised the test save fixtures under assets/saves/ (natural-clean, synthetic-clean, synthetic-dirty) and moved the structure template in, adding a human-readable structure.md wired into Doxygen. All fixture-relative test paths and READMEs updated; full suite green (66/66).
8e6598a - Twilight
Stopped tracking Qt Creator's per-user CMakeLists.txt.user and gitignored it.
2652422 - Twilight
Documented the Windows case-insensitivity gotcha where checking out old history collides with the VERSION file.
dab0a1d - Twilight
Renamed the changelog folder version/ to version-notes/ to clear the case-insensitive collision that had forced the .txt suffix, freeing the bare name VERSION for the version file. Updated CMake, the Doxyfile INPUT, the VERSION header comment, and all cross-links; reconfigure confirmed the version still reads 0.7.0-alpha.
bb56fa7 - Twilight
Corrected the i18n note – lupdate works fine; the apparent hang was a UAC prompt – and deferred runtime language switching until a second locale and an Options screen exist.
28083c5 - Twilight
Documented the new i18n pipeline across reference/i18n.md, the decisions, status, next-steps, and the notes index.
a375363 - Twilight
Stood up a Qt Linguist translation pipeline: qt_add_translations producing pse_en.ts to .qm at :/i18n, a QTranslator installed early in boot (honoring the ui/language setting, else system, English fallback), ~139 QML display literals wrapped in qsTr(), router screen titles via QT_TRANSLATE_NOOP, and a few C++ model strings in tr(). UI chrome only – game-data names are out of scope.
de30af9 - Twilight
Introduced a SemVer version system whose single source of truth is the repo-root VERSION.txt. CMake reads it, feeds project(VERSION), and generates pse_version.h with the git commit appended as build metadata; boot sets the runtime version from it (no more hardcoded v1.0.0), About shows the cleaned SemVer, and a Windows VERSIONINFO resource embeds it in the .exe. Starting number 0.7.0-alpha; full suite green (66/66).
bd88c0c - Twilight
Living-credits update – added Docker (now powering the Linux build/test env) under Tools Used.
a597ad0 - Twilight
Added a containerised Qt 6.11 + clang Linux toolchain under docker/ that rsyncs the repo into a persistent ext4 volume (ccache-cached) and runs the suite four ways via dtest.ps1: standard offscreen, ASan+UBSan (which are broken on the Windows kit), real-X xvfb, and llvm-cov coverage. First run all green – 66/66 each, ASan clean, 89.73% line coverage.
5bcd3e4 - Twilight
Fixed a heap-use-after-free: totalWorth() walked the shared static registry calling _itemWorth() on entries whose Item could already be freed (box rebuild/teardown). Windows read stale-valid memory; only ASan in CI caught it. Switched to QPointer with guards so stale entries return a safe default.
af4c6d6 - Twilight
Extended tst_gui_fidelity to instantiate the full Pokemon editor over a real party mon and browse it non-destructively – every tab, dropdown, and field – then assert the flattened save is byte-identical. Covers 9 tabs, 9 dropdowns, 22 fields.
fd82672 - Twilight
tst_gui_drag now also drives the PC-box reorder/delete on a runtime-populated box and verifies the changes survive a save then reopen.
b5172ce - Twilight
Extracted the shortcut-to-verb wiring into a shared pse::shortcutActions() used by both MainWindow and the test; tst_shortcuts now asserts every shortcut has an action and actually fires the safe non-dialog verbs.
4e35853 - Twilight
Notes for the new shortcut and drag E2E tests, plus recording the otherShortcuts copy-bug fix.
7a852ee - Twilight
tst_shortcuts pins every key sequence and guards against collisions; tst_gui_drag drives item reorder/transfer/delete through the live bridge and asserts persistence across a save then reopen.
9d30509 - Twilight
Moved the global shortcut key sequences into boot/shortcutdefs.h so tests can pin them, and fixed an auto os = otherShortcuts copy bug that had left the member empty (the shortcuts only survived via their QObject parent).
d9b3dbc - Twilight
Dropped the short-lived "check CI by default" guidance while keeping the record of the Qt-download flake fix.
34a1153 - Twilight
Recorded the CI Qt-download flake mitigation (and, briefly, a check-CI-by-default note).
0fe0913 - Twilight
The ASan CI job kept failing at Install Qt when aqtinstall couldn't reach the mirrors. Enabled the install-qt-action cache and pinned the exact Qt version for a stable cache key so a good download is reused.
ad61b19 - Twilight
Documented the synthetic-fixture work and the badgeCount fix, and laid out the remaining per-control testing roadmap.
557a907 - Twilight
Notes capturing the badgeCount stub fix and the synthetic fixture matrix.
03e281c - Twilight
Added a generator that builds deterministic edge-case saves (maxed/zeroed/all-badges/midgame) from a fresh new file via the engine into assets/synthetic/, with a suite asserting each loads, round-trips byte-stable, and carries its edited values; the fidelity test also browses the maxed save. Surfaced the badgeCount stub.
3f25da8 - Twilight
PlayerBasics::badgeCount() had been returning the max unconditionally (a 0-badge save reported 8); it now counts the set badge flags per its contract. Found by the synthetic fixtures.
5e413e6 - Twilight
Notes recording the comprehensive testing pass (fidelity, dirty fixtures, editor verification).
79af2b2 - Twilight
tst_qml_screens now injects a real party mon before completeCreate so the Pokemon editor's runtime bindings resolve against real data and it's held to the full zero-warning bar.
19634c9 - Twilight
Notes for the corrupt-save crash fix and the new dirty-fixture suite.
3aacb24 - Twilight
Added assets/dirty/ (empty/truncated/oversized/all-zeros/all-FF/garbage) and a negative suite asserting wrong-size files are rejected cleanly, right-size garbage loads without crashing, and bad checksums still load. Caught the corrupt-save crash fixed alongside.
bbb18d9 - Twilight
Storage::load() indexed boxes[] (0-11) with the raw current-box byte (0-127), and a malformed all-0xFF save crashed via a null deref. It now clamps an out-of-range index to 0 – a no-op for valid saves (byte fidelity intact) and a graceful default for garbage. Found by the dirty fixtures.
e8f1129 - Twilight
Added tst_gui_fidelity to the testing plan.
9d9db03 - Twilight
New tst_gui_fidelity: baseline a save, navigate every screen, open/close every modal and dropdown, focus/blur every field without typing, then flatten+recalc and assert the bytes are identical (reporting any changed offsets) – across both progressed and new saves.
1498fd0 - Twilight
Notes recording the GUI suite green on the kit, the Pokemart crash fix, and the test-harness gotchas.
91dabef - Twilight
A real-app headless GUI suite on the guiapp harness – navigation sweep, save/load-through-screens with independence and byte-stability, and synthesized-keystroke input – plus the load smoke test, all green (61/61). Includes a batch of harness triage fixes (StackView matching/waits, an allowlisted offscreen-font warning, objectName-located fields).
d5950d9 - Twilight
The Pokemart footer bindings evaluated during the screen push before the list was built, hitting an empty list and a fatal assert. Fixed by building the list in the constructor (valid from construction, independent of nav-slot order) and returning sane empties from the cart accessors. Found by the navigation test.
256cca3 - Twilight
Clarified in CLAUDE.md that the PowerShell terminal has real access to build, test, run, and git on the Windows machine – it is not limited to the sandbox.
b21a55b - Twilight
The Credits/About screen never opened due to two QML bugs: a contentWidth property on the Page root collided with a FINAL Control property (renamed to colWidth), and a Page id of top collided with the top anchor inside delegates, breaking icons/links (renamed to root). Verified on-screen; both added to fix-patterns.
f648391 - Twilight
Reworked credits.json into an ordered sections shape (adding a credit or section is now a pure JSON edit), replaced the eight hardcoded blocks in CreditDBEntry::process() with a single data-driven loop, made CreditsModel section-grouped, and redesigned About.qml with translucent section cards over the wallpaper, colourised icons, clickable links, and a version/copyright footer. Full suite green (57/57).
791e063 - Twilight
Credited ChatGPT for the rival artwork in both the AI Assistance and Icons sections.
a502364 - Twilight
Logged the rival artwork swap in status.md.
0a2aff1 - Twilight
Imported the new colored rival.png into the app asset tree and repointed the Rival screen from the old grayscale art (mirrors the trainer.png swap).
29bb763 - Twilight
Logged the Rival screen cleanup in status.md.
719f8b1 - Twilight
Replaced the Rival screen's magic-margin positioning with a centred bordered card matching the Trainer Card style (shared height knob, divider under the name), kept the simple name/image/starter stack and identical bindings, and routed access through a null-guarded rival() helper.
e606b67 - Twilight
Softened the Unformat warning – the data is effectively gone unless recovered before an editor or the game destroys it.
9757368 - Twilight
Recorded source-verified Gen 1 box format/recovery findings cross-checked against the pret/pokered disassembly.
e731288 - Twilight
View All tooltips now render species name markup (e.g. Nidoran with a male sign).
03ae2c7 - Twilight
Notes on the Gen 1 box-format triggers (box-switch / new-file only) and the recovery-opportunity theory.
6f810f3 - Twilight
Corrected the Unformat warning – the data is erased on an in-game box switch, not on open.
fe58bb2 - Twilight
Lightened the Format-boxes copy (it just unlocks) while stressing the real data danger on Unformat.
c154192 - Twilight
Polished the Format-boxes confirmation copy to reflect that the state depends on how the save was loaded.
bd3ad00 - Twilight
Reworded the Format-boxes confirmation middle paragraph for clarity.
8dc1ff9 - Twilight
Rewrote the format/unformat confirm copy to match how Storage actually behaves – within a loaded session unformatting is fully reversible by re-formatting; loss only becomes real once the save is reloaded or the game overwrites the freed space.
5cf12c1 - Twilight
Turned the footer "Boxes Setup" button into a Tools (wrench) menu with a "Boxes Formatted: On/Off" toggle that opens a direction-aware confirm (format = erase+format all but the current box; unformat = soft delete with permanent-loss risk), flipping only the boxesFormatted bit. Also made the storage grid render species gender markup generically.
b2d69a6 - Twilight
Applied the Pokedex name-fix on the storage selection grid so the gender markers show as symbols, and removed the bold on the View All box-number column headers.
2938f53 - Twilight
The View All header sort button now shows the current order's icon (using three PNGs, aspect-fit so the non-square art isn't squished), and the table was condensed roughly in half via per-column widths so it fits without sideways scroll in the common case.
fe77289 - Twilight
Polished the Pokemon View All overview – zebra row/column tints with whole-row hover, a wider species column with the Pokedex name-fix, a header sort button cycling Dex/Alphabetical/Internal, and a tooltip that appears only when something is traded (an all-caught no-nickname cell has none). Test extended accordingly.
74bb2bf - Twilight
Added a Party + non-empty-boxes overview table (rows alphabetised by species), each cell showing the per-box count with a hover tooltip of differing nicknames and a caught/traded split, backed by a new read-only PokemonOverviewModel with precomputed tooltips and wired through the bridge.
42ed2cd - Twilight
Credited ChatGPT for the gym-badge images and gym leader portraits, and marked the trainer-card badge/portrait work approved.
64797bc - Twilight
Re-processed the badge icons onto uniform 256x256 centred square canvases (content at ~98%) so earned badges and unearned shadows render at a consistent footprint. No QML change.
2af7159 - Twilight
Recropped the tall full-body leader silhouettes into square head-and-shoulders busts so they fill the badge cell uniformly with the near-square badge icons.
efdf3b5 - Twilight
Switched the badge-grid delegate from Stretch to PreserveAspectFit (with mipmap and a capped sourceSize) so the non-square art isn't distorted, and re-imported the eight badge icons after size optimisation (~1MB to ~360-430KB each).
cdbda00 - Twilight
The Trainer Card badge grid now shows the badge icon when earned and the gym leader's shadow when unearned (replacing the old on/off pair and the opacity dimming, since the shadow conveys state). Imported the artwork and swapped the qrc entries.
55f0e37 - Twilight
Updated the Trainer Card front to the revised trainer.png artwork.
579e948 - Twilight
Replaced the grayscale Gen-1 sprite on the Trainer Card front with the new colored two-trainer illustration; layout unchanged.
d333449 - Twilight
Corrected a status note to reflect that View All is a hand-rolled slide panel, not a Material Drawer.
586695d - Twilight
Stopped the bag rows' hover delete-chip lighting up through the open View All panel by adding a blocking HoverHandler to the panel and scrim, and narrowed the panel slightly.
7a0027a - Twilight
Removed the panel's right-edge divider (it read as a grey border) and stopped leftover wheel/clicks from falling through to the panes behind by adding a full-size MouseArea + WheelHandler to the panel and scrim.
54a8468 - Twilight
The Material Drawer kept leaving a white strip and frame that padding/inset tweaks couldn't fix, so it was replaced with a plain Rectangle we fully control – x-animated slide, fading scrim that closes on outside click, flush header, no frame – keeping the same overview table and behaviour.
251a26f - Twilight
Attempted to make the Drawer full-bleed by zeroing all insets and elevation and giving it a flat background (later superseded by the hand-rolled panel).
23ff6a2 - Twilight
Set the Drawer content padding to 0 to remove the white strip above the accent header.
9f26d18 - Twilight
Capped the drawer width so the fillWidth Item column stopped eating the slack.
a71853b - Twilight
Added a left-edge slide-in drawer (new leftmost footer button) showing a condensed, alphabetised table of every item the save holds – Item / Bag / Storage – backed by a new read-only ItemOverviewModel that aggregates both boxes by index (so glitch items still appear) and is wired through the bridge. Test added; full suite green (57/57).
9831f66 - Twilight
A checked item row now renders the delete chip in its filled red/white-X "armed" look permanently, driven by a lit = hovered || checked condition.
76c5be7 - Twilight
The delete chip toggled with visible:, collapsing its layout slot and reflowing the whole row on hover; it's now permanently in the layout and just fades opacity, so nothing resizes.
84c1c68 - Twilight
Made the per-row delete chip transparent at rest, and fixed the recurring "button under the scrollbar" problem by reserving the scrollbar lane and letting the item combo shrink so the trailing chip stays clickable. Documented the list/grid variant in ui-patterns.
d4a753c - Twilight
Item transfers no longer clamp a stack to 99 and drop the excess – overflow becomes its own second row, or the transfer is refused if there's no room (no silent loss, ever) – and the item dropdown now shows each item's total owned across both panes. New tests; full suite green (57/57).
d9ad035 - Twilight
Transfers now auto-stack onto an existing matching row (clamped to 99) instead of inserting a duplicate, and the item dropdown greys out names already present in the same pane. Pre-existing duplicate save data is deliberately left untouched. New tests; suite green (57/57).
07fbeda - Twilight
Brought the storage grid's drag interaction to the Bag/PC items list – rows are DropAreas, the content floats to an overlay while dragging, drag starts from a left grip handle (controls keep their input), and you can reorder within a list or transfer bag-PC. Removed the bulk-action footer in favour of a per-row delete chip. Regression tests added; suite green (57/57).
3a09889 - Twilight
Added the "Keep the Credits Screen Living" and "Default Workflow" sections to CLAUDE.md (auto build+launch, affected-then-full tests before main, debug/profile with logs, green-gated auto commit/push/FF-main) and reconciled the older git-workflow wording.
215684b - Twilight
Box-header UX pass – removed the dropdown fill circle (the count conveys fullness), made the current-box marker a leading triangle, and turned the "set current box" control into an Active toggle on both panes (hidden on Party), with the toggle colour-parameterised to read on the accent bar.
61ae6ea - Twilight
Added a new AI Assistance credits section (Claude, ChatGPT) in display order, and credited ChatGPT under Icons for the in-app status-symbol icons.
66e9f72 - Twilight
Documented the storage HP-bar thresholds, the status-badge decode and corner-sharing with the checkbox, and the RGBA asset import workflow.
046cdbe - Twilight
Ignored the contents of the assets/icons staging folder (keeping the folder via its .gitignore).
3a88495 - Twilight
Each storage mon now shows a narrow colour-coded HP bar (green/amber/red matching GlancePane) between the icon and name, and a status-condition badge in the upper-left that yields the corner to the hover/checked checkbox. Added the model roles and the status icons, decoded from the status byte.
7d9f22d - Twilight
Documented the storage drag/drop, delete-button, and scoped-checkbox-persistence conventions, plus the icon.width-int gotcha.
18ee8d3 - Twilight
Made the storage grid cells DropAreas with a manually-driven Drag – dropping reorders within a pane or transfers to the other pane at the drop slot (honouring the checked set as a group, with a dashed insertion caret and a drag threshold so plain clicks still open the editor). Replaced the bulk-action footer with a per-cell delete chip; checkbox selection is scoped to persist only across the editor round-trip.
696104d - Twilight
Corrected an include's casing so the case-sensitive Linux CI builds.
b38052b - Twilight
Stopped tracking the root CI helper log/script (_ci.log, _runci.bat) and gitignored the patterns.
971caef - Twilight
Began work on the Pokemon Selection screen, with cleanup, fixes, and polish.
b8bacf3 - Twilight
Captured the app-layer testing push and the full comprehensive-testing program as a resumable handoff in the notes.
736263b - Twilight
TypesModel's placeholder row 0 fell through to an at(-1) crash for undeclared roles (unreachable from the UI, but a latent crash); it now returns an empty QVariant. The test was extended to drive an undeclared role on every row.
9e52623 - Twilight
Extended tst_bridge to cover the PokedexModel methods the data() sweep misses – all three sort comparators, the pageClosing reset, the dex lookup hit/miss, and the per-entry change signal – lifting the worst app file off 51%.
354c244 - Twilight
A generic driver hits every row x declared role through data() across the select models (types/nature/species/status/starters/move/item/map), plus the index guards, headerData, and each model's value-row helper over edge values. Noted (gated) the placeholder-row undeclared-role crash.
07551fb - Twilight
Extended the db getter tests to cover connect-render math, DB base accessors, the qmlProtect cascade, and several entry getters, and fixed MapDBEntryConnect::xAlign()'s guard (it returned 0 for every real map, making its math dead code). All three library layers now >=90%; suite green (56/56).
6693899 - Twilight
Drove every getter over each store with deepLink so resolved-link getters take their non-null path; db overall 75->83%.
78d769a - Twilight
Exhaustive sweeps of FontSearch's filters and FontsDB's code conversion/expansion edge cases (driven from each glyph's real name so they stay data-independent), pushing both files into high coverage.
ac65d4a - Twilight
Covered the Utility/Random QML helpers (common to 100%) and all ~30 MapSearch predicates (mapsearch to 100%), and fixed a sprite-set sentinel bug where the guards treated index 0 as "none" instead of -1, dereferencing a null on maps with no sprite set. db 71.6->75.2%; suite green (53/53).
ef858ac - Twilight
Added AreaWarps setTo/randomize/list coverage and proved that isType(Cave)/isType(Outdoor) match correctly once deepLink resolves the tileset – the long-standing "matches 0 maps" note was a misdiagnosis of the deepLink landmine. Reverted a defensive bound per Twilight (Gen 1 wild tables are fixed at 10). savefile 89.4->90.2%.
5d5b62f - Twilight
Coverage-testing the area family surfaced three bugs (all approved before fixing): an inverted tileset-load ternary that crashed on null and discarded the real tileset otherwise; a Random_Pokedex roll that excluded Bulbasaur (off-by-one on the 0-based dex); and an unbounded setTo array write. Added tst_area_logic regression-guarding all three. Big coverage gains across the area files; suite green (51/51).
95307f3 - Twilight
Investigated the documented sprite-link crash first and proved (with deepLink called) that all 918 map sprites resolve cleanly – the crash is purely the tracked deepLink-not-called landmine, not a SpriteData defect. Added a 13-case suite taking spritedata.cpp from 46% to 100%. savefile 82.5->86.5%.
3cb14a5 - Twilight
isMinEvs() returned true if any single stat-exp was 0; "minimum EVs" means all zero. The real impact: StatsTab's "Reset EVs" was greyed out whenever one stat-exp was 0 even on a heavily-EV'd mon. Fixed and regression-guarded.
22e1098 - Twilight
The empty-slot bug also lived in isMaxPP()/isMaxPpUps(), which counted an empty move slot as "not maxed" so the heal indicator was wrong for any mon with fewer than four moves; both now skip empty slots. Also tracked the type2 single-write-truth and isMinEvs decisions in next-steps.
0dafe96 - Twilight
Fixed three approved type/reset bugs: update(resetType=false) no longer clobbers a dual-type mon's type2 to type1; isCorrected() treats a record as dual-type only when type2 truly differs from type1; and isPokemonReset() judges a reset against the species' real initial moves and PP-Ups. Regression-guarded.
586454a - Twilight
New 18-case tst_pokemonbox covering the active PC/party-mon logic the other suites miss – PP-Up clamping, move type/validity, randomize, newPokemon scopes, nature/level clamps, trade-status/OT data, cleanup. savefile 78->82.4%; flagged three latent type/reset quirks (fixed in the following commits).
26af370 - Twilight
Covered Storage's box-space/freeSpace/deposit/randomize verbs, taking storage.cpp from 79% to 98%.
5fb68cc - Twilight
Fixed a MAX+1 off-by-one so the recent-files list caps at exactly the limit; regression-guarded.
e28796a - Twilight
Covered FileManagement's recent-files handling and load-error reporting (64->77%) and flagged the recent-cap off-by-one fixed alongside.
c0f09b3 - Twilight
Covered PlayerBasics' OT rewrite, randomizers, and accessors, taking it from 67% to 99%.
dfd02c0 - Twilight
Covered the Item fragment's constructors, load, randomize, pricing, and clamping, taking it from 62% to 97%.
9023cbc - Twilight
Recorded the latent MapsDB::deepLink landmine and the push-every-commit git policy.
24210ca - Twilight
Exhaustively covered the SaveFileIterator cursor, taking it from 57% to 100%.
881dfa9 - Twilight
Covered the WarpData and MapConnData DB-population and resolve paths (warpdata 54->98%, mapconndata 71->100%).
6eac22b - Twilight
Covered SignData's DB-population and randomize paths, taking signdata.cpp from 40% to 100%.
354ebb2 - Twilight
Added tst_app_e2e: edits made through the app layer are saved to a real file, reopened in a fresh manager, and verified – plus a write-and-compare proving the bytes on disk equal the in-memory flatten+recalc buffer exactly. Suite green (41/41).
7157b5f - Twilight
Noted the engine-providers and QML-behavioural test tiers as complete.
f6e37cd - Twilight
Added tst_qml_brg, a Qt Quick Test harness that boots a real Bridge and drives the C++-QML property chain from QML (money/coins/playtime read+write, model reachability), guarding the session-13 "undefined chain" regression from QML's side. Suite green (40/40).
dd36ab9 - Twilight
Added a GUI test compiling the app qrc so the tileset/font PNGs resolve, covering the TilesetEngine builders, TilesetProvider (valid tile + blank fallback), and FontPreviewProvider – closing the last untested app tier. Suite green (39/39).
60d7c30 - Twilight
The market test segfaulted ~1/3 of runs from creating and destroying a Bridge per test method (the real app never churns Bridges); it now uses one shared Bridge with order-independent assertions. Verified zero crashes in 20 runs.
b16235c - Twilight
Added tst_fuzz – over 150 deterministically-seeded random field combinations asserting every edit persists across a real flatten->recalc->reopen cycle, the checksum stays valid, and flatten is idempotent. Suite green (38/38).
bff8759 - Twilight
Added tst_bench (benchmarks with hard wall-clock ceilings that fail on a hang) and tst_regression (one named guard per real historical bug). Suite green (37/37).
1cc4acf - Twilight
Recorded that the app coverage percentage is measurement noise, and that the library layers plus functional suites are the trustworthy signal.
71f801c - Twilight
Added a coins-currency sweep to the market test, covering the Game Corner money/GcPokemon/Message entry subtypes that money-mode never reached. Suite green (35/35).
0bea8c6 - Twilight
Covered MoveSelectModel's general all-moves list, the species-specific rebuild path, move-to-row lookup, and the null-mon fallback. Suite green (35/35).
7ef91d1 - Twilight
Added an off-by-default PSE_SHARED_APPCORE option so all test exes share one instrumented appcore and llvm-cov merges cleanly – revealing true app coverage of 58.2% line (the earlier 0%/43.6% were static-lib artifacts). The shipped build stays static.
8754592 - Twilight
Confirmed the low app coverage figure was a tooling attribution artifact, not a real gap.
f2c8256 - Twilight
Corrected the app coverage figure to 43.6% using a per-file-max union method.
6eba779 - Twilight
Re-measured coverage (savefile 73, db 62) and noted the app static-link measurement caveat.
0aecfe9 - Twilight
Round-tripped the AreaPokemon wild-encounter tables (rates + both 10-slot lists, with grass-swap) and AreaLoadedSprites. Suite green (34/34).
46a0072 - Twilight
Covered ItemStorageModel's data()/setData() roles, the trailing empty placeholder row, and the checked bulk ops over the BaseSAV bag via a real Bridge. Suite green (33/33).
8430ad5 - Twilight
Added a buy-mode checkout to the market test (covering the store-item/money subtypes) and a Settings test for colour-scheme derivation and tileset preview-index resolution. Suite green (32/32).
c2caa15 - Twilight
Documented the Windows-vs-Linux case-sensitivity include landmine.
6ade594 - Twilight
Fixed a wrong-case include (spriteset.h -> spriteSet.h) that Windows accepted but the case-sensitive Linux CI rejected.
aa2bde3 - Twilight
Re-measured coverage after five db suites (db 56->63).
01ec841 - Twilight
Added the canonical negative-index guard to AbstractRandomString::getStoreAt (the same crash as the nine DBs fixed earlier) and covered all five random-string sources, including draw-without-replacement. Suite green (32/32).
18830b1 - Twilight
Swept every map's child sub-entry getters (warps/signs/sprites/wild mons/connections) – the lowest-covered db files – checking getParent() links back. Suite green (31/31).
74951ae - Twilight
Covered the MapSearch fluent finder – baseline, disjoint has/no filter behaviour, index-filter edges, accessor agreement, and pickRandom staying within the narrowed set. Suite green (30/30).
1197aee - Twilight
Covered the Game Boy text codec round-trip and the FontSearch fluent filters. Suite green (29/29).
9cddcb5 - Twilight
Drove the getter APIs of the two richest getter-based db entries (ItemDBEntry and MapDBEntry), with size-At consistency checks; noted that price and encounter-rate can be negative sentinels. Suite green (28/28).
72182a1 - Twilight
Full 27-suite instrumented re-measure – every layer up (common 79, savefile 72, app 56, db 56); db now the lowest and next focus.
1f3fc4c - Twilight
Covered the Pokemart ItemMarketModel across all four buy/sell x money/coins modes, with cart tracking and a sell checkout that raises the player's money. Suite green (27/27).
fa260c4 - Twilight
Covered PokemonStorageModel's bulk checked operations (toggle-all, move, delete, transfer) via setData on a real Bridge, with ordering verified by object identity. Suite green (26/26).
0d23337 - Twilight
Covered the Random chance/coin helpers' threshold edges (over 400 iterations) and range(float) bounds including the zero-span case. Suite green (25/25).
8dcac58 - Twilight
CreditsDB::getStoreAt had an inverted guard and eight other DBs missed the negative-index check, so getStoreAt(-1) crashed; all nine now use the canonical (ind<0 || ind>=size) guard. Found by the new store sweep. Suite green (24/24).
8249e48 - Twilight
Covered the area child lists – warp/sign new/at/swap/remove with byte round-trips, plus sprite list ops and tileset talk-over-tile access. Suite green (23/23).
45a6b84 - Twilight
Covered Item amount-clamp and DB-resolved pricing, and ItemStorageBox add/move/sort/remove/worth/relocate with post-mutation byte round-trips. Suite green (22/22).
672e33c - Twilight
Covered the TilesetEngine blank-image and water-wave image math (frame identities, symmetry, rotation wrap) as pure QImage math runnable guiless. Suite green (21/21).
7cbfac2 - Twilight
Full 20-suite re-measure – app/appcore line coverage nearly doubled on the Bridge suite – recording the per-module table and the exact llvm-cov method.
176a6dd - Twilight
Covered the Day Care (including the empty-destructor regression path), Rival, and Hall of Fame regions with round-trips. Suite green (20/20).
b9970f7 - Twilight
reset() passed the dex count as memset's fill value, marking the entire dex seen+owned instead of clearing it (randomize() had masked the bug). The fill value is now 0; found and guarded by the new tst_pokedex. Suite green (19/19).
cf74e18 - Twilight
Per-species verification that every resolved-pointer vector (types, initial moves, TM/HM, learnset, evolutions) lines up with its source and resolves, plus a Bulbasaur characterisation anchor. Confirmed the DB pokedex field is 0-based (the documented dexInd+1 convention). Suite green (18/18).
6cbb1ce - Twilight
Round-tripped the current-area facets (general/audio/player/map/tileset/warp flags) and the Storage container (curBox, boxesFormatted, all 12 boxes). Suite green (17/17).
111b7e4 - Twilight
ctest had four tests Not Run because CI built a drifting explicit target list; replaced it with a CMake tests_all aggregate depending on every registered test exe, so no suite can be left unbuilt.
7440a38 - Twilight
Recorded the Bridge test suite and two app-layer landmines (the loadScreens static-init requirement and the unchecked getBoxMon/getPartyMon .at() contract).
eb832f9 - Twilight
Added a Bridge integration suite that wires every model the way the app does and sweeps each across rows x roles, plus PC storage box ops, the box selector, and Router navigation – covering the previously-untested models. Suite green (16/16).
52399a6 - Twilight
Dropped qtdeclarative and qtsvg from the aqt module list (they ship in the Qt 6 base package, not as add-ons), keeping only qtcharts.
5399a58 - Twilight
Updated the coverage baseline (savefile 67%, app 20%).
ddbc548 - Twilight
Round-tripped the remaining world regions – hidden items, missables, general flags, and scripts.
0142235 - Twilight
Round-tripped the world regions for events, towns, trades, and completion flags.
7536595 - Twilight
Covered the item-select and map-select app models.
90cee6d - Twilight
Marked the git remote setup complete – default branch main, master removed.
f346525 - Twilight
Added the git workflow standards (main fast-forward-only, frequent dev) and a pointer from the notes.
c7f47bf - Twilight
Covered five more app-layer models – species, move, status, starters, and credits.
32dc20d - Twilight
Continued progress writing tests.
fb42d69 - Twilight
Further progress writing tests.
3a03bfc - Twilight
More progress writing tests.
db86e38 - Twilight
More progress writing tests.
4af3e01 - Twilight
More tests written.
580bf02 - Twilight
Further tests written.
eb9686d - Twilight
Began adding the test scaffolding and the first tests.
f01ae86 - Twilight
Disabled the home-screen icons that lead to incomplete features.
c07d81b - Twilight
Fixed a recent-files bug on moved files and corrected file-open error handling.
852a129 - Twilight
A slight QML change for comments.
31a0341 - Twilight
The big documentation commit – used AI to macro- and micro-analyse the project and produce the initial documentation pass, as part of resurfacing the project before a full manual review. This is the oldest entry in the June 7-14 revival batch the changelog had been missing.
480105a - Twilight
Twilight's checkpoint marking the project as fully uncorrupted again after the data-loss scare, and roughly 85% of the way back to where it stood six years earlier. The actual code changes are small cleanups riding along with that milestone: the full-keyboard character picker got tidied up (the search header, criteria, params and the results grid lost some dead weight, with the results view trimmed by a large chunk), a settings tweak in the bridge, a small fix in the main window, and the running notes (status and the fix-patterns reference) were updated to reflect the new state.
13b6816 - Twilight
A milestone in the recovery: the app compiles and launches once more. This is a small, surgical commit fixing a handful of bugs left over from the earlier data destruction – one-line corrections in the database boot path, the map-select model, and the fonts database – plus notes updates capturing what was wrong and how it was fixed.
87b9648 - Twilight
A large hand-rebuild pulling the project back together from scattered backups after a corruption event. Touching 78 files across every layer, it restores heavily damaged sources – the Pokemon storage model, the font database and font search, file management, the Hall of Fame and item-storage fragments, area sign/sprite/warp parsing, the rival screen, and the main window among them – and brings the notes system (history, principles, session log, status, fix patterns) back in line. Net it rewrites thousands of lines to undo the damage rather than to add new behavior.
c9dc210 - Twilight
The commit that reopens the project after six years and begins modernizing it with AI assistance, with the stated goal of finally finishing the one project Twilight most wanted to complete. It is sweeping – on the order of 400 files – and lays the groundwork for everything that follows: a full CMake build setup across the common/db/savefile/app projects, the entire notes/ knowledge base (architecture, history, principles, decisions, Qt 6 migration patterns, UI patterns, diagnostics, session log), and a broad pass modernizing the C++ and QML to build under Qt 6. Many UI fragments and models are reworked in the process (notably NameDisplay, the Pokemon-details tabs, the Pokemart screen, and the item/move/select models). This is the inflection point between the original 2019-2020 effort and the 2026 revival.