CatGunner CookbookBardCat 1.1.59 · L1 ANALYSIS · tài liệu học
Trang chủCookbook › 02 — Kinh tế & số lớn: InfVal, chuỗi nhân, và bảng bake bằng lặp cắt cụt
Nguồn: 02-kinh-te-so-lon.md · Cập nhật lần cuối: 2026-09-06 23:06 +07 · sha256 nguồn 7c3b72391f6b

02 — Kinh tế & số lớn: InfVal, chuỗi nhân, và bảng bake bằng lặp cắt cụt

Nguồn framework: docs/framework/01-economy-money.md §2–§6, docs/framework/09-prestige-rebirth.md §3.2 (bảng cân bằng thật), [phân tích nội bộ] (hai nhà phân tích hội tụ), gap list (nội bộ) G138a / G142 / G143. Cấp bằng chứng: L1 ANALYSIS — dựng lại clean-room từ disasm + metadata + quan sát runtime; không phải source gốc. Mỗi dòng bên dưới mang nhãn bằng chứng riêng.

Đính chính quan trọng: doc 01 §2 (viết trước) có hai dòng đã bị doc 09 §3.2 + CONVERGENCE + G138a thay thế: (a) ToPrecision(n) CẮT về phía 0, không làm tròn; (b) Balance_Reloaddead code — bảng thật là mảng bake sẵn trong Main.unity, sinh bằng lặp có cắt cụt. Trang này dùng phiên bản mới. Mô hình tham chiếu economy_reference_sim.py tái tạo mảng scene 0/360 lệch với mô hình cắt (mục [M]).

Mục tiêu

Muốn một game idle có tiền tăng vượt double, nhân qua nhiều nguồn multiplier mà không sửa công thức mỗi lần thêm hệ thống, hiển thị gọn trên điện thoại (134.58b), và — quan trọng nhất — bảng cân bằng bạn ship phải đúng bằng bảng designer nghĩ. CatGunner giải quyết trọn bộ này, và cũng để lại một bài học đắt: công thức trong code và bảng trong game không giống nhau.

Game gốc làm gì

Điều game làm Con số / hằng số Mức bằng chứng Nguồn
Kiểu số lớn là package thương mại InfiniteValue.InfVal (Asset Store 194963), không tự viết bignum value = _digits × 10^s_exponent; serialize string s_digits rồi int s_exponent package-source + scene-census doc 01 §2 (G007, G122)
Precision mặc định của đường kinh tế 5 chữ số có nghĩa (20 → (20000,-3), 152.2 → (15220,-2)) runtime-verified doc 01 §2 (parity samples)
ToPrecision(n) cắt về n chữ số (không làm tròn) code 0x44E0E5C → 0x44DF888; 1 202 phần tử mảng scene khớp 100 % mô hình cắt, 0 % mô hình tròn VERIFIED doc 09 §2; CONVERGENCE U3
Tiền tệ là static field trên Gamemanager Money @+0x28, Money_Accum @+0x48, Ruby @+0x68 (InfVal); PetCoin @+0xB8 (int); DiaObscuredInt disasm-proven doc 01 §2
4 event đổi tiền tệ OnMoneyChanged / OnDiaChanged / OnRubyChanged / OnPetCoinChanged (static Action, field-like event) — HUD số dư làm mới chỉ qua chúng; bản dựng lại từng có 14 event bất động do công cụ decompile render accessor rỗng (G376) disasm-proven doc 01 §2
Get_Money(v) v' = MoneyValue_return(v); Money += v'; Money_Accum += v'; OnMoneyChanged?.Invoke() — 85 lần / 40 s disasm-proven + runtime-verified doc 01 §2 (RVA 0x2A4ECBC)
MoneyValue_returnchuỗi nhân thu nhập Upgrade × Rebirth × Skin × Pet × Relic (mỗi cái /100) rồi × 3 nếu Money_Buff disasm-proven + runtime-verified doc 01 §3.2 (RVA 0x2A4E7B0)
Chuỗi là thuần nhân, không có cộng/offset 20 → 152.2, 60 → 456.6 ⇒ hệ số ×7.61 ổn định; 25 mẫu đều khớp k·x runtime-verified doc 01 §2, §6
Cache LRU cho chuỗi nhân, khoá theo giá trị input Dictionary<string,InfVal> + Queue<string>, trần 50 (MoneyCacheMaxSize const) disasm-proven + metadata-proven doc 01 §2, §3.3
Cache phải bị xoá khi bất kỳ multiplier nào đổi Gamemanager.InvalidateMoneyCache() xoá cả dictionary lẫn queue disasm-proven doc 01 §3.3 (RVA 0x2A4E324)
Lose_Money clamp bằng ((InfVal)0).ToPrecision(5); Lose_Ruby clamp bằng (InfVal)0 trần bất đối xứng thật trong binary disasm-proven doc 01 §2 (RVA 0x2A4F6E4 / 0x2A4FACC)
Money_Accum chỉ tăng (wallet vs lifetime) — nhưng bị reset khi Rebirth Money_Reset() đặt cả hai về 0 disasm-proven doc 01 §3.4, §6 (RVA 0x2A4F1F8)
Dia (tiền premium): Get_Dia cộng v vào ví và v × 3 vào sổ gem gửi máy chủ (_getDia); Lose_Dia phản chiếu vào _useDia, không kẹp ở 0; Lose_PetCoin thì có kẹp ×3 disasm-proven doc 01 §2 (G395)
Save() của game throttle 5 s thời gian thực (nhiều biến đổi tiền tệ liên tiếp không ghi đĩa dồn dập) 5 s disasm-proven doc 01 §2 (G395)
Notation hiển thị: ≤ 3 chữ số có nghĩa + đơn vị chữ cái thường, bước 3 bậc InfVal_change(): mag = exp + len(digits); if mag>=4: mag=(mag-1)%3+3; ToString(mag) — ví dụ 297a, 203.09a disasm-proven + runtime-verified doc 01 §3.5 (RVA 0x44E47D4; 14 cặp parity)
Formatter hiển thị cắt (45660,-2) = 456.6 → "456" (làm tròn sẽ ra 457) runtime-verified (1 mẫu phân biệt) doc 01 §3.5
Đơn vị viết HOA ở text bay trong thế giới, thường ở HUD — cùng giá trị 3.12A (text sát thương) vs 3.12a (panel nâng cấp), 149.75A (tiền bay) vs 134.58b (số dư) observed doc 01 §3.5.1 (ảnh user_screenshot_stage5-4.png)
Throttle floating text theo ô lưới không gian BUCKET_SIZE = 1.5, DAMAGE_TEXT_COOLDOWN = 0.12, MONEY_TEXT_COOLDOWN = 0.2; CanSpawnText 287 lần / 40 s metadata-proven + runtime-observed doc 01 §2 (dump.cs:583820)
Bảng cân bằng lúc chạy = mảng serialize trong scene, không phải hàm sinh Upgrade_manager Price/Power/Money/Speed @Main.unity:20353425/20373428/20393431/20413434; Rebirth_manager Value ×3 @20451299/20491308/20531317, Price @20571320 scene-YAML doc 09 §2, §3.2
Balance_Reload không bao giờ chạy trong 1.1.59 CodeRefsTo = [] cho 0x2B4A02C (Upgrade), 0x2B21CB4 (Rebirth), 0x2B0AB4C (Gun); không string literal, không UnityEvent trong scene VERIFIED doc 09 §2; CONVERGENCE; G138a
Mảng Money/Power của upgrade sinh bằng lặp có cắt cụt [0]=100; [i]=trunc3([i-1]·1.08) — tái tạo 0/60 lệch VERIFIED doc 09 §3.2
Giá upgrade dùng double nhị phân 1.38 acc=50; acc=trunc4(acc·1.38); price=trunc3(int(acc))[1] = 68, không phải 69 (vì 1.38 = 1.3799999999999998…) VERIFIED doc 09 §2
Sai lệch tích luỹ so với dạng đóng 100·1.08^lv lv 10: 210 vs 215.89 (−2.73 %); lv 46: 3 120 vs 3 447 (−9.50 %); lv 50: −10.24 %; lv 1 000: ~6.5 lần VERIFIED doc 09 §3.2
Ảnh chụp save thật khớp mảng scene 4/4 Speed[46] = 468, Power[46] = Money[46] = 3 120 → 3.12a, Price[46] = 1.34×10⁸ → 134b observed + scene-YAML doc 09 §3.2
Mô hình tham chiếu Python thuần tự kiểm 58/58 vector (hạng A oracle-live + hạng B dẫn xuất) + mục [M] mảng scene 0/360 lệch derived doc 01 §5.6; doc 09 §3.2
Liveops không ghi đè kinh tế qua kênh nào ServerConfig 15 field toàn ads; Firebase RC 5 key version/offerwall; PlayFab không TitleData VERIFIED (G144 còn mở) LIVEOPS_SURFACE §0; CONVERGENCE U4

Công thức / pseudocode

1. Kiểu số (mô hình hoá InfVal) — L1 ANALYSIS, doc 01 §3.1 / §5.2

BigNum { digits: BigInteger, exponent: int }      value = digits × 10^exponent
precision = 5 chữ số có nghĩa                     (bằng chứng: (20000,-3), (15220,-2))
serialize = (string digits, int exponent)          khớp s_digits / s_exponent của SOT
ToPrecision(n)  → CẮT về n chữ số có nghĩa (về phía 0)      [VERIFIED: 0x44E0E5C, doc 09]
ToString(n)     → CẮT, dùng cho hiển thị                     [runtime-verified: 456.6 → "456"]

2. Chuỗi thu nhập — L1 ANALYSIS, RVA 0x2A4E7B0 (runtime-verified)

private static InfVal MoneyValue_return(InfVal value)
{
    string key = GetMoneyCacheKey(value);
    if (_moneyCache != null && _moneyCache.TryGetValue(key, out InfVal cached))
        return cached;                                          // hit — không tính lại

    InfVal r = value * Upgrade_manager.Money_return() / 100;   // Money_balance[Lv]  (mảng scene) — RESET khi rebirth
    r = r * Rebirth_manager.Money_return() / 100;              // Value_balance[2][Lv] + 100     — BỀN qua rebirth
    r = r * Skin_manager.Money_Accum       / 100;              // int %
    r = r * Pet_manager.Pet_Money_Accum    / 100;              // InfVal %
    r = r * Relic_manager.Money_Value      / 100;              // InfVal %
    if (Buff_manager.instance != null &&
        Buff_manager.instance.IsBuffActive(AD_Type.Money_Buff))
        r = r * 3;                                             // buff quảng cáo: ×3 cứng, KHÔNG phải +200 %

    if (_moneyCache != null) {                                 // LRU trần 50
        if (_moneyCache.Count >= 50) _moneyCache.Remove(_moneyCacheKeys.Dequeue());
        _moneyCache[key] = r;  _moneyCacheKeys.Enqueue(key);
    }
    return r;
}
// Gamemanager.InvalidateMoneyCache() [0x2A4E324] xoá cả hai — BẮT BUỘC gọi sau khi mua upgrade / đổi skin / pet / relic / buff.

Gợi ý framework (doc 01 §4, không phải SOT): mỗi nguồn là một IMultiplierSource.PercentFor(kind) trả 100 = trung tính; pipeline sources.Aggregate(base, (acc, s) => acc * s.PercentFor(kind) / 100); buff/event là tầng riêng nhân sau và phải đi qua service fail-closed; source bắn event để cache tự invalidate thay vì trông chờ call-site nhớ.

3. Formatter hiển thị — L1 ANALYSIS, RVA 0x44E47D4

public readonly string InfVal_change()
{
    int mag = exponent + digits.ToString().Length;   // tổng bậc độ lớn
    if (mag >= 4) mag = (mag - 1) % 3 + 3;           // ép về 3..5, canh theo nhóm nghìn
    return ToString(mag);                            // maxDisplayedDigits; đơn vị a, b, c… bước 3 bậc; CẮT
}
// Ground truth: (100,0)→"100"  (500,-1)→"50"  (297,3)→"297a"  (45660,-2)→"456"  (15220,-2)→"152"  (0,-4)→"0"

4. Bảng cân bằng — BAKE BẰNG LẶP CẮT CỤT, không phải pow() — VERIFIED, doc 09 §3.2

# Tái tạo đúng mảng trong Main.unity (0/360 lệch, lv 0..59 đã đối chiếu) — economy_reference_sim.py mục [M]
def trunc(x, n): ...   # cắt về n chữ số có nghĩa, về phía 0 (ToPrecision của InfVal)

# Upgrade Money_balance / Power_balance (Main.unity:20393431 / :20373428)
v = 100
for i in range(1, N): v = trunc(v * 1.08, 3)          # KHÔNG PHẢI 100 * 1.08**i

# Upgrade Speed_balance (:20413434) — tuyến tính, khớp chính xác
speed[lv] = 100 + 8 * lv

# Upgrade Price_balance (:20353425) — 1.38 là DOUBLE NHỊ PHÂN 1.3799999999999998…
acc = 50
for i in range(1, N):
    acc = trunc(acc * 1.38, 4)
    price[i] = trunc(int(acc), 3)                     # price[1] = 68, không phải 69

# Rebirth Value_balance[0] (Damage) và [2] (Money) (:20451299 / :20531317)
value[lv] = trunc(int(trunc(1.05**lv * 100 - 100, 3)), 3)   # ≈ 1.05^lv nhưng LUÔN thấp hơn
# Rebirth Value_balance[1] (Speed) (:20491308)
value[lv] = 5 * lv
# Rebirth Price_balance (Ruby, :20571320)
price[lv] = trunc(int(trunc(3 * (lv + 1) ** 1.6, 4)), 3)

Bảng lệch (doc 09 §3.2, đọc thẳng từ mảng scene):

lv Money_balance mảng thật 100·1.08^lv lệch
1 108 108.00 0 %
2 116 116.64 −0.55 %
10 210 215.89 −2.73 %
46 3 120 3 447.41 −9.50 %
50 4 210 4 690.16 −10.24 %
1 000 4.06e34 2.65e35 ~6.5 lần

Sai số luôn một chiều (thấp hơn) vì cắt không bao giờ làm tăng.

4b. Bảng chỉ số theo HẠNG — hàm mũ hai cơ số + làm tròn theo chữ số có nghĩa (doc 01 §4 mục 8–9)

Bảng ở §4 là bảng cấp nâng cấp (hàng trăm mức, bake bằng lặp cắt cụt). Còn khi bạn cần một bảng hạng vật phẩm ngắn (10–13 hạng: pet, cá, súng), khuôn đã chạy thật đơn giản hơn nhiều — và mỗi trục chỉ số được một cơ số riêng:

# 13 hạng cá — công thức thật, đã đối chiếu với mảng bake bằng hai tổng đọc trên máy
for i in range(13):
    Damage = round_sig(4  * 3   ** i, 2)          # trục CHÍNH,  cơ số 3
    CriPer = clamp(round_sig(     2.5 ** i, 2), 0, 100)   # trục PHỤ, cơ số 2.5, có TRẦN
    CriVal = round_sig(8  * 2.5 ** i, 2)

def round_sig(x, digits=2):                       # 2 chữ số có nghĩa
    if x == 0: return 0
    exp   = floor(log10(abs(x)))
    scale = 10 ** (digits - exp - 1)
    return int(round(round(x * scale) / scale))   # LÀM TRÒN ToEven: 125 -> 120, không phải 130
hạng 0 1 2 3 4 5 6 7 8 9 10 11 12
Damage % 4 12 36 110 320 970 2900 8700 26000 79000 240000 710000 2100000
CriPer % 1 2 6 16 39 98 100 100 100 100 100 100 100
CriVal % 8 20 50 120 310 780 2000 4900 12000 31000 76000 190000 480000

Ba thứ đáng chép:

  1. round_sig(x, 2) chứ không phải số thô. Bảng đọc lên như bảng do người viết (110, 320, 970) chứ không phải 108, 324, 972. Chi phí: sáu dòng. Nhớ khai báo tường minh là làm tròn ToEven, không phải cắt như ToPrecision ở §4 — hai thao tác khác nhau, dùng lẫn là ra bảng khác (xem "Bẫy đã gặp").
  2. Hai cơ số tách hai trục ra theo thời gian. 3^12 ≈ 531 000 so với 2.5^12 ≈ 60 000: sau 12 bậc trục chính bỏ xa trục phụ ~9 lần. Bạn chọn tốc độ tương đối giữa các chỉ số bằng một con số mỗi trục, không phải bằng 13 ô bảng gõ tay.
  3. Chỉ số phụ nên có TRẦN, và trần nên nằm GIỮA bảng. CriPer chạm 100 ngay từ hạng 6 ⇒ một nửa bảng không cộng thêm một điểm tỉ lệ chí mạng nào. Cố ý: nó ngăn con số vô nghĩa ("chí mạng 400%") mà vẫn giữ công thức gọn, và rút gọn không gian quyết định theo thời gian — đầu game người chơi cân ba chỉ số, cuối game chỉ còn một trục. Kỹ thuật: clamp phải nằm trong hàm sinh (bảng bake đã sạch) và lặp lại ở chỗ hiển thị TỔNG — tổng của nhiều vật phẩm vẫn vượt trần dù mỗi vật phẩm đã bị chặn.

Mẹo đối chiếu rẻ nhất (khi không muốn mở file scene hàng trăm MB để đọc mảng bake): chọn một hồ sơ save có các hạng khác nhau rồi so TỔNG hiển thị trên UI, không so từng ô. Với kho [0, 0, 1, 2, 5], bảng trên cho tổng Damage = 1026 và tổng CriVal = 866; màn hình in +1.02a%991% (= 866 + hằng nền 125). Hai tổng độc lập khớp là đủ để kết luận mảng bake đúng bằng công thức — mà chỉ tốn một lần chụp màn hình.

5. Ví tiền — hợp đồng hành vi (doc 01 §5.3)

GetMoney(v)        : v' = Chain(v); Money += v'; MoneyAccum += v'; fire(OnMoneyChanged); return v'
GetMoney(v, pos)   : v' = GetMoney(v); SpawnText(v'.ToDisplayString(), pos)     [text bay trong thế giới]
LoseMoney(v)       : Money -= v; if (Money < 0) Money = Zero.ToPrecision(5); fire()
MoneyReset()       : Money = 0; MoneyAccum = 0; fire()                          [gọi bởi Rebirth()]
GetRuby / LoseRuby : Ruby ±= v; if (Ruby < 0) Ruby = 0   [KHÔNG ToPrecision]; fire()
LosePetCoin        : clamp >= 0; có call-site analytics (R1 → IAnalyticsService)

Bẫy đã gặp

  • Dùng pow() / dạng đóng để sinh bảng. Mô hình đầu tiên của chính dự án này dùng công thức + làm tròn: dự đoán panel Lv.46 là 3.5a và giá 136b; ảnh chụp thật là 3.12a134b. Sai 2/3 ô — và chính hai ô lệch đó lật lại mô hình (doc 09 §3.2, D008/D010). Bảng là nguồn sự thật; hàm sinh chỉ là công cụ Editor. Với restore: giữ nguyên mảng serialize, đừng gọi Balance_Reload.
  • Nhầm hai thao tác "làm tròn" và "cắt". ToPrecision(n) (số học) và ToString(n) (hiển thị) đều cắt trong SOT; doc 01 §2/§3.5 còn ghi ToPrecision làm tròn — đó là dòng cũ, đã bị doc 09 §2 + CONVERGENCE U3 thay thế. Một InfVal khác version (làm tròn) sẽ sinh ra một nền kinh tế khác SOT (G007a: bản store v2.2.0 có thể làm tròn — phải chạy InfVal.ParityTest.cs để chốt).
  • 1.38 không phải 1.38. Hằng số là double nhị phân; Price_balance[1] = 68 chứ không 69. Nếu bạn bake bằng decimal bạn sẽ ra bảng khác.
  • Quên InvalidateMoneyCache() sau khi mua upgrade ⇒ người chơi trả tiền mà thu nhập không tăng cho tới khi key bị đẩy khỏi LRU — bug im lặng, cực khó truy (doc 01 §3.3).
  • Một formatter cho mọi chỗ. SOT hiển thị 3.12A ở text bay và 3.12a ở HUD. Restore chỉ dùng InfVal_change() sẽ sai ở text bay. Đường code viết hoa chưa biết (UNKNOWN-pending) — đừng giả định .ToUpper().
  • Đổi tên field serialize của kiểu số. Main.unity serialize InfVal inline (s_digits/s_exponent) vào hàng nghìn component; nâng version package mà đổi tên field = âm thầm mất toàn bộ số liệu kinh tế (doc 01 §3.1; G122 đã kiểm layout v2.2.0 khớp).
  • Trộn kinh tế với máy móc toàn vẹn. Một nửa Gamemanager (70 method) là chống gian lận thời gian / autosave; tầng kinh tế phải chạy được không cần Unity, không cần mạng — đó là lý do có simulator (doc 01 §5).
  • Stub quảng cáo trả "đã thưởng". Nhánh ×3 chỉ được bật bởi service thật; NullAdService trả NotAvailable (R1, doc 01 §3.6).
  • Lấy bảng số từ CONSTRUCTOR thay vì từ scene. Với field public/[SerializeField] có mặt trong scene, scene THẮNG lúc chạy; ctor chỉ là mặc định khi AddComponent lên một prefab mới. Dự án này sai hai lần vì bỏ qua điều đó: FormationRadius (ctor 10 / scene 2.5) và bảng tỉ lệ gacha cá (ctor {40,30,20,10} / scene {60,30,10,0} — đã publish bảng sai ra dashboard trước khi bị bắt). Luật đọc: public/[SerializeField] có trong scene ⇒ scene thắng; private không [SerializeField] ⇒ ctor là giá trị thật (scene không cứu được); const/static readonly ⇒ ctor thắng. Với bảng chỉ số cá (§4b) thì hai nguồn khớp nhau — nên bài học không phải "đừng tin ctor" mà là "phải kiểm loại field trước khi tin bất kỳ con số cân bằng nào". Phép thử rẻ khi chưa đọc được dữ liệu gốc: một kiểm tra tần suất — N lượt liên tiếp ra cùng kết quả có xác suất bao nhiêu với mỗi bảng? (0,6⁵ ≈ 7,8% với bảng thật so với 0,4⁵ ≈ 1% với bảng ctor — đủ lệch để đi đọc lại.)

Checklist khi tự làm

  • [ ] Tiền là một kiểu bignum theo chữ số có nghĩa, serialize được dạng text; không có double trên đường thu nhập.
  • [ ] Mọi nguồn multiplier trả phần trăm, mặc định 100; thêm hệ thống = đăng ký thêm source, không sửa công thức.
  • [ ] Buff / event / ad là tầng riêng, nhân sau, đi qua service fail-closed.
  • [ ] Cache theo giá trị input, LRU trần nhỏ, và invalidate tự động khi source đổi (source bắn event).
  • [ ] Tách Wallet (tiêu được) khỏi LifetimeEarned (chỉ tăng); quyết định rõ cái nào reset khi prestige.
  • [ ] Formatter là contract có test parity (bộ cặp (giá trị → chuỗi)), quy tắc cắt/làm tròn khai báo tường minh.
  • [ ] Bảng cân bằng: chọn tường minh "đệ quy có lượng tử" hay "dạng đóng", ghi quantizeDigits, và bake ra asset — asset là nguồn sự thật.
  • [ ] Nếu bake, kiểm bằng bộ vector thật (ảnh chụp / oracle) ở ít nhất 3 level cách xa nhau.
  • [ ] Bảng hạng vật phẩm (10–13 hạng): một cơ số mũ cho mỗi trục chỉ số, làm tròn 2 chữ số có nghĩa, và ít nhất một trục phụ có trần chạm giữa bảng (§4b).
  • [ ] clamp của trục phụ phải có ở hai chỗ: trong hàm sinh ở chỗ cộng tổng để hiển thị.
  • [ ] Đối chiếu bảng bake bằng TỔNG của một hồ sơ nhiều hạng khác nhau, không cần đọc mảng trong scene.
  • [ ] Throttle floating text ở tầng framework (ô lưới + cooldown theo loại text).
  • [ ] Tầng kinh tế chạy độc lập (simulator không đồ hoạ) trước khi cắm vào Unity.

Điều chưa biết (UNKNOWN)

Chép từ doc 01 §6 và doc 09 §5, cập nhật theo CONVERGENCE:

  • Đường code viết HOA đơn vị ở text bay (3.12A) vs thường ở HUD (3.12a): UNKNOWN-pending — hiện tượng đã xác nhận bằng ảnh, đường code chưa. Khoá bằng disasm Damage_Txt_Spawn 0x2A50594, Critical_Txt_Spawn 0x2A506AC, Money_Txt_Spawn 0x2A4EF48.
  • ToString(int) cắt: đã chốt CẮT nhưng dựa trên một mẫu phân biệt (456.6 → "456"). Khoá bằng mở rộng parity ở giá trị lớn + InfVal.ParityTest.cs.
  • Thứ tự đơn vị sau z (aa, ab… hay A, B) ở bậc rất lớn: UNKNOWN — mẫu hiện tại dừng ở a.
  • ~~GetMoneyCacheKey sinh key chính xác thế nào~~: ĐÃ ĐÓNG (G400) — chuỗi 7 phần giá trị | Upgrade | Rebirth | Skin | Pet | Relic | buff đang bật, tức key liệt kê đúng mọi nguồn multiplier của chuỗi thu nhập ⇒ đổi bất kỳ mắt nào là key khác, cache tự vô hiệu (bổ sung cho InvalidateMoneyCache). Stub trả null của nó từng làm tra cache ném và huỷ khoản trả offline trên thiết bị (G394).
  • Phân rã hệ số ×7.61 theo từng nguồn: UNKNOWN — chỉ đo được tích, trên một save đã tiến xa không ghi lại state (G142). Lần oracle sau phải log 5 getter Money_return + IsBuffActive cùng lúc.
  • Version InfVal chính xác mà SOT ship: UNKNOWN — chỉ biết "cũ hơn 2.2.0, có API unitsList / DisplayOption / InfVal_change".
  • Kinh tế offline là một mô hình riêng (OfflineProgressSimulator, cap MaxOfflineSeconds 28800 + relic Offline_Value·60): đã dựng trọn — harness độc lập tái tạo khoản trả của game gốc 17 chữ số (G382; 12 luật + ví dụ ở 14). Đính chính: Skin/Pet/Relic áp offline, chỉ buff quảng cáo ×3 không. Fallback Math.Pow(1.08) không thể chạy trong 1.1.59 (doc 01 §5.8). Mô hình tham chiếu economy_reference_sim.py không có mô hình clear-wave nên không cho ra được con số tiền — gap đã ghi (G1000).
  • Cache LRU (_moneyCache, _moneyCacheKeys) và _textCooldowns được cấp trong static ctor của Gamemanager — công cụ decompile bỏ rơi khởi tạo này; bản dựng lại thiếu nó NRE ở lần đầu chạm cache (G319). Không phải UNKNOWN, là bẫy khôi phục đã đóng.
  • Mảng scene chỉ mới đối chiếu lv 0…59; điểm gãy ×1.002 (lv ≥ 14 999) và ×2.0 (lv ≥ 5 000) chỉ tồn tại trong Balance_Reload đã chết — UNKNOWN trong mảng thật ở tầm level đó.
  • Đã giải quyết (không còn UNKNOWN, ghi để khỏi tìm lại): lệch giá 136b vs 134b và panel 3.12a3500 trong doc 01 §6 là do mô hình cũ dùng công thức + làm tròn; mảng scene khớp 4/4 (doc 09 §3.2). Money_Accum có bị reset khi rebirth: . Liveops ghi đè kinh tế: KHÔNG (CONVERGENCE U4), ngoại trừ rủi ro G144 (Google Sheet súng) còn phải probe.

Đọc thêm

  • docs/framework/01-economy-money.md — đặc tả module kinh tế chạy độc lập + simulator (§5)
  • docs/framework/09-prestige-rebirth.md §3.2 — "công thức của designer vs bảng đã ship"
  • [F001: economy_reference_sim.py] — chạy python3 để tự kiểm 58 vector + mục [M]
  • [phân tích nội bộ] — U3 (ToPrecision cắt), U4 (không remote override), dead code
  • [liveops catalog: LIVEOPS_SURFACE.md] §0, §4 — liveops và rủi ro G144
  • 03 Prestige / Rebirth — mắt Rebirth_manager.Money_return() trong chuỗi
  • gap list (nội bộ) — G007, G007a, G122, G129, G138a, G142, G143