Weights
Weights are used for random pools, quality pools, rewards, and guarantee systems.
Example
pool:
common: 80
rare: 15
epic: 5Weights are relative. They do not have to add up to 100.
Probability formula
A single outcome's real probability is:
probability = that outcome's weight / sum of all weightsWith this pool the total is 1000:
pool:
common: 700
uncommon: 200
rare: 80
epic: 18
legendary: 2| Outcome | Weight | Probability |
|---|---|---|
| common | 700 | 70% |
| uncommon | 200 | 20% |
| rare | 80 | 8% |
| epic | 18 | 1.8% |
| legendary | 2 | 0.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:
quality:
enabled: true
custom_pool:
common: 70
rare: 25
epic: 5Conditional bonuses
Some modules adjust weights based on the inputs:
quality:
enabled: true
custom_pool:
common: 70
rare: 25
epic: 5
material_bonus:
rare_material:
rare: 10
epic: 5With a rare material, rare goes from 25 to 35 and epic from 5 to 10.
Expression weights
Some modules allow expressions as weight values:
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:
| Level | Field | Default | Meaning |
|---|---|---|---|
Global config.yml under quality.guarantee | enabled | true in the shipped config | Whether the minimum-quality guarantee is active. |
Global config.yml under quality.guarantee | threshold | 10 when absent | Consecutive attempts before minimum is applied. |
Global config.yml under quality.guarantee | minimum | falls back to default_tier | Minimum quality name applied once triggered. |
Per-recipe quality.guarantee | enabled | — | Same meaning, per recipe. |
Per-recipe quality.guarantee | attempts | 10 when absent | Per-recipe counterpart of threshold. |
Per-recipe quality.guarantee | minimum | — | Minimum quality name for this recipe. |
Writing attempts in the global config, or threshold in a recipe, silently falls back to the default of 10.
# 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:
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: 10How 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.