A retro-styled browser trading game inspired by the Drug Wars gameplay loop.
Players move between real U.S. city markets, buy/sell illegal cat breeds, manage heat risk, and survive escalating debt pressure.
- Runtime: Val Town HTTP Val (
main.ts) - Web framework:
Hono - Persistence: Val-scoped SQLite via
https://esm.town/v/std/sqlite/main.ts - Binary/media: Val Town
blob(avatar path still supports blob keys, UI uses pixel presets) - Frontend: server-rendered HTML + vanilla JS
main.ts: HTTP routes, DB bootstrap/migrations, rendering, game actions, and admin dashboard/APIauth.ts: session cookie parsing, session token lifecycle, authenticated user lookupconfig.ts: static game config (markets, breeds, pixel avatar metadata)rules.ts: pure game logic helpers (borrow terms and heat streak cap)rules_test.ts: script-based regression tests forrules.ts
- Register or log in.
- Check market prices for each breed.
- Buy cats where prices are low.
- Travel to another city (travel advances day and raises heat).
- Sell for profit.
- Manage debt before deadline.
- There is no fixed dollar travel fee.
- Each travel action increments
dayby 1. - Travel raises heat and may trigger a DHS bust.
- Bust outcomes can reduce cash (fine) and inventory (seizure).
- High-volatility breeds (
Sphynx,Bengal) produce the largest swings. - Early borrowing increases buying power but fees escalate per borrow tier.
- Low-risk travel hubs (
Seattle,Austin) help reduce bust frequency while hunting good exits. - Over-traveling increases bust risk and can erase profits quickly.
- Debt has a due day (
debt_due_day). - Borrowing:
- increases cash and debt
- applies escalating fee by borrow tier (capped)
- extends due window, capped to at most 90 days ahead
- Missing debt deadline while debt > 0 triggers bankruptcy:
- inventory wiped
- run reset to baseline state
- Travel increases heat and can trigger a DHS bust event.
- Bust events may seize inventory and fine cash.
- Heat cannot stay at 100% for more than 2 consecutive turns; on the 3rd turn it is forced down to 95%.
Account identity, role (player/admin/super_admin), and avatar key.
Cookie-token sessions with expiry.
Current market, cash, debt, heat, day, cargo, debt due day, borrow tier, and heat max streak.
City list and enforcement risk profile.
Base economy metadata for each cat breed.
Owned quantity per breed per user.
Shared daily per-market prices across all users (multiplayer-ready market tape).
Deprecated table kept only for migration compatibility. Active writes no longer target this table.
Tracks one-way migration IDs that have been applied.
POST /api/registerPOST /api/loginPOST /api/logoutGET /api/me
GET /admin(dashboard UI; admin-only)GET /api/admin/overviewGET /api/admin/marketsPOST /api/admin/markets/:marketIdGET /api/admin/breedsPOST /api/admin/breeds/:breedId
POST /api/buyPOST /api/sellPOST /api/travelPOST /api/borrowPOST /api/pay-debt
POST /api/avatar/presetGET /api/pixel-avatar/:avatarIdGET /api/avatar/:userId
initDb() creates base tables and then runs idempotent tracked migrations:
2026-03-02-add-player_state-debt_due_day2026-03-02-add-player_state-borrow_count2026-03-02-add-player_state-heat_max_streak2026-03-02-backfill-debt_due_day2026-03-02-add-users-role2026-03-02-backfill-super-admin-first-user2026-03-02-create-market-day-prices2026-03-02-backfill-market-day-prices-from-user-market-prices2026-03-02-clear-user-market-prices-legacy
Applied migrations are recorded in schema_migrations.
Run the script file directly in Val Town:
rules_test.ts
It validates:
- borrow fee escalation and cap behavior
- debt-ceiling clamping
- due-date 90-day cap
- heat 100% streak cooldown behavior
- Market prices are shared by
day + marketfor all users. - Price history retention is enforced by deleting rows older than
MARKET_PRICE_RETENTION_DAYS(currently 45) relative to the active max player day. - Mutating routes (
travel,borrow,pay-debt,buy,sell) centralize write coordination and usesqlite.batchfor multi-write updates where possible. - Full cross-statement transaction boundaries are pending a runtime-safe transaction primitive in Val Town's sqlite helper.
- Val is public:
pchinjr/cat-wars - Auth/session logic extracted into
auth.ts - Static game config extracted into
config.ts - Core deterministic rules extracted into
rules.ts - Regression coverage added for extracted rules via
rules_test.ts
- Add tests for route-level behaviors (
/api/travel,/api/borrow, bankruptcy reset). - Introduce a tiny data-access layer to remove inline SQL from route handlers.
- Add structured event logging for travel, bust, borrow, and bankruptcy actions.
- Add multiplayer-ready turn clock abstraction for synchronized city market ticks.
