Skip to content

Merge dev → main (automated) - #771

Merged
SorraTheOrc merged 33 commits into
mainfrom
release/dev-to-main-20260731195715
Jul 31, 2026
Merged

Merge dev → main (automated)#771
SorraTheOrc merged 33 commits into
mainfrom
release/dev-to-main-20260731195715

Conversation

@SorraTheOrc

Copy link
Copy Markdown
Member

Automated release created by ship skill.\n\nIncludes CHANGELOG.md with work-item summaries from this release.

Sorra the Orc added 30 commits July 29, 2026 19:06
… to grid

- Rewrote onBusinessCardClick to use buyBusinessToHandCommand
  (purchaseBusinessToHand) instead of buyBusinessCommand (direct-to-grid)
- Removed auto-place code path entirely (was used by T3 and T8)
- Rewrote onSlotClick to use placeFromHand instead of buyBusinessCommand
- Added 'placing-from-hand' UI phase and pendingHandIndex to scene state
- Removed 80% placement cost deduction from placeFromHand (free placement)
- Added legacy fallback in onSlotClick for backward compatibility
- Updated MainStreetPlaceSell tests to verify free placement
- Updated E2E tutorial helpers for the new buy-to-hand + place-from-hand flow
- Updated PRD documentation (placement cost is now free)
…e-from-hand in tutorial

- Changed T9 from confirm gate (upgrade concept) to action gate (place
  from hand) since auto-place was removed from T8
- Updated T9 overlay text to explain placing Bookshop from hand
- Updated TutorialFlow budget table and gate distribution comment
- Fixed E2E test helpers: clickRequiredBusinessCard now handles T8
  (step 7) force-advance; clickStreetSlot fallback finds correct card
  for place-business steps by looking up preceding select-business step
- Updated part3, part5, part6 E2E tests to use clickRequiredBusinessCard
  + clickStreetSlot for T8-T9 instead of clicking Next on action steps
- Updated unit test assertions for new T9 gate type and distribution
…en by hand

- Fix first business card in hand being half off-screen (left side):
  Use handX + handCardW/2 as startX instead of raw handX=40, so
  cards are centered in the hand zone (matching how HandView positions
  event cards).
- Fix action buttons (End Turn, Cancel, Hint) hidden behind hand cards:
  Set actionContainer depth to 100 so it renders above handBusinessContainer
  (depth 0). The hand cards were added to the scene display list after
  actionContainer, causing them to render on top despite being added first
  in refreshAll ordering.
…s, remove hand label

- Fix animation jump: compute correct hand card destination based on the
  card's future index in the hand (current hand size). Uses the same
  formula as refreshBusinessHandCards (handX + handCardW/2 + i * spacing,
  handY), so the card flies to its exact final position with no jump.
- Add 'placing-from-hand' branch to refreshActionButtons so End Turn
  (and other) buttons remain visible when a card is in hand awaiting
  placement. Previously the 'placing-from-hand' phase fell through both
  branches, leaving the action area empty.
- Remove "Hand: X/Y" label that overlapped the hand cards.
- Remove unused handSizeText declaration from MainStreetScene.
…h selection highlight

- Replaced custom refreshBusinessHandCards() with a core HandView
  instance (handBusinessView) for business cards in the player's hand.
- Added green border selection indicator (updateBusinessHandSelection)
  that highlights which card in hand is pending placement.
- Cards in hand are now clickable during placing-from-hand phase to
  select which card to place (customClickFn sets pendingHandIndex).
- Removed deprecated handBusinessContainer field from scene.
- HandView layout matches previous positioning (handX + handCardW/2,
  handY) with same spacing (handCardW + 8).
Add handCenterX derived from SLL street zone topCenter anchor to both
HandView instances (handView and handBusinessView) in MainStreetRenderer.
This ensures the hand is horizontally centred in the left column regardless of
card count, preventing the left card from clipping off-screen.

Changes:
- MainStreetConstants.ts: Add handCenterX field to SceneLayout interface
- MainStreetLayoutAdapter.ts: Compute handCenterX from street topCenter anchor
- MainStreetRenderer.ts: Pass centerX: handCenterX to both HandView constructors
- tests/main-street/layout-adapter.test.ts: Verify handCenterX is 410px
- MainStreetRenderer.ts: Round score in HUD display (Score: X/Y)
- MainStreetHudTooltips.ts: Round score estimate and remaining to win values
- MainStreetOverlayContent.ts: Round Final Score and High Score values
- hud-tooltips.test.ts: Update tests to expect rounded values, add rounding-specific test

Only display formatting changes — no logic changes to score calculation.
…phase

Add interactive hit area to business hand cards in the market phase so
players can select a hand card to start the placing-from-hand flow.

Changes:
- MainStreetRenderer.ts: Add transparent interactive rectangle in
  handBusinessView.renderCard with useHandCursor and pointerdown handler
  calling s.onHandBusinessCardClick(cardIndex). Made
  updateBusinessHandSelection public for turn controller access.
- MainStreetTurnController.ts: Add onHandBusinessCardClick method that
  sets pendingHandIndex, switches uiPhase to placing-from-hand, updates
  instruction text, calls refreshAll(), and updates selection highlight.
  Preserves existing customClickFn behavior for placing-from-hand switching.
  Respects tutorial gating.
- MainStreetScene.ts: Add onHandBusinessCardClick delegation proxy.
- tests/main-street/hand-business-click.test.ts: 9 tests covering market
  phase click, non-market gating, placing-from-hand switching, scene
  delegation, edge cases (empty hand, OOB index), and tutorial gating.
Implements three features:
1. CSV data embedded in serialized state (csvData field)
2. CSV mismatch resolution and legacy save handling
3. Market state preservation on checkpoint resume

Files changed:
- MainStreetCards.ts: Export CARD_DATA_RAW; add loadTemplatesFromCsv,
  resetTemplatesToDefault; mutable template arrays for runtime CSV reload
- MainStreetState.ts: Add csvData to MainStreetSerializedState;
  serialize embeds CSV; deserialize detects mismatch & uses saved CSV
  or rejects legacy saves; setupMainStreetGame resets templates
- MainStreetEngine.ts: Add skipMarketRefill param to executeDayStart
- MainStreetTurnController.ts: Pass skipMarketRefill through startDayPhase
- MainStreetLifecycleManager.ts: Use skipMarketRefill on checkpoint resume
- MainStreetSvgTextureManager.ts: Use getCsvRows() for live CSV data
- tests/main-street/csv-checksum.test.ts: 29 tests covering all ACs
…AGENTS.md

Add a comprehensive 'Game Architecture Best Practices' section with 18
architectural patterns drawn from Gym demo scenes. Each pattern includes:
- Pattern name and one-sentence description
- Gym scene reference with file path
- Key core-engine APIs used
- Verbatim references to Gym help panel text (Feature/Usage Example sections)
- Usage guidance drawn from the scene implementations

Patterns covered:
1. Seeded RNG for Deterministic Randomness (GymDeckRngScene)
2. View/Model Separation with HandView/PileView (GymHandPileScene)
3. Command Pattern for Undo/Redo (GymUndoRedoScene)
4. Strategy Pattern for AI (GymAiStrategyScene)
5. Overlay Lifecycle & GeometryMask (GymOverlayUiScene, GymParameterizedOverlayScene)
6. Event Sourcing via Transcript (GymTranscriptScene)
7. Versioned Save/Load (GymSaveLoadScene)
8. Event-Driven Audio/Feedback (GymAudioFeedbackScene)
9. SLL Declarative Layout (GymSllScene)
10. Economy & Legality (GymRuleEngineScene)
11. Tooltip System (GymTooltipScene)
12. Grid/Pathfinding (GymSpatialRulesScene)
13. HUD Components (GymHudComponentsScene)
14. Token Piles (GymTokenPileViewScene)
15. Market/Offer Engine (GymMarketOfferEngineScene)
16. SVG Rasterisation (GymSvgHelpersScene)
17. I18n (GymI18nScene)
18. Graphics Feasibility Spikes (GymGraphicsShaderSpikeScene, GymGraphicsLightingSpikeScene)

All other Gym scenes (GymSceneBase, GymRouterScene) are referenced
in the Scene Base Class Pattern section. References to docs/gym/GYM_INDEX.md
are included as the authoritative scene-to-API mapping.

Closes CG-0MRPE1D1B003JHAF
…s in Canvas renderer

Replace setTint() calls on Image/Sprite sprites with colored
semi-transparent Rectangle overlays that render correctly in both
Canvas and WebGL Phaser 4 renderers.

Changes:
- src/ui/HandView.ts: Add _tintOverlays array and _setCardTint helper.
  Replace all setTint() calls with overlay-based tinting. Update overlay
  positions during drag, layout, and selection changes. Clean up overlays
  in clearDisplay() and destroy().
- src/ui/shakeIllegalMove.ts: Add Rectangle overlay alongside setTint()
  for Canvas-compatible red tint during illegal-move shake animation.
- example-games/gym/scenes/GymHandPileScene.ts: Fix reduced-motion path
  to use overlay instead of bare setTint/clearTint.
- tests/ui/handView.test.ts: Add 3 new tests for tint overlay lifecycle
  (selection, hover, destroy).
- tests/ui/shakeIllegalMove.test.ts: Update mocks for overlay support.
- tests/gym/GymHandPile.test.ts: Add rectangle mock to scene factory.
- tests/gym/GymHandPileClickToPlay.test.ts: Add rectangle mock + mock
  properties (depth, originX, originY).
- tests/lost-cities/lost-cities-hand-pile-migration.test.ts: Update
  rectangle mock with overlay lifecycle methods.

Fixes: Card selection highlight (green), illegal-move feedback (red),
and hover tint (green) now work under Phaser.CANVAS renderer without
regression to GymOverlayUiScene GeometryMask functionality.
Generalize CardMemoryTracker with recordKey() to support custom card
models that do not implement the engine Card interface (Lost Cities
LostCitiesCard), and wire the tracker into LostCitiesAiPlayer.

Changes:
- src/ai/CardMemoryTracker.ts: Add recordKey(key: string) generic
  method; recordCard(card) now delegates to recordKey(rank). Update
  docs and internal naming (key instead of rank).
- example-games/lost-cities/AiStrategy.ts: Add memoryTracker field to
  LostCitiesAiPlayer with config { skill: 80, maxCopies: 12 } (5 colors
  x 12 cards per color). Add recordDiscard(card) which groups by
  expedition color.
- example-games/lost-cities/scenes/LostCitiesTurnController.ts: Record
  AI's own discards and human player's discards (both fully visible)
  into the AI's memory tracker.
- src/ai/README.md: Document recordKey API and Lost Cities usage.
- tests/ai/CardMemoryTracker.test.ts: Add recordKey tests (5 new).
- tests/lost-cities/lost-cities-ai.test.ts: Add memory tracker
  integration tests (4 new).

Verified: npm run build succeeds; all 268 unit test files pass
(4736 tests); Lost Cities suite (12 files, 288 tests) passes; only the
6 pre-existing browser test failures remain.
…G-0MS8T34T8004ZVEM)

initButtonBar() previously destroyed any existing bar before creating a new
one, so scenes that call it 2-3x (GymHandPile, GymAiStrategy, GymAudioFeedback,
GymSpatialRules, GymRuleEngine) lost every button row except the last.

- Add protected buttonBars: GymButtonBar[] registry; initButtonBar() appends
  new bars instead of destroying prior ones (this.buttonBar stays a
  backward-compatible accessor to the most recent bar).
- Register shutdown/destroy cleanup on first initButtonBar() call (lazy,
  flag-guarded) that destroys all registered bars so scene restarts are
  leak-free.
- F1: rewrite tests/gym/GymSceneBaseButtonBar.test.ts for the multi-bar
  contract (10 tests).
- F2: new tests/gym/GymButtonBarVisibility.browser.test.ts asserting all
  setup/interaction buttons exist and are visible in all 5 affected scenes
  (5 tests).
- Resolves test-failure CG-0MS8UH8G8009JUW0 (Cancel Move returns moved card
  to original hand position).
…(CG-0MS8T34T8004ZVEM)

F4: update the GymButtonBar 'Integration with GymSceneBase' section to
describe the multi-bar semantics (registry accumulation, this.buttonBar =
most recent bar, shutdown/destroy cleanup) and remove the stale
destroy-and-recreate wording.
…gs changes

Pre-existing failures (tracked as critical test-failure items) caused by
tests asserting behaviour that intentional code changes superseded:

- SettingsPanelTooltips (CG-0MS8V1L3H003LYIX): findTextObjects now recurses
  into nested containers so labels inside the scrollable content container
  are found.
- MainStreetScene (CG-0MS8V1TRB007IE1B): update 'only materializes
  purchased cards' test for the buy-to-hand flow (CG-0MS0J0Y6G009WXMF):
  business transfers market->hand first, then is placed from hand.
- MainStreetZOrder (CG-0MS8V1UEX008X8YR): actionContainer is deliberately
  raised to depth 100 (CG-0MS0J0Y6G009WXMF); gameplay containers list now
  excludes it and asserts depth 100 explicitly.
- TutorialOverlayManager (CG-0MS8V1V300080WPA): T9 now highlights the
  streetGrid zone (place-business action step), not investmentsRow.
… on by default

Addresses the manual review rejection:
- Card in hand highlight now rotates to match the card (tint overlay
  rectangles inherit sprite rotation in HandView, shakeIllegalMove, and
  the GymHandPileScene reduced-motion illegal path)
- Drag-and-drop is now enabled by default in GymHandPileScene
- Fixed misleading instruction text when drag is off: now says
  'click card, then click discard pile' instead of 'drag to discard'
- Updated help text, test plan, and DEVELOPER.md button-bar example
- Updated browser tests (button visibility, drag flow) and added unit
  test verifying tint overlay rotation matches the sprite angle
…0853Y4, CG-0MS83F3K4004JZ0U, CG-0MS83FDWP005UENP)

Root cause: getCardDesign() and sibling getters/setters defaulted
storage to null, so resolveStorage(null) returned null immediately and
no-arg calls (e.g. preloadCardAssets) never read globalThis.localStorage.
Card style preference therefore reset to 'Classic' after page reload.

Fix: make the storage param optional (storage?: StorageLike | null).
undefined falls back to globalThis.localStorage; null still means
'explicitly no storage'. Applied to all getters/setters:
getCardDesign, setCardDesign, getSelectedDifficulty, setSelectedDifficulty,
getReducedMotion, setReducedMotion, getTooltips, setTooltips,
getEndTurnKeybind, setEndTurnKeybind. JSDoc updated on all getters.

Tests:
- tests/ui/CardDesign.test.ts: no-arg read/write regression tests via
  stubbed globalThis.localStorage
- tests/ui/CardDesign.browser.test.ts: browser tests verifying
  setCardDesign writes to window.localStorage and preloadCardAssets
  loads the persisted design's assets after simulated reload
…W6RQQ005NJHG)

The multi-bar registry fix (CG-0MS8T34T8004ZVEM) revealed that the 12
main action buttons wrapped into 3 rows in the center zone while the
toggle-button bar at y=112 overlapped the third row, and the drag/
discard/face status labels overlapped the toggle buttons.

- Spread the 12 action buttons across left/center/right zones so they
  fit on a single row (row 1, y=60)
- Move the 4 toggle buttons (Disable Drag, Toggle Discard Mode,
  Toggle Face Up, Toggle Layout) onto a single second row (y=112)
- Move the status labels (Drag/Discard/Face/Layout) to a dedicated
  info line below the buttons (y=134) so they never overlap buttons
- Remove the third button bar that previously held Toggle Layout next
  to the sliders

Result: exactly two rows of buttons, no overlap with info text.
…stance slider; per-index card depth keeps selection highlight on the selected card (CG-0MS934XSL00453HF, CG-0MS937FT9006273E)
Per producer clarification, 'score' includes coins as well as total score.
Round coin balance displays (HUD, score tooltip breakdown, game-over overlay)
to nearest whole number alongside the already-rounded total score.

- MainStreetRenderer.ts: HUD coins display uses Math.round(coins)
- MainStreetHudTooltips.ts: score tooltip breakdown coins uses Math.round(coins)
- MainStreetOverlayContent.ts: game-over overlay coins uses Math.round(coins)
- Tests: updated hud-tooltips assertions + added browser tests for HUD
  coin display and game-over overlay coin display rounding

Only display formatting changes - no logic changes to score calculation.
…position (CG-0MS99JZBK001ZIBM)

The selection-raise feature regressed Cancel Move: moveSelectedCard()
recorded the sprite's current x/y as the origin, which includes the
selection-raise offset. cancelMove() then returned the card to that
raised position instead of its true base hand position, leaving the
card ~18px off (leftmost card at x≈582 instead of 600).

Changes:
- HandView: add public getBasePosition(index) returning the un-raised
  layout position from _basePositions
- moveSelectedCard(): record the base position instead of the sprite's
  current (possibly raised) position
- cancelMove(): clear the selection when returning a moved card so
  applySelectionRaise kills any in-flight raise tween and no raise
  offset is re-applied on top of the return tween
- Add unit test for HandView.getBasePosition (raised vs base position)

Fixes failing browser test gym-handpile-cancel.browser.test.ts (3/3
previously failing).
…s (CG-0MS9AGG3N003ASCR)

Removed 32 test files (~3,500 lines) that did not serve true value:
- Source-code-grep tests (assert file text, not behavior): GymHandPile* layout/spacing/raise/rotation/shutdown/button-bar, FeudalismTurnController reducedMotion/patronAnimationTiming/HandViewPileViewMigration, GolfAiController.reducedMotion, GolfSoundDuplication, LostCitiesTurnController.reducedMotion, SushiGoScene.reducedMotion, game-selector-integration, tfRuntimeSynthPreset, phaserVersionPin
- Placeholder tests: GolfAnimator.reducedMotion (all expect(true).toBe(true)), smoke.test.ts
- Self-referential simulations: GymHandPileClickToPlay, GymHandPileDiscardConsistency, GymSaveLoadScreenshotFilter
- Duplicates of core coverage: GymDeckRng, GymReducedMotion, GymAudioFeedback, GymAudioFeedbackAutoDiscover, visibility-ownership-runtime
- Type-level/structural-only: DebugToolsRegistry (x2), GameEventLogOverlay, SessionExportTool, StateInspectorOverlay
- Zero-assertion browser test: GymTooltipScene.browser

Stripped dead/placeholder assertions from 8 kept files: ReducedMotion, MainStreetSellCards, hud-layer-contract.browser, stats-button-icon, GymHandPile, handPileScene.animation, monte-carlo-greedy-guardrail, incident-queue-card-aspect.

Updated docs (GYM_INDEX.md, DEVELOPER.md, AGENTS.md) to drop references to removed test files.

Verified: unit 239 files/4564 tests pass, browser 66 files/414 tests pass, tutorial E2E 6/6 pass, npm run build succeeds.
@SorraTheOrc
SorraTheOrc merged commit 4b3b044 into main Jul 31, 2026
@SorraTheOrc
SorraTheOrc deleted the release/dev-to-main-20260731195715 branch July 31, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant