Skip to content

Latest commit

 

History

2,640 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Punch Skater™

A cyberpunk electric-skate card game built with React, TypeScript, Vite, Firebase, and a small Express proxy for paid APIs.

Welcome to Punch Skater™

Create your skater. Build your crew. Win the joust. Rule the neon streets.

Punch Skater™ is a collectible card game set in the Sk8rpunk™ universe, built around forging cyberpunk electric-skate warriors, assembling a 6-card Crew, winning mission jousts, beating district rivals, and climbing seasonal neon leaderboards.

  • What — Forge skater cards with joust-ready identities, grow your collection, and choose your best 6 cards as your active Crew.
  • How — Start with a bonus Rare card on signup, then earn more through missions, joust encounters, battles, trades, daily rewards, weekly heat, and seasonal progression.
  • Why — Build Deck Power, chase Ozzies and XP, outplay named rivals, unlock cosmetic/status rewards, and prove your Crew belongs at the top of the underground ladder.

Core game loop

Forge → Crew → Mission / Joust → Reward → Upgrade / Trade → Unlock → Flex

Progression at a glance

Axis What it means
XP What your cards have done — earned through missions, battles, and streaks
Points What your cards can do — individual stat numbers (Speed, Range, Stealth, Grit)
Deck Power How strong your Crew is — sum of all stat Points across all 6 active cards
Ozzies How valuable and respected your collection is — earned Ozzy value per card

No pay-to-win. Legendary status cannot be forged — only earned through gameplay, missions, achievements, battle pass progress, or leaderboard rewards.

See docs/PROGRESSION.md for the full progression model.

Current Stack

  • React 18
  • TypeScript 5
  • Vite 6
  • React Router 7
  • Firebase Auth + Firestore
  • Express proxy for Fal.ai, Stripe, admin, weather, and battle endpoints
  • Playwright for end-to-end coverage

Installed App and Cross-Device Quality

Punch Skater ships as an installable Progressive Web App (PWA). On Chrome or Edge, use the in-game Install app prompt (or the browser install control). On iPhone and iPad, open the game in Safari, tap Share, then select Add to Home Screen. The installed app opens in standalone mode, has home-screen shortcuts for Forge, Crew, and Arena, and retains the app shell and cached game assets for offline revisits.

The supported experience targets:

  • Browsers: current Chrome, Edge, Firefox, and Safari releases.
  • Screen sizes: 320 CSS pixels wide and up; responsive layouts are checked at desktop 1280×720, phone 390×844 / 412×915, and tablet 834×1112 viewports.
  • Input: keyboard/mouse, touch, and touch-plus-keyboard. Primary game actions maintain 48px touch targets on compact screens.
  • Motion and effects: prefers-reduced-motion, data-saving preferences, and constrained devices disable nonessential parallax, automatic card spin, heavy backdrop filters, and continuously redrawn race decoration. Race outcomes still follow their server-authored fixed timeline ticks.

Performance targets for release builds are a responsive initial shell on mid-range phones, a steady 60fps where effects are enabled, and graceful static presentation rather than dropped input or unstable layouts when the device asks to reduce effects. Treat the static app shell, route transitions, and touch responsiveness as higher priority than decorative animation.

Device QA

e2e/app-shell.visual.spec.ts maintains screenshot baselines for desktop Chrome, Edge, Firefox, iOS Safari, Android Chrome, and iPad Safari:

npx playwright install chromium firefox webkit
npx playwright test app-shell.visual.spec.ts
# Deliberately refresh the approved baselines after a reviewed visual change:
npx playwright test app-shell.visual.spec.ts --update-snapshots

Before a release, also verify a real Android device and a real iPhone/iPad:

  1. Install the app, close the browser, and launch it from the home screen.
  2. Load the home screen online, disable connectivity, then relaunch to confirm the cached shell and game art remain usable.
  3. Deploy a newer build, reopen the installed app, and verify the Refresh now prompt appears without interrupting an active session.
  4. Check portrait and landscape layouts, keyboard navigation, touch targets, and reduced-motion mode.

Native wrappers such as Capacitor, Tauri, or Electron are only needed for store distribution or native platform APIs; they do not replace this cross-browser QA matrix. A Unity or Godot rewrite is reserved for future engine-level rendering or performance requirements.

Core Game Systems

  • Card Forge — deterministic card generation, layered art, factions, charge-up access, and joust-ready card identity including lance, shield, hype, gear, and traits
  • Collection — saved cards in Firestore for signed-in users, local storage for guests, collection rewards, and a bonus Rare card on signup
  • Crew / Deck Builder — choose 6 cards as your active Crew, name it, optimize Deck Power, and prep it for missions, jousts, battles, and seasonal submissions
  • Missions + Joust Encounters — server-authored district contracts, live encounter runs, counter choices, tactic-driven rival jousts, and risk/reward stat changes
  • Rivals + District Progression — named district rivals, boss-tier rival snapshots, codex unlocks, and reputation hooks woven into mission outcomes
  • Trades + Ranked Seasons — direct offers, market listings, lifetime Crew rankings, and a seasonal leaderboard that ranks verified 6-card Crews on submitted Deck Power
  • Battle Pass + Daily Progression — daily login streaks, daily missions, weekly heat, and a 6-week seasonal battle pass with free and premium reward tracks
  • Race Arena — public scouting summaries, real opponent deck snapshots, persistent results, and stat resolution syncing for both players
  • CraftLingua Codex — district language library, account-linked share codes, forge profile uploads, and Rare/Legendary conlang flavor text

Environment

Copy .env.example to .env for local client config.

Client variables

  • VITE_FIREBASE_*
  • VITE_IMAGE_API_URL
  • VITE_CHECKOUT_API_URL
  • VITE_AUTH_SYNC_API_URL
  • VITE_ADMIN_API_URL
  • VITE_BATTLE_API_URL
  • VITE_DISTRICT_WEATHER_API_URL
  • VITE_CRAFTLINGUA_DISTRICTS_API_URL
  • VITE_CRAFTLINGUA_TRANSLATE_API_URL
  • VITE_CRAFTLINGUA_RESOLVE_API_URL

Server-only variables

  • FAL_KEY
  • FAL_IMAGE_MODEL_URL (optional; defaults to https://fal.run/fal-ai/flux-lora)
  • FAL_CONFIG_URL (optional; remote JSON defaults for Fal image requests)
  • FAL_LORA_PATH (optional; server-side default LoRA path)
  • FAL_LORA_SCALE (optional; server-side default LoRA scale)
  • FAL_CHARACTER_IMAGE_MODEL_URL (optional; defaults to https://fal.run/fal-ai/flux-2/lora)
  • FAL_CHARACTER_CONFIG_URL (optional; remote JSON defaults for character image requests)
  • FAL_CHARACTER_LORA_PATH (optional; server-side default character LoRA path)
  • FAL_CHARACTER_LORA_SCALE (optional; server-side default character LoRA scale)
  • FAL_COLLECTION_STYLE_TRAINING_MODEL (optional; admin-only Fal style-LoRA trainer, defaults to fal-ai/flux-lora-fast-training)
  • FAL_COLLECTION_STYLE_LORA_SCALE (optional; collection-style inference scale, defaults to 0.9)
  • STRIPE_SECRET_KEY
  • STRIPE_WEBHOOK_SECRET
  • ADMIN_EMAILS
  • APP_ORIGINS (optional; extra CORS / checkout redirect origins)
  • REDIS_URL (optional; enables shared Redis-backed rate limiting)
  • PUNCHSKATER_PARTNER_KEY (reserved for CraftLingua partner integrations; keep server-only)
  • FIREBASE_SERVICE_ACCOUNT_JSON or (FIREBASE_PROJECT_ID, FIREBASE_ADMIN_CLIENT_EMAIL, FIREBASE_ADMIN_PRIVATE_KEY), or platform-provided application default credentials

Do not commit server secrets.

The admin Collection Style tab packages only selected, persisted Boss Asset character layers for training. Configure the training endpoint to output a LoRA compatible with FAL_CHARACTER_IMAGE_MODEL_URL; the generated profile and each 64-card batch snapshot that model URL to prevent an incompatible inference run.

Project Assets

Keep the repository root free of loose media. New art and other static assets belong in one of two places:

  • public/assets/ — production assets served by the app at /assets/.... Add the optimized (web-ready) copy here. Run WEBP_QUALITY=72 npm run optimize:assets to generate .webp siblings.
  • src/assets/ — images imported directly by React components (for example import logo from "../assets/logo.png").

Original, unoptimized design/source files that the app does not ship (reference art, PDFs, video sources, screenshots) live in design/. See design/README.md. Do not drop these in the repository root.

Local Development

cd /path/to/Punch-Skater
npm install

# terminal 1
FAL_KEY=your_key_here STRIPE_WEBHOOK_SECRET=whsec_... npm start

# terminal 2
npm run dev

Validation

cd /path/to/Punch-Skater
npm install
npm run lint
npm run build
WEBP_QUALITY=72 npm run optimize:assets   # optional: generate .webp siblings for public/assets
npx playwright install chromium
npm run test:e2e

Security Notes

  • The Express API is intentionally hardened for API-only hosting. If you ever serve the SPA from the same origin, expand the CSP connectSrc allow-list first so Firebase, Stripe, and Fal requests continue to work.
  • Fal image-generation, board-generation, and background-removal routes now require an authenticated Firebase user plus Firebase Admin credentials on the server.
  • Stripe checkout redirects are restricted to approved app origins, and webhook delivery should be configured for /api/stripe/webhook.
  • Admin access is now enforced via Firebase custom claims, with userLookup providing the minimal public directory used for trade lookups.

Firebase Storage CORS

Browser uploads from the admin Asset Generator (for example the Cyber Joust sprite generator) talk directly to the Firebase Storage bucket. The bucket must allow the app origins or the browser blocks the upload preflight with a CORS error. Apply the bucket CORS policy from cors.json whenever origins change:

gcloud storage buckets update gs://<your-storage-bucket> --cors-file=cors.json
# or, with the legacy tooling:
gsutil cors set cors.json gs://<your-storage-bucket>

Use the bucket named in VITE_FIREBASE_STORAGE_BUCKET (e.g. punch-skater.firebasestorage.app). Write access to Storage paths is still gated by storage.rules (admin-only for the factionImages/ and cyber-joust/ prefixes).

Prompt Surface Inventory

Current image-prompt map

  • src/pages/CardForge.tsx
    • Orchestrates the live forge
    • Builds the background, character, frame, and board prompt strings
  • src/lib/promptBuilder.ts
    • Owns buildCharacterPrompt, buildBackgroundPrompt, buildFramePrompt
    • Also retains buildCardBackPrompt and buildImagePrompt as dormant legacy/fallback builders
  • src/lib/boardBuilder.ts
    • Owns buildBoardImagePrompt
  • src/services/imageGen.ts
    • Appends the mandatory positive suffix and negative prompt to every generated image request
  • src/services/staticAssets.ts
    • Short-circuits district backgrounds and rarity frames to uploaded files before Firestore cache or fal.ai generation is attempted

Prompt set that matters in normal runtime

  • Active prompts
    • Character prompt
    • Board prompt
    • Global mandatory safety suffix / negative prompt
  • Fallback-only prompts for the current shipped catalog
    • District / background prompt
    • Border / frame prompt
  • Dormant legacy prompts
    • Combined card prompt
    • Card-back prompt

District and frame prompt status

All current forge districts and all current rarity tiers are registered to static assets, so the live forge does not normally need district or frame prompts anymore. Those prompt builders remain in the codebase only as fallback support for missing assets, future districts / rarities, or emergency regeneration.

Launch Asset Checklist

Required before launch

  • Upload and register all rarity frame files in /home/runner/work/Punch-Skater/Punch-Skater/public/assets/frames/
  • Audit district backgrounds so each live forge district has both print and small variants
  • Review card/share/download flows and replace remaining generated stable layers with static assets where possible

Future content decisions already locked

  • Treat The Roads as a corridor gameplay layer where route events spawn, not as a forgeable district or standalone mission

Nice-to-have immersion uploads

  • District ambience loops
  • Mission success / failure / fork-choice audio
  • Battle queue / draw / result audio
  • Trade sent / accepted / declined audio
  • Launch promo / social share art

Known Follow-Ups

  • The retired BoardComposite pipeline should stay out of the live card flow unless it is deliberately rebuilt from scratch later.
  • The Roads remain a separate route-event layer rather than a district selector.

About

This is a Virtual Card Game within the larger Sk8r Punk intellectual Property.

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages