Recruit Rates — Step Probability + Per-Unit Weight
How the gacha (招募 / Recruit) decides which unit you actually get. The roll is two-stage: first pick a rarity bucket (Step) using a fixed rate ladder, then pick a specific unit inside that bucket using each unit's Weight from RoleDataTable.
Source: decompiled
GeneralDataTables.SummonDataTable+SummonManager.SummonRoleAsync/SummonSkinAsyncindata-session/intermediate/decompiled_summon.cs. Constants are hot-loaded fromMainScripts.dll(HybridCLR HotUpdate), NOT in any DataTable — re-decompile after every patch (datamine-extractoragent diffsdecompiled_summon.cs).
Cost per single roll
| Resource | Cost | Source field |
|---|---|---|
| Gem | 50 per roll | summonGemConsumption = 50 |
Standard Recruit Ticket (D00006_031 推荐信) |
1 per roll, replaces gem | inventory check before gem deduction |
Alter Recruit Ticket (D00006_032 异化推荐信) |
1 per roll on Alter banner, replaces gem | same |
Tickets are consumed first; gem only spent when invitation count runs out. 10-pull = 10 × 50 = 500 gem if no tickets. First 10-pull on Alter banner is free (usedFirstSkinBundleSummon flag).
Stage 1 — pick the Step bucket
Per roll, the game does WeightedRandom([C-weight, B-weight, A-weight, S-weight]) where the weights come from StepSummonPossibility(step):
| Step | Display | Step weight | Per-roll % |
|---|---|---|---|
| S | 3★ vàng | 20 | 2.0% |
| A | 2★ tím | 60 | 6.0% |
| B | 1★ xanh | 360 | 36.0% |
| C | 0★ xám | 560 | 56.0% |
Total = 1000 → percentages are exact (no normalisation rounding).
Pity nudge to S in 10-pull: inside a 10-pull batch, the code accumulates a counter num4 from "missed S rolls" and adds it onto the S weight on each subsequent roll within the same batch. So a 10-pull biases the last few rolls toward S if early rolls didn't hit one. (Exact accumulator math: see SummonRoleAsync lines 1156–1172 in the decompile.)
10th-roll guarantee on Alter pool: if no 3★ rolled in the first 9 of an Alter 10-pull, the 10th is forced to A minimum (if (num2 == 9 && ConvertStepToInt(text) < 3) text = "A" at line 1286–1289). Standard pool has no equivalent forced floor.
Stage 2 — pick the unit inside that Step
Once a Step bucket is chosen, the game builds the candidate list: every unit in the active pool whose Step matches the rolled bucket. Each unit's Weight (RoleDataTable.Weight) is then used in another WeightedRandom to pick the specific unit.
RoleDataTable.Weight per Step (uniform within a Step):
| Step | RoleDataTable Weight |
|---|---|
| S | 10 |
| A | 100 |
| B | 1000 |
| C | 10000 |
Because every unit of the same Step shares the same Weight, Stage-2 effectively picks uniformly at random from all in-pool units of that Step. The numeric value differs per Step but cancels out within a bucket.
So the Weight column on
RoleDataTableis largely vestigial in the current build — it would only matter if devs ever ship a unit with a non-default Weight. Currently 0 such cases across all 166 entries.
Pool composition (current data)
The active pool depends on banner:
- Standard Recruit (SummonDataTable, 128 entries) — bare-id units, no _NNN suffix.
- Alter Recruit (SkinSummonDataTable, 34 entries) — alter units with _NNN suffix.
Per-roll odds for any specific unit = (Step rate) × (1 / count of units of that Step in the active pool). Example, on Standard pool:
- If pool has 12 S-step units → odds of any one specific S = 2.0% / 12 ≈ 0.167% per roll.
- If pool has 24 A-step units → odds of any one specific A = 6.0% / 24 ≈ 0.25% per roll.
For the 128-unit standard pool, the web-session/gacha-optimizer/gacha_optimizer_128unit.html computes these per-tag, per-combo (used for the daily free recruit tag-pick decision).
Rate-up windows
Each banner's poolInfo.upRoles list marks rate-up units. The mechanic differs slightly between pools:
- Standard: if Stage-1 rolls the rate-up unit's Step, there's a 50% chance the result is pinned to that specific rate-up unit, otherwise normal Stage-2 weighted pick (which still can land on the rate-up unit). So effective rate-up rate ≈
step% × (50% + 50% × 1/poolSize). - Example: S-step rate-up on a pool with 12 S units →
2.0% × (0.5 + 0.5/12) = ~1.04%for the up unit, vs~0.083%for any non-up S. - Alter: multiple rate-ups supported (
list2); when the rolled step matches an up unit, the result is biased toward an up unit via a similar 50% pin (seeSummonSkinAsynclines 1300+ for the exact branch).
StartTime / EndTime columns on SummonDataTable / SkinSummonDataTable flag the active window per rate-up.
Daily Free Recruit (tag-pick) flow
Daily free recruit uses a different code path — it filters the pool by the player's chosen 1–3 tags first, then picks via GetResultRole() at line 1689–1693:
List<int> weightList = filterRoleList.Select(s => s.Weight).ToList();
return filterRoleList[GeneralHelper.WeightedRandom(weightList)];
This is raw weighted pick over RoleDataTable.Weight, no Step-bucket pre-roll. Because Weight is inverse to rarity (S=10, C=10000), the chance of pulling a specific unit is unit.Weight / sum(filterPool.Weight) — meaning filter-pool composition matters enormously for hitting an S.
Maximising "S-density" of the filter pool = maximising chance of pulling a 3★. The optimiser tool answers exactly that: it enumerates all valid 1-tag-per-category combos and reports which combo yields the highest S share.
Cross-refs
- duplicate-shards — what you get when a roll lands on a unit you already own.
web-session/gacha-optimizer/gacha_optimizer_128unit.html— daily free recruit tag-combo picker (uses Stage-2-only formula).data-session/mgg_datamine/RoleDataTable.md—Weightcolumn.data-session/mgg_datamine/SummonDataTable.md/SkinSummonDataTable.md— pool membership + rate-up windows.
Implementation notes
- All per-roll constants live in
GeneralDataTables.SummonDataTable(class wrapper inside MainScripts.dll). To audit after a patch:diff data-session/back-up/<prev>/intermediate/decompiled_summon.cs data-session/intermediate/decompiled_summon.csand watch theSummonDataTableblock (lines ~112–153 currently). - If
StepSummonPossibilityladder changes, update this page andweb-session/gacha-optimizer/gacha_optimizer_128unit.html(the optimizer hardcodes the rate ladder for its expected-value calculations).