-
Notifications
You must be signed in to change notification settings - Fork 0
Player Profile Schema
A player profile is two components merged into one queryable object:
-
Stats — sourced from
actor.rpg.statson AT Proto (Phase 2, planned) -
Deck — sourced from Firestore
playerProfilescollection (to be built alongside Sealed Deck mechanic)
Matchmaking for the 4-player engine reads both to balance skill and faction composition across the table.
- AT-Protocol — the-rift bot, Bluesky posting, Phase 2 stats write-back
- Player-Profile-Schema — Sealed Deck + stat matchmaking, Firestore schema, build order
PlayerProfile {
// Identity
did: string // AT Proto DID — primary key across both systems
// From actor.rpg.stats (AT Proto — Phase 2)
acpl: number // Average centipawn loss — skill proxy
winRate: number // Overall win rate
veilRate: number // How often their pieces get veiled — aggression proxy
rebirthAdvanceCount: number // Promotion cap hits — pressure-style proxy
// From Firestore playerProfiles (Sealed Deck mechanic — to be built)
primarySuit: "spades" | "hearts" | "diamonds" | "clubs"
deck: CardSelection[] // Which face cards / pip cards they run
playstyle: derived // Conquest / Restoration / Intelligence / Fortification
}Collection: playerProfiles
Document key: player's AT Proto DID
{
did: "did:plc:...",
primarySuit: "diamonds",
deck: [
{ card: "courier", suit: "diamonds", ability: "courier_intercept" },
{ card: "spy", suit: "diamonds", ability: "spys_glance" }
],
playstyle: "intelligence",
updatedAt: Timestamp
}Stats are not duplicated in Firestore — they're fetched from actor.rpg.stats at matchmaking time and merged in memory. Firestore owns deck data only.
For a 4-player table, balance across two axes:
- Compute MMR from
acpl+winRate - Find players within a tolerance window (e.g. ±150 MMR)
- Prefer tighter windows; widen if lobby pool is small
The four suits have distinct mechanical roles — a table missing a role is mechanically weaker:
| Suit | Role | Why It Matters |
|---|---|---|
| Diamonds | Intelligence / Information | Counters Fog Mode hidden info (courier_intercept, spys_glance) |
| Hearts | Restoration / Sustain | HP recovery; game longevity (phoenix_rite, menders_grace) |
| Spades | Conquest / Aggression | Pressure and tempo |
| Clubs | Fortification / Defense | Board control and anchoring |
Composition targets:
- At least 1 Diamonds player at any Fog Mode table
- At least 1 Hearts player (sustain)
- No more than 2 players sharing the same suit (prevents mono-doctrine games)
Algorithm:
- Find all players within skill tolerance
- Score each possible 4-player grouping by faction coverage (all 4 suits covered = highest score)
- Prefer highest faction score within skill window
- Fall back to any skill-balanced table if faction balance isn't achievable from the current lobby pool
When a player confirms their deck selection in the UI, write to Firestore immediately:
await db.collection('playerProfiles').doc(playerDid).set({
did: playerDid,
primarySuit: selectedSuit,
deck: selectedCards,
playstyle: derivePlaystyle(selectedSuit),
updatedAt: Firestore.Timestamp.now()
}, { merge: true });Use merge: true so a deck update doesn't wipe stats data if they ever co-locate.
This system builds in sequence with the broader roadmap:
- Fog Mode playtest in Duet (in progress)
-
Sealed Deck mechanic in Duet → deck selection UI + Firestore
playerProfileswrite built here -
AT Proto Phase 2 →
actor.rpg.statsOAuth write-back; stats now live on AT Proto - Matchmaking reads Firestore (deck) + AT Proto (stats) → full profile available
- 4-player engine matchmaking uses the complete profile
Paragon (Epic Games) used a card-based system for hero build customization — proof that card-driven MOBA builds work at scale. Key distinction: in Paragon, cards were separate from hero identity. In Veiled Dominion, the deck is the identity — Sufi Tariqah vs. Systems Quartet are worldviews with mechanical consequences, not just stat modifiers. The matchmaking system needs to respect that distinction: faction balance is not cosmetic, it's structural.
- AT-Protocol — the-rift posting, Phase 2 stats write-back
-
Sealed Deck design — full card system spec (see
docs/design/dueling-systems.md)