Merge dev → main (automated) - #771
Merged
Merged
Conversation
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.
…uring placing-from-hand" This reverts commit d2a1135.
…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)
…180 / default 60 (CG-0MS98MXQP007OGFU)
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated release created by ship skill.\n\nIncludes CHANGELOG.md with work-item summaries from this release.