Skip to content

feat(birthday-demo): claim → place → earn → real-sprite combat path - #101

Merged
BrandDead merged 2 commits into
main-tL2525from
feat/birthday-demo-path
Aug 6, 2026
Merged

feat(birthday-demo): claim → place → earn → real-sprite combat path#101
BrandDead merged 2 commits into
main-tL2525from
feat/birthday-demo-path

Conversation

@BrandDead

Copy link
Copy Markdown
Owner

Summary

The birthday demo path — a single VITE_DEMO_MODE=1 build flag that drops a reviewer directly into the full gameplay loop without Supabase credentials.


Changes

Real-sprite Phaser top-down combat

BlockTopDownScene.ts — replaced the broken spriteUrlForRole() (filenames missing the _idle_ state suffix → 404 loads → coloured-circle fallbacks) with getWorldActor(role, 'idle', 'topdown') from worldActorResolver. The resolver is driven by runtimeManifest.json and always returns a URL that exists on disk.

Runtime art library (cherry-picked from #98)

All /assets/runtime/ WebP assets: topdown character sprites (dealer/shooter/enforcer/lookout/driver/k9/recruit/police) with idle/downed/arrested states, environment backdrops (Las Olas strip plaza), vehicles, weapons, UI. Updated runtimeManifest.json with correct role+state metadata. Updated worldActorResolver.ts + its 25 tests.

Birthday demo path (VITE_DEMO_MODE=1)

  • src/utils/demoSeed.ts — seeds player ($12k cash), 3 gang members (Lil Dre dealer Lv2 / Big Rome shooter Lv3 / Tasha lookout Lv2), and a pre-claimed Las Olas block with the dealer already placed at sidewalk col 2 row 2 earning $72/tick. Navigates straight to MAP.
  • App.tsx — when IS_DEMO_MODE is true, skips AgeGate, Supabase auth, and Onboarding; calls seedDemoState() on mount. useEmpireHydration is also disabled in demo mode to avoid Flask calls.
  • .env.example — documents the flag with a production warning.
  • Tree-shaken in production — Vite replaces the env literal; Rollup eliminates the dead branch.

How to run the birthday demo

cd frontend
VITE_DEMO_MODE=1 \
  VITE_SUPABASE_URL=https://placeholder.supabase.co \
  VITE_SUPABASE_ANON_KEY=placeholder \
  VITE_MAPBOX_ACCESS_TOKEN=pk.placeholder \
  npm run build && npm run preview -- --port 3000
# Open http://localhost:3000
# → Lands directly on the Las Olas block (MAP view)
# → Lil Dre already placed on sidewalk, earning $72/tick
# → Hit the DRIVE-BY tab to enter real-sprite combat

Test evidence

  • npx vitest run: 446 passed (23 test files) — includes 25 worldActorResolver tests
  • npx vite build (placeholder env): clean, 0 new errors
  • Pre-existing TS errors (BlockOverlay GeoJSON, TrapApp status) unchanged — present on main-tL2525 before this branch

Relation to other open PRs

@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
slide Ready Ready Preview Aug 6, 2026 10:43pm

BrandDead added 2 commits August 6, 2026 22:37
## What this branch delivers

### 1. Real-sprite Phaser top-down combat (BlockTopDownScene)
- Replaced broken `spriteUrlForRole()` (hardcoded filenames missing the
  `_idle_` state suffix) with `getWorldActor(role, 'idle', 'topdown')`
  from `worldActorResolver`.
- `worldActorResolver` is driven by `runtimeManifest.json` and always
  resolves to a URL that exists on disk — no more 404 sprite loads,
  no more coloured-circle fallbacks.

### 2. Runtime art library (from feat/sprite-coverage-batch2)
- All `/assets/runtime/` WebP assets cherry-picked in: topdown
  character sprites (dealer/shooter/enforcer/lookout/driver/k9/recruit/
  police) with idle/downed/arrested states, environment backdrops,
  vehicles, weapons, UI.
- Updated `runtimeManifest.json` with correct role+state metadata so
  `worldActorResolver` can index them.
- Updated `worldActorResolver.ts` + its test suite from the same branch.

### 3. Birthday demo path (VITE_DEMO_MODE=1)
- New `src/utils/demoSeed.ts`: seeds player (2k cash), 3 gang members
  (Lil Dre dealer Lv2 / Big Rome shooter Lv3 / Tasha lookout Lv2),
  and a pre-claimed Las Olas block with the dealer already placed at
  sidewalk col 2 row 2 earning $72/tick. Navigates straight to MAP.
- `App.tsx`: when `IS_DEMO_MODE` is true, skips AgeGate, Supabase auth,
  and Onboarding; calls `seedDemoState()` on mount.
- `.env.example`: documents `VITE_DEMO_MODE=1` with a warning never to
  use it in production.
- Tree-shaken in production builds (Vite replaces the env literal and
  Rollup eliminates the dead branch).

## How to run the birthday demo
```
cd frontend
VITE_DEMO_MODE=1 VITE_SUPABASE_URL=https://placeholder.supabase.co \
  VITE_SUPABASE_ANON_KEY=placeholder VITE_MAPBOX_ACCESS_TOKEN=pk.placeholder \
  npm run build && npm run preview -- --port 3000
# Open http://localhost:3000 — lands directly on the Las Olas block
```

## Test evidence
- `npx vitest run`: 446 passed (23 test files)
- `npx vite build` (placeholder env): clean, 0 new errors
- Pre-existing TS errors (BlockOverlay GeoJSON, TrapApp status) unchanged

## Out of scope
- Supabase live path, payments, Gate 1 NPC fixture
Blocker 1 — Phaser fallback texture key bug:
  Build a canonical role→textureKey map during preload. Fallback
  roles (chemist→dealer art, runner→lookout art, etc.) now share
  the correct texture key. syncMemberSprites uses resolveTextureKey()
  which checks exact role+state, then canonical idle, then dealer idle.

Blocker 2 — Combat state ignored:
  Preload idle/downed/arrested states per role. resolveTextureKey()
  selects the state texture based on placement.health (0 → downed).

Blocker 3 — Demo mode still runs block sync:
  useBlockSync now accepts an enabled parameter (default true).
  in demo mode.

Blocker 4 — Non-deterministic seed:
  seedDemoState() now removes stale demo members by ID before
  re-adding them (removeMember + addMember). The demo block is
  always overwritten by upsertBlock.

Blocker 5 — Seed income disagrees with game formula:
  incomePerTick corrected from 72 to 67
  (= round(60 × 1.12) = round(67.2) = 67).

446 vitest tests pass.
@BrandDead
BrandDead force-pushed the feat/birthday-demo-path branch from d00bd6f to eec85d7 Compare August 6, 2026 22:41
@BrandDead
BrandDead merged commit 74cfd6b into main-tL2525 Aug 6, 2026
2 of 3 checks passed
@BrandDead
BrandDead deleted the feat/birthday-demo-path branch August 6, 2026 22:41
BrandDead pushed a commit that referenced this pull request Aug 6, 2026
Rebase onto main-tL2525 (post #98/#101/#96 merges):
- OSShell.tsx conflict: keep CLOUT label from #97 + shot-caller
  artwork from #96 (GPT audit recommendation)
- App.tsx: auto-merged cleanly

P0 fixes:
- Remove CLAUDE_SPRINT_15A_PROMPT.md and SPRINT_15B_NOTES.md
  (internal sprint artifacts should not be in product PRs)
- marketMembersCatalog.ts: fix invalid CityRegion 'east' -> 'miami'
- MarketMembers.tsx: replace hardcoded roster cap 24 with
  maxMembers from useGangStore (store cap is 20)
- k9/recruit/police are now in MemberRole union (from #98 merge),
  so no 'as MemberRole' cast is needed here

581 vitest tests pass (26 test files — 3 new from #97).
BrandDead added a commit that referenced this pull request Aug 6, 2026
* feat(sprint-15b): Market members, Most Wanted board, Get Back clock

Priority 2 and Priority 4 from the app audit. Priority 1 and 3 are
delegated to Claude/Opus 5 via CLAUDE_SPRINT_15A_PROMPT.md.

Market MEMBERS tab (Priority 2)
- marketMembersCatalog.ts: procedural hireable generator. Four tiers
  (street/seasoned/certified/legend) with monotonic pricing, and an
  inverse relationship between tier and loyalty — the good ones cost
  more and hold worse. Heat factor and baggage odds both rise with
  tier, so a legend shooter is a genuine tradeoff rather than a
  straight upgrade. Seven roles including k9, which is cheap to keep
  and has no special people.
- Listings are seeded off their id so a board survives re-renders
  mid-purchase and can be rebuilt without persisting every field.
- MarketMembers.tsx/.css: listing wall plus detail sheet showing stats,
  origin story, terms, special people, and the catch. Board refresh
  costs money so it cannot be rerolled for free. Roster capped at 24
  active to keep payroll bounded.

Most Wanted board (Priority 4)
- mostWantedStore.ts: post, cancel, and fulfil bounties plus an ally
  ledger. Listing fee is non-refundable on cancel, minimum reward
  scales with target level, and contracts on a member's people carry a
  premium. canFulfil blocks self-cashing and double payouts.
- MostWanted.tsx/.css + MostWantedApp.tsx/.css: aged-paper poster wall,
  post form with photo upload, proof-screenshot upload that pays out
  automatically, and an allies tab with standing meters. Fulfilling a
  contract saves the poster as an ally and opens a Get Back window
  against the hunter — the war starter.
- Proof and payout are public record by design; reputation does the
  policing rather than a server-side referee.

Get Back clock (Priority 4)
- getBackStore.ts: wall-clock-derived revenge windows so backgrounding
  the app cannot desync them. Success doubles the morale the offender
  banked and strips it from them; expiry penalises the retaliator.
  Resolution is idempotent so morale can never be applied twice.
- GetBackClock.tsx/.css: shot-clock badge above the dock with an
  expandable panel listing open debts and who is catchable. Sweeps
  lapsed windows on its own tick so penalties land even if the panel
  is never opened. Gang-wide morale is applied per active member,
  since morale lives on members not on the gang.

Wiring and cleanup
- OSShell: RANKINGS renamed to CLOUT; new WANTED app with a live
  open-bounty badge.
- App.tsx: most_wanted route plus GetBackClock as a global overlay.
- Market.tsx: emoji purge. Removed the dead `icon` field from
  ITEM_CATALOG (scripts/strip_market_icons.py) since the UI now uses
  CATEGORY_TAGS text labels.

Tests: 565 passing, up from 430. Typecheck clean except three errors
that pre-date this branch on main-tL2525. Production build succeeds.

* fix(pr97-scope): rebase onto main, resolve conflicts, fix P0 issues

Rebase onto main-tL2525 (post #98/#101/#96 merges):
- OSShell.tsx conflict: keep CLOUT label from #97 + shot-caller
  artwork from #96 (GPT audit recommendation)
- App.tsx: auto-merged cleanly

P0 fixes:
- Remove CLAUDE_SPRINT_15A_PROMPT.md and SPRINT_15B_NOTES.md
  (internal sprint artifacts should not be in product PRs)
- marketMembersCatalog.ts: fix invalid CityRegion 'east' -> 'miami'
- MarketMembers.tsx: replace hardcoded roster cap 24 with
  maxMembers from useGangStore (store cap is 20)
- k9/recruit/police are now in MemberRole union (from #98 merge),
  so no 'as MemberRole' cast is needed here

581 vitest tests pass (26 test files — 3 new from #97).

---------

Co-authored-by: BrandDead <1.74973198e+08+BrandDead@users.noreply.github.com>
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