Simulation: Gacha System (BETA)

Add randomized loot boxes, booster packs, and gacha pulls to your game. Players open packs to receive random rewards from weighted pools, with pity counters, rarity guarantees, and new-item bias — all validated server-side.

This is a self-contained recipe. Everything you need — config, client code, and patterns — is on this page.


Where Config Goes

All JSON config on this page goes in your project's server config — either a single config.json file or split across files in a config/ directory. Each file must have a top-level "simulation" key. The CLI uploads config automatically when you rundot deploy.

my-game/
├── config.json                      ← option A: all-in-one (add "simulation" key)
├── config/                          ← option B: split files for complex games
│   └── boosters.config.json
├── game.config.json                 ← game ID + build settings only (separate)
└── package.json

game.config.json is for CLI metadata only. Simulation config goes in config.json or the config/ directory.


What You'll Build

  • Booster packs that players open for randomized rewards

  • Weighted loot tables with rarity tiers (common → legendary)

  • A pity system that guarantees rare drops after N unlucky opens

  • Minimum rarity guarantees per pack tier

  • New-item bias so players are more likely to get items they don't own yet

  • Server-side resolution — the client never determines drop results


Server Configuration

Step 1: Define Pack Entities

Each pack type is an entity that tracks how many the player owns:

Step 2: Define Reward Entities

The items players can receive. Tag them by rarity so loot tables can filter:

Step 3: Define Pity Counter Entities

Track how many opens since the last high-rarity drop:

Step 4: Define Loot Tables

Loot tables are the pools the server draws from when a pack is opened.

Rarity pool tables — one per rarity tier, using tag-based filtering:

uniform tables automatically include all entities matching the tags. When you add a new rarity:common unit entity, it's automatically part of the common pool — no table edits needed.

Rarity selection tables — weighted roll to pick which pool, per pack tier:

Each entry uses tableRef to chain into a rarity pool. Weights determine the odds of rolling each rarity. For common_pack_cards: 75% common, 20% rare, 4.5% epic, 0.5% legendary.

Bonus coin tables — guaranteed currency alongside card drops:

Step 5: Define Pack-Opening Recipes

Each recipe consumes 1 pack and rolls from the appropriate loot tables:

How Rolls Work

Each roll entry:

  1. table — Which loot table to draw from

  2. count — How many draws. { "min": 3, "max": 5 } means 3–5 random draws.

  3. guarantees — After all random draws, if the results don't include at least minCount items with the specified tag, extra draws are forced. E.g., { "tag": "rarity:rare", "minCount": 1 } ensures at least one rare in every rare pack.

  4. pity — Increments counterEntity each time the pack is opened. When the counter reaches threshold, the next roll guarantees an item with guaranteedTag. resetOnHit: true resets the counter when the guaranteed rarity drops naturally (before hitting the threshold).

How New-Item Bias Works

When newItemBias.enabled is true, items the player doesn't own yet get their selection weight multiplied by biasMultiplier. A multiplier of 2.0 means unowned items are twice as likely to be selected. This helps new players build a collection quickly.


Client-Side Implementation

Step 1: Read Pack Inventory

Step 2: Subscribe to Pack Count Changes

Step 3: Open a Pack

Step 4: Display Results

After opening a pack, read updated state to show the player what they received:

Step 5: Check if Player Can Open


Purchasing Packs

With Premium Currency (Wallet)

Free Booster on Cooldown

Grant a free pack every 4 hours:

The pack is granted immediately via beginEffects, while the 4-hour cooldown runs. The player can claim again after the cooldown expires.


Common Variations

Faction-Specific Packs

Use weighted loot tables with explicit entity entries for curated faction packs:

Wildcard Conversion

A small chance to replace a drop with a wildcard (trade-in) token:

When a drop triggers conversion, the player receives the wildcard entity instead of the original item. Higher rarities have higher conversion rates.

Curated Starter Pack

Fixed contents with no randomization:


Displaying Drop Rates

For legal compliance and player trust, read loot table weights from config:


Best Practices

  • All randomization happens server-side — the client never determines drop results.

  • Use uniform (tag-filtered) tables for pools that grow over time. New entities with matching tags are automatically included.

  • Use weighted tables when you need explicit control over individual item probabilities.

  • Chain tables with tableRef to separate "which rarity" from "which item within that rarity."

  • Implement a pity system for high-rarity items to prevent frustrating dry streaks.

  • Set newItemBias to help new players build collections faster.

  • Display drop rates to players — read them from getConfigAsync().

  • Use beginEffects for free-booster cooldowns so the pack is granted instantly while the timer runs.

Last updated