Skip to content

Weights

Weights are used for random pools, quality pools, rewards, and guarantee systems.

Example

yaml
pool:
  common: 80
  rare: 15
  epic: 5

Weights are relative. They do not have to add up to 100.

Probability formula

A single outcome's real probability is:

text
probability = that outcome's weight / sum of all weights

With this pool the total is 1000:

yaml
pool:
  common: 700
  uncommon: 200
  rare: 80
  epic: 18
  legendary: 2
OutcomeWeightProbability
common70070%
uncommon20020%
rare808%
epic181.8%
legendary20.2%

An entry whose weight is zero or negative is dropped from the pool, and a pool whose total weight is not positive rolls nothing at all.

Pool forms

Plain map

The simplest pool is a key-value map:

yaml
quality:
  enabled: true
  custom_pool:
    common: 70
    rare: 25
    epic: 5

Conditional bonuses

Some modules adjust weights based on the inputs:

yaml
quality:
  enabled: true
  custom_pool:
    common: 70
    rare: 25
    epic: 5
  material_bonus:
    rare_material:
      rare: 10
      epic: 5

With a rare material, rare goes from 25 to 35 and epic from 5 to 10.

Expression weights

Some modules allow expressions as weight values:

yaml
quality:
  custom_pool:
    common: '100 - %material_score%'
    rare: '%material_score% * 0.5'
    epic: '%material_score% * 0.2'

Whether expressions are accepted depends on the calling module.

Guarantee rules are not a CoreLib feature

CoreLib's own WeightedPool implements weighted rolling only: it has no guarantee, counter, or pity logic. The guarantee fields below are implemented by EmakiForge, so they are only available where Forge reads them; do not expect them in other modules' pools.

Forge also uses a different key name at each level:

LevelFieldDefaultMeaning
Global config.yml under quality.guaranteeenabledtrue in the shipped configWhether the minimum-quality guarantee is active.
Global config.yml under quality.guaranteethreshold10 when absentConsecutive attempts before minimum is applied.
Global config.yml under quality.guaranteeminimumfalls back to default_tierMinimum quality name applied once triggered.
Per-recipe quality.guaranteeenabledSame meaning, per recipe.
Per-recipe quality.guaranteeattempts10 when absentPer-recipe counterpart of threshold.
Per-recipe quality.guaranteeminimumMinimum quality name for this recipe.

Writing attempts in the global config, or threshold in a recipe, silently falls back to the default of 10.

yaml
# Per-recipe form
quality:
  enabled: true
  custom_pool: []
  guarantee:
    enabled: true
    attempts: 60
    minimum: "无暇"

The counter increases while results stay below minimum, and once it reaches the configured count the next roll is raised to at least minimum. Reaching minimum or better resets the counter.

Multiple and nested pools

Some modules layer pools, rolling a category first and then an outcome inside it:

yaml
reward:
  category_pool:
    equipment: 30
    material: 50
    currency: 20
  equipment_pool:
    sword: 40
    armor: 35
    accessory: 25
  material_pool:
    common_ore: 60
    rare_ore: 30
    gem_fragment: 10

How nesting works is decided by the calling module.

Verifying a distribution

Roll a large sample on a test server using whatever batch command the module provides, then compare against the theoretical share. With common=70, rare=25, epic=5, 1000 rolls should land near 700 / 250 / 50. Real results fluctuate; larger samples converge closer to the theoretical probability.

Advice

Avoid negative weights and all-zero pools. For rare outcomes, consider guarantee rules to improve player experience.