🎰 Loot Tables

Weighted random drops with nested table support, variable amounts, and unique-per-roll guarantees.

Defining a Loot Table

Register loot tables via the Convex defineLootTable mutation. Each table has weighted entries that can drop items, currencies, nothing, or reference nested tables.

typescript
await ctx.runMutation(api.economyLootTables.defineLootTable, {
  gameId,
  tableId: "boss_dragon_table",
  name: "Dragon Boss Drops",
  rollCount: 3,       // Roll 3 times per drop
  unique: true,       // No duplicate entries per roll
  entries: [
    { type: "item", id: "dragon_scale", amount: 1, weight: 30 },
    { type: "currency", id: "gold", amountMin: 100, amountMax: 500, weight: 50 },
    { type: "item", id: "legendary_staff", amount: 1, weight: 2 },
    { type: "nothing", weight: 18 },
  ],
});

Entry Fields

FieldTypeDescription
type'item' | 'currency' | 'nothing'What this entry drops
idstringItem ID or currency ID (not needed for 'nothing')
weightnumberRelative weight (higher = more likely)
amountnumberFixed drop amount
amountMin / amountMaxnumberRandom amount range (used instead of fixed amount)
nestedTableIdstringRoll another table instead of dropping directly

Table Options

FieldTypeDescription
rollCountnumberHow many times to roll (default 1)
uniquebooleanIf true, each entry can only be picked once per roll

Rolling a Table

Use POST /loot/roll. The system rolls the table, grants all results to the player, and records a loot transaction.

bash
curl -X POST https://gameplaygen.com/api/economy/loot/roll \
  -H "Authorization: Bearer gg_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "gameId": "j97abc...",
    "playerId": "j97def...",
    "tableId": "boss_dragon_table"
  }'
json
{
  "success": true,
  "data": {
    "results": [
      { "type": "item", "id": "dragon_scale", "amount": 1 },
      { "type": "currency", "id": "gold", "amount": 347 },
      { "type": "item", "id": "dragon_scale", "amount": 1 }
    ]
  }
}

Nested Tables

An entry can reference another loot table via nestedTableId. When that entry is rolled, the referenced table is rolled instead. Nesting is capped at 5 levels to prevent infinite loops.

typescript
// Sub-table for rare materials
await ctx.runMutation(api.economyLootTables.defineLootTable, {
  gameId,
  tableId: "rare_materials",
  name: "Rare Materials",
  entries: [
    { type: "item", id: "mithril_ore", amount: 1, weight: 40 },
    { type: "item", id: "dragon_heart", amount: 1, weight: 10 },
    { type: "item", id: "phoenix_feather", amount: 1, weight: 5 },
  ],
});

// Main table references the sub-table
{
  type: "nothing",  // type is ignored for nested
  weight: 15,
  nestedTableId: "rare_materials",
}

Variable Amounts

Use amountMin and amountMax instead of amount for randomized quantities. The actual amount is uniformly distributed between min and max (inclusive).