feat(state): 迁移 tipsState/rankUpDialogState/battleResult/captchaState/modalScreen/droppableGold/eventGoods/creatorTagState/rewardedVideosState/passwordChangeState

This commit is contained in:
TermiNexus committed 2026-10-24 14:43:51 +08:00
1 parent 6d2487d9ea
commit 8633fc91c1
17 files changed
+1206 -68

No files matched your search

+30 -6
View File
@@ -143,7 +143,7 @@
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
- 已迁移切片 **33/89**:
- 已迁移切片 **43/89**:
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
@@ -256,12 +256,35 @@
- `referralEvent` → `pQ` / `lh.osk`(`ReferralEventReducer.kt`):14 字段,4 个实例臂。
`SetReferrer` 写前 2 字段、`SetReferrals` 写人数与明细、`SetPromoCode` 写邀请码;
`SetReferralEventData` 一次写后 9 个字段(面板标题 / 弹窗图与背景 / 两个分类 id / 任务数 / 两个隐藏标志 / 公告时间)。
- `tipsState` → `dst` / `Hw.vth`(`TipsStateReducer.kt`):1 字段,`UpdateTips` 整体替换提示列表,缺省空表。
- `rankUpDialogState` → `LO` / `Se.br3`(`RankUpDialogReducer.kt`):1 字段,2 个分支。
`ShowAvailableItems` 是**追加**(`Im`:元素或整表),`FO`→`ResetRankUpDialog` 直接 `new LO()` 清空。
- `battleResult` → `sR` / `Fi.aqp`(`BattleResultReducer.kt`):5 字段,6 个实例臂。
`SetRewards` / `SetHasBattleResult` 写前两字段;`cR`/`lR`→`SetIncreasedReward`/`SetSimpleReward` 写两个奖励数值;
`fR`→`ResetBattleRewards` 只把两数值归零;`DoInit` 重建状态只带 `initialCurrentUserXP`。
- `captchaState` → `jP` / `ar.iqt`(`CaptchaStateReducer.kt`):4 字段,7 个实例臂。
三个位置集合缺省 `emptySet()`;`SetLocations` 用 `toSet()` 收口;
`SP`→`ShowFakeCaptcha` 只置 `useFake`,其余是启用 / 已验证 / 待重置集合的增删元素。
- `modalScreen` → `nV` / `Fs.js1`(`ModalScreenReducer.kt`):2 字段,4 个分支。
`AddModal` 按 `toString()` 判重后 `current=screenId` 并入队;`RemoveModal` 出队后 `current = queue.firstOrNull()`;
`XW`→`CloseCurrentModal` 关当前窗口;`RemoveCurrentByName` 名称不匹配时 no-op。
- `droppableGold` → `Vet` / `Wp.kth`(`DroppableGoldReducer.kt`):1 字段,缺省 `showDroppableGoldAuthor=true`;
`Ket`/`Yet` 两个单例 → `ShowDroppableGoldAuthor` / `HideDroppableGoldAuthor`。
- `eventGoods` → `W7` / `Wc.esx`(`EventGoodsReducer.kt`):1 字段 `Map<Any?, Any?>`,缺省空 Map;
`InitEventGoods(itemId, goods)` 在 `goods == null` 时删键,否则写键值。
- `creatorTagState` → `qO` / `ze.er2`(`CreatorTagReducer.kt`):2 字段,4 个分支。
`creatorTags` 缺省 null;`SetCreatorTags` / `SetCreatorTag` / `SetAttachedCreatorTag` 写对应字段,
`jO`→`ClearCreatorTag` 只清 `creatorTag`。
- `rewardedVideosState` → `JQ` / `xh.zsl`(`RewardedVideosReducer.kt`):1 字段,
新增枚举 `RewardVideoDisplayState { NONE, LOADING, DISPLAY }`,缺省 `NONE`;`SetDisplayState` 整体替换。
- `passwordChangeState` → `VD` / `uu.pr6`(`PasswordChangeReducer.kt`):3 个字符串字段(hash / uid / email),
缺省全为空串;`SetPasswordChangeHash` 只写 hash,`ShowEmailAndPasswordChangeDialog` 写 uid/email 并保留 hash。
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 32 个有 reducer 的切片,
`launchParams` 与其余 **56 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 33/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **457 项全绿**,含 89 槽位完整性、32 个归约切片一次归约互不串片、未迁移字段透传,
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 42 个有 reducer 的切片,
`launchParams` 与其余 **46 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 43/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **529 项全绿**,含 89 槽位完整性、42 个归约切片一次归约互不串片、未迁移字段透传,
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
@@ -274,7 +297,8 @@
entrance 的 6 个、user 的 3 个、settings 的 2 个、accountSettings 的 11 个、
language 的 1 个、announcement 的 3 个、news 的 3 个(含两个单例)、notifications 的 2 个、
quests 的 1 个、lobbyLoader 的 2 个、purchaseDialogState 的 1 个、supportUrl 的 1 个、
notificationByCountry 的 1 个、mainQuest 的 1 个匿名类 / 单例同样按效果定名并登记。
notificationByCountry 的 1 个、mainQuest 的 1 个、rankUpDialogState 的 1 个、battleResult 的 3 个、
captchaState 的 5 个、modalScreen 的 1 个、droppableGold 的 2 个、creatorTagState 的 1 个匿名类 / 单例同样按效果定名并登记。
### S4 引擎核心
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
+115 -27
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-10-24T06:01:37.203Z",
"generatedAt": "2026-10-24T06:30:01.615Z",
"module": "module_10032.js",
"RootState": {
"Class": "z6",
@@ -39,7 +39,17 @@
"questCategories",
"mainQuest",
"securitySettings",
"referralEvent"
"referralEvent",
"tipsState",
"rankUpDialogState",
"battleResult",
"captchaState",
"modalScreen",
"droppableGold",
"eventGoods",
"creatorTagState",
"rewardedVideosState",
"passwordChangeState"
],
"AnonymousActions": [
{
@@ -708,6 +718,84 @@
"Name": "ResetQuestsLoaded",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isLoaded=false(`n.qsj(!1)`;动作类 rsj_1 在 reducer 里未被读取)"
},
{
"Local": "FO",
"Name": "ResetRankUpDialog",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "availableItems=emptyList()(`new LO()`)"
},
{
"Local": "cR",
"Name": "SetIncreasedReward",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "increasedReward=动作数值(`n.qqn(cd, cd, cd, t.cqp_1)`)"
},
{
"Local": "lR",
"Name": "SetSimpleReward",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "simpleReward=动作数值(`n.qqn(cd, cd, cd, cd, t.bqp_1)`)"
},
{
"Local": "fR",
"Name": "ResetBattleRewards",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "increasedReward=0、simpleReward=0(`n.qqn(cd, cd, cd, 0, 0)`)"
},
{
"Local": "SP",
"Name": "ShowFakeCaptcha",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "useFake=true(`n.wqs(!0)`)"
},
{
"Local": "IP",
"Name": "AddVerifiedCaptchaLocation",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "verifiedLocations + location(`Xg`)"
},
{
"Local": "AP",
"Name": "RemoveVerifiedCaptchaLocation",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "verifiedLocations - location(`Jg`)"
},
{
"Local": "RP",
"Name": "AddCaptchaLocationToReset",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "locationsToReset + location(`Xg`)"
},
{
"Local": "PP",
"Name": "RemoveCaptchaLocationToReset",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "locationsToReset - location(`Jg`)"
},
{
"Local": "XW",
"Name": "CloseCurrentModal",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "current 非空时 queue -= current、current=queue.firstOrNull()"
},
{
"Local": "Ket",
"Name": "ShowDroppableGoldAuthor",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "showDroppableGoldAuthor=true(`n.f7j(!0)`)"
},
{
"Local": "Yet",
"Name": "HideDroppableGoldAuthor",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "showDroppableGoldAuthor=false(`n.f7j(!1)`)"
},
{
"Local": "jO",
"Name": "ClearCreatorTag",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "creatorTag=null(`n.ar2(null)`)"
}
],
"Markers": [
@@ -822,13 +910,13 @@
"Reducer": "Fs.js1",
"Delegate": "Fs.js1(t, n.apv_1)",
"Actions": [
"?XW",
"AddModal",
"CloseCurrentModal",
"RemoveCurrentByName",
"RemoveModal"
],
"Default": "new nV()",
"Migrated": false
"Migrated": true
},
{
"Field": "battleStatistics",
@@ -1093,16 +1181,16 @@
"Reducer": "ar.iqt",
"Delegate": "ar.iqt(t, n.kpv_1)",
"Actions": [
"?AP",
"?IP",
"?PP",
"?RP",
"?SP",
"AddCaptchaLocationToReset",
"AddVerifiedCaptchaLocation",
"LocationsUpdated",
"SetLocations"
"RemoveCaptchaLocationToReset",
"RemoveVerifiedCaptchaLocation",
"SetLocations",
"ShowFakeCaptcha"
],
"Default": "new jP()",
"Migrated": false
"Migrated": true
},
{
"Field": "passwordChangeState",
@@ -1115,7 +1203,7 @@
"ShowEmailAndPasswordChangeDialog"
],
"Default": "new VD()",
"Migrated": false
"Migrated": true
},
{
"Field": "system",
@@ -1283,7 +1371,7 @@
"UpdateTips"
],
"Default": "new dst()",
"Migrated": false
"Migrated": true
},
{
"Field": "news",
@@ -1556,15 +1644,15 @@
"Reducer": "Fi.aqp",
"Delegate": "Fi.aqp(t, n.gpw_1)",
"Actions": [
"?cR",
"?fR",
"?lR",
"DoInit",
"ResetBattleRewards",
"SetHasBattleResult",
"SetRewards"
"SetIncreasedReward",
"SetRewards",
"SetSimpleReward"
],
"Default": "new sR()",
"Migrated": false
"Migrated": true
},
{
"Field": "battleUsers",
@@ -1758,11 +1846,11 @@
"Reducer": "Se.br3",
"Delegate": "Se.br3(t, n.npw_1)",
"Actions": [
"?FO",
"ResetRankUpDialog",
"ShowAvailableItems"
],
"Default": "new LO()",
"Migrated": false
"Migrated": true
},
{
"Field": "clanUserInfo",
@@ -1833,11 +1921,11 @@
"Reducer": "Wp.kth",
"Delegate": "Wp.kth(t, n.spw_1)",
"Actions": [
"?Ket",
"?Yet"
"HideDroppableGoldAuthor",
"ShowDroppableGoldAuthor"
],
"Default": "new Vet()",
"Migrated": false
"Migrated": true
},
{
"Field": "home",
@@ -2212,7 +2300,7 @@
"InitEventGoods"
],
"Default": "new W7()",
"Migrated": false
"Migrated": true
},
{
"Field": "creatorTagState",
@@ -2221,13 +2309,13 @@
"Reducer": "ze.er2",
"Delegate": "ze.er2(t, n.spx_1)",
"Actions": [
"?jO",
"ClearCreatorTag",
"SetAttachedCreatorTag",
"SetCreatorTag",
"SetCreatorTags"
],
"Default": "new qO()",
"Migrated": false
"Migrated": true
},
{
"Field": "twoStepVerificationState",
@@ -2255,7 +2343,7 @@
"SetDisplayState"
],
"Default": "new JQ()",
"Migrated": false
"Migrated": true
},
{
"Field": "twitchState",
+102
View File
@@ -181,6 +181,25 @@ const AnonymousActionNames = new Map([
['qJ', 'MarkHomeScreenDialogShown'],
// mainQuest 切片(WZ / ih.bsk):VZ 没有 toString,按 reducer 效果定名。
['VZ', 'ResetQuestsLoaded'],
// rankUpDialogState 切片(LO / GO.br3):FO 没有 toString。
['FO', 'ResetRankUpDialog'],
// battleResult 切片(sR / pR.aqp):cR/lR/fR 没有 toString。
['cR', 'SetIncreasedReward'],
['lR', 'SetSimpleReward'],
['fR', 'ResetBattleRewards'],
// captchaState 切片(jP / MP.iqt):SP/IP/AP/RP/PP 没有 toString。
['SP', 'ShowFakeCaptcha'],
['IP', 'AddVerifiedCaptchaLocation'],
['AP', 'RemoveVerifiedCaptchaLocation'],
['RP', 'AddCaptchaLocationToReset'],
['PP', 'RemoveCaptchaLocationToReset'],
// modalScreen 切片(nV / QW.js1):XW 没有 toString。
['XW', 'CloseCurrentModal'],
// droppableGold 切片(Vet / Xet.kth):Ket/Yet 两个单例。
['Ket', 'ShowDroppableGoldAuthor'],
['Yet', 'HideDroppableGoldAuthor'],
// creatorTagState 切片(qO / SO.er2):jO 没有 toString。
['jO', 'ClearCreatorTag'],
]);
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
@@ -599,6 +618,16 @@ const Migrated = {
mainQuest: { Type: 'MainQuestStateSlice', Reduce: 'ReduceMainQuest' },
securitySettings: { Type: 'SecuritySettingsStateSlice', Reduce: 'ReduceSecuritySettings' },
referralEvent: { Type: 'ReferralEventStateSlice', Reduce: 'ReduceReferralEvent' },
tipsState: { Type: 'TipsStateSlice', Reduce: 'ReduceTips' },
rankUpDialogState: { Type: 'RankUpDialogStateSlice', Reduce: 'ReduceRankUpDialog' },
battleResult: { Type: 'BattleResultStateSlice', Reduce: 'ReduceBattleResult' },
captchaState: { Type: 'CaptchaStateSlice', Reduce: 'ReduceCaptcha' },
modalScreen: { Type: 'ModalScreenStateSlice', Reduce: 'ReduceModalScreen' },
droppableGold: { Type: 'DroppableGoldStateSlice', Reduce: 'ReduceDroppableGold' },
eventGoods: { Type: 'EventGoodsStateSlice', Reduce: 'ReduceEventGoods' },
creatorTagState: { Type: 'CreatorTagStateSlice', Reduce: 'ReduceCreatorTag' },
rewardedVideosState: { Type: 'RewardedVideosStateSlice', Reduce: 'ReduceRewardedVideos' },
passwordChangeState: { Type: 'PasswordChangeStateSlice', Reduce: 'ReducePasswordChange' },
};
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
@@ -811,6 +840,43 @@ const MainQuestAnonymousEffects = {
VZ: 'isLoaded=false(`n.qsj(!1)`;动作类 rsj_1 在 reducer 里未被读取)',
};
// rankUpDialogState 切片(LO / GO.br3):FO 没有 toString。
const RankUpDialogAnonymousEffects = {
FO: 'availableItems=emptyList()(`new LO()`)',
};
// battleResult 切片(sR / pR.aqp):cR/lR/fR 没有 toString。
const BattleResultAnonymousEffects = {
cR: 'increasedReward=动作数值(`n.qqn(cd, cd, cd, t.cqp_1)`)',
lR: 'simpleReward=动作数值(`n.qqn(cd, cd, cd, cd, t.bqp_1)`)',
fR: 'increasedReward=0、simpleReward=0(`n.qqn(cd, cd, cd, 0, 0)`)',
};
// captchaState 切片(jP / MP.iqt):SP/IP/AP/RP/PP 没有 toString。
const CaptchaAnonymousEffects = {
SP: 'useFake=true(`n.wqs(!0)`)',
IP: 'verifiedLocations + location(`Xg`)',
AP: 'verifiedLocations - location(`Jg`)',
RP: 'locationsToReset + location(`Xg`)',
PP: 'locationsToReset - location(`Jg`)',
};
// modalScreen 切片(nV / QW.js1):XW 没有 toString。
const ModalScreenAnonymousEffects = {
XW: 'current 非空时 queue -= current、current=queue.firstOrNull()',
};
// droppableGold 切片(Vet / Xet.kth):Ket/Yet 是单例。
const DroppableGoldAnonymousEffects = {
Ket: 'showDroppableGoldAuthor=true(`n.f7j(!0)`)',
Yet: 'showDroppableGoldAuthor=false(`n.f7j(!1)`)',
};
// creatorTagState 切片(qO / SO.er2):jO 没有 toString。
const CreatorTagAnonymousEffects = {
jO: 'creatorTag=null(`n.ar2(null)`)',
};
fs.writeFileSync(
path.join(P, 'names', 'root_wiring.json'),
JSON.stringify(
@@ -941,6 +1007,42 @@ fs.writeFileSync(
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(RankUpDialogAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(BattleResultAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(CaptchaAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(ModalScreenAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(DroppableGoldAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(CreatorTagAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
],
Markers: [
{
@@ -0,0 +1,55 @@
// BattleResultReducer.kt —— 战斗结算切片(对应 module_10032 的 sR 状态 + pR.aqp)
//
// 证据链:
// 状态类 sR:5 个字段,字段名取自 toString;copy 是 qqn(5 字段 + 回调)。
// 默认:hasBattleResult=false、rewards=emptyList()、initialCurrentUserXP=0、increasedReward=0、simpleReward=0。
// 根委派:`Fi.aqp(t, n.gpw_1)`。
// reducer:
// aR(SetRewards) → rewards
// hR(SetHasBattleResult)→ hasBattleResult
// cR(无 toString) → increasedReward
// lR(无 toString) → simpleReward
// fR(无 toString) → increasedReward=0、simpleReward=0
// vR(DoInit) → 全新状态,只带 initialCurrentUserXP(`new sR(cd, cd, t.zqo_1)`)
package App.State.Reducers
import App.State.ContainerGivenItem
import App.State.DoInit
import App.State.SetHasBattleResult
import App.State.SetRewards
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `cR`:写「加成后奖励」数值。 */
data class SetIncreasedReward(val value: Int)
/** 原 `lR`:写「原始奖励」数值。 */
data class SetSimpleReward(val value: Int)
/** 原 `fR`:把两个奖励数值归零。 */
object ResetBattleRewards
/* ------------------------------------------------------------------ 状态 */
data class BattleResultStateSlice(
val HasBattleResult: Boolean = false,
val Rewards: List<ContainerGivenItem> = emptyList(),
val InitialCurrentUserXP: Any? = null,
val IncreasedReward: Int = 0,
val SimpleReward: Int = 0,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceBattleResult(
State0: BattleResultStateSlice,
Action: Any,
): BattleResultStateSlice = when (Action) {
is SetRewards -> State0.copy(Rewards = Action.rewards)
is SetHasBattleResult -> State0.copy(HasBattleResult = Action.hasBattleResult)
is SetIncreasedReward -> State0.copy(IncreasedReward = Action.value)
is SetSimpleReward -> State0.copy(SimpleReward = Action.value)
is ResetBattleRewards -> State0.copy(IncreasedReward = 0, SimpleReward = 0)
is DoInit -> BattleResultStateSlice(InitialCurrentUserXP = Action.initialCurrentUserXP)
else -> State0
}
@@ -0,0 +1,67 @@
// CaptchaStateReducer.kt —— 验证码切片(对应 module_10032 的 jP 状态 + MP.iqt)
//
// 证据链:
// 状态类 jP:4 个字段,copy 是 wqs(4 字段 + 回调)。
// 默认:useFake=false,三个位置集合都是 `Yg()` = emptySet()。
// 根委派:`ar.iqt(t, n.kpv_1)`。
// reducer:
// SP(无 toString) → useFake=true
// CP(SetLocations) → enabledLocations = Sm(captchaLocations)(`Sm` = toSet)
// IP(无 toString) → verifiedLocations + location(`Xg` = 集合加元素)
// AP(无 toString) → verifiedLocations - location(`Jg` = 集合删元素)
// RP(无 toString) → locationsToReset + location
// PP(无 toString) → locationsToReset - location
// NP(LocationsUpdated)→ enabledLocations + location
package App.State.Reducers
import App.State.LocationsUpdated
import App.State.SetLocations
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `SP`:开启假验证码(`n.wqs(!0)`)。 */
object ShowFakeCaptcha
/** 原 `IP`:把地点加入已验证集合。 */
data class AddVerifiedCaptchaLocation(val location: Any?)
/** 原 `AP`:把地点移出已验证集合。 */
data class RemoveVerifiedCaptchaLocation(val location: Any?)
/** 原 `RP`:把地点加入待重置集合。 */
data class AddCaptchaLocationToReset(val location: Any?)
/** 原 `PP`:把地点移出待重置集合。 */
data class RemoveCaptchaLocationToReset(val location: Any?)
/* ------------------------------------------------------------------ 状态 */
data class CaptchaStateSlice(
val UseFake: Boolean = false,
val EnabledLocations: Set<Any?> = emptySet(),
val VerifiedLocations: Set<Any?> = emptySet(),
val LocationsToReset: Set<Any?> = emptySet(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceCaptcha(
State0: CaptchaStateSlice,
Action: Any,
): CaptchaStateSlice = when (Action) {
is ShowFakeCaptcha -> State0.copy(UseFake = true)
is SetLocations -> State0.copy(
EnabledLocations = (Action.captchaLocations as List<Any?>).toSet(),
)
is AddVerifiedCaptchaLocation ->
State0.copy(VerifiedLocations = State0.VerifiedLocations + Action.location)
is RemoveVerifiedCaptchaLocation ->
State0.copy(VerifiedLocations = State0.VerifiedLocations - Action.location)
is AddCaptchaLocationToReset ->
State0.copy(LocationsToReset = State0.LocationsToReset + Action.location)
is RemoveCaptchaLocationToReset ->
State0.copy(LocationsToReset = State0.LocationsToReset - Action.location)
is LocationsUpdated ->
State0.copy(EnabledLocations = State0.EnabledLocations + Action.location)
else -> State0
}
@@ -0,0 +1,42 @@
// CreatorTagReducer.kt —— 创作者标签切片(对应 module_10032 的 qO 状态 + SO.er2)
//
// 证据链:
// 状态类 qO:2 个字段,toString 名 "CreatorTagState(creatorTag=…, creatorTags=…)";copy 是 ar2(2 字段)。
// 默认:creatorTag=null、creatorTags=null。
// 根委派:`ze.er2(t, n.spx_1)`。
// reducer:
// yO(SetCreatorTags) → creatorTags
// $O(SetCreatorTag) → creatorTag
// jO(无 toString) → creatorTag=null(`n.ar2(null)`)
// xO(SetAttachedCreatorTag) → creatorTag
package App.State.Reducers
import App.State.SetAttachedCreatorTag
import App.State.SetCreatorTag
import App.State.SetCreatorTags
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `jO`:清空已挂载的创作者标签。 */
object ClearCreatorTag
/* ------------------------------------------------------------------ 状态 */
data class CreatorTagStateSlice(
val CreatorTag: Any? = null,
val CreatorTags: List<Any?>? = null,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceCreatorTag(
State0: CreatorTagStateSlice,
Action: Any,
): CreatorTagStateSlice = when (Action) {
is SetCreatorTags ->
State0.copy(CreatorTags = Action.creatorTags as List<Any?>?)
is SetCreatorTag -> State0.copy(CreatorTag = Action.creatorTag)
is SetAttachedCreatorTag -> State0.copy(CreatorTag = Action.creatorTag)
is ClearCreatorTag -> State0.copy(CreatorTag = null)
else -> State0
}
@@ -0,0 +1,33 @@
// DroppableGoldReducer.kt —— 掉落金币署名开关切片(对应 module_10032 的 Vet 状态 + Xet.kth)
//
// 证据链:
// 状态类 Vet:1 个字段,copy 是 f7j(1 字段)。
// 默认:`showDroppableGoldAuthor=true`。
// 根委派:`Wp.kth(t, n.spw_1)`。
// reducer:`Ket ? n.f7j(!0) : Yet ? n.f7j(!1) : n`。
package App.State.Reducers
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `Ket` 单例:显示掉落金币的作者署名。 */
object ShowDroppableGoldAuthor
/** 原 `Yet` 单例:隐藏掉落金币的作者署名。 */
object HideDroppableGoldAuthor
/* ------------------------------------------------------------------ 状态 */
data class DroppableGoldStateSlice(
val ShowDroppableGoldAuthor: Boolean = true,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceDroppableGold(
State0: DroppableGoldStateSlice,
Action: Any,
): DroppableGoldStateSlice = when (Action) {
is ShowDroppableGoldAuthor -> State0.copy(ShowDroppableGoldAuthor = true)
is HideDroppableGoldAuthor -> State0.copy(ShowDroppableGoldAuthor = false)
else -> State0
}
@@ -0,0 +1,31 @@
// EventGoodsReducer.kt —— 活动商品表切片(对应 module_10032 的 W7 状态 + K7.esx)
//
// 证据链:
// 状态类 W7:1 个字段,toString 名 "EventGoodsState(eventGoods=…)";copy 是 bsx(1 字段)。
// 默认:eventGoods = ag() = emptyMap()。
// 根委派:`Wc.esx(t, n.rpx_1)`。
// reducer:`t instanceof V7 ? (goods 为空 ? map 删键 : map 写键值) : n`
// V7 = InitEventGoods(itemId, goods);`am` = map 删键、`cg` = map 写键值。
package App.State.Reducers
import App.State.InitEventGoods
/* ------------------------------------------------------------------ 状态 */
data class EventGoodsStateSlice(
val EventGoods: Map<Any?, Any?> = emptyMap(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceEventGoods(
State0: EventGoodsStateSlice,
Action: Any,
): EventGoodsStateSlice = when (Action) {
is InitEventGoods -> State0.copy(
EventGoods =
if (Action.goods == null) State0.EventGoods - Action.itemId
else State0.EventGoods + (Action.itemId to Action.goods),
)
else -> State0
}
@@ -0,0 +1,67 @@
// ModalScreenReducer.kt —— 模态窗口队列切片(对应 module_10032 的 nV 状态 + QW.js1)
//
// 证据链:
// 状态类 nV:2 个字段,toString 名 "ModalScreen(current=…, modalQueue=…)";copy 是 ks1(2 字段)。
// 默认:current=null、modalQueue=emptyList()。
// 根委派:`Fs.js1(t, n.apv_1)`。
// reducer:
// YW(AddModal) → 队列里已有同名窗口时 no-op;否则 current=screenId、queue += screenId。
// 原判重是 `Xd(e, iV) && n.ls1(e)`:`ls1` 用 `toString()` 比较队列元素。
// `iV` 是模态屏幕标识的标记接口,Kotlin 侧按「所有 AddModal 都参与判重」处理。
// JW(RemoveModal)→ queue -= screenId、current = queue.firstOrNull()(`mk` = firstOrNull)
// XW(无 toString) → current 为空时 no-op;否则 queue -= current、current = queue.firstOrNull()
// ZW(RemoveCurrentByName)→ current 为空或 toString 不等于 name 时 no-op,否则同上
package App.State.Reducers
import App.State.AddModal
import App.State.RemoveCurrentByName
import App.State.RemoveModal
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `XW` 单例:关掉当前窗口,并把 current 交给队首。 */
object CloseCurrentModal
/* ------------------------------------------------------------------ 状态 */
data class ModalScreenStateSlice(
val Current: Any? = null,
val ModalQueue: List<Any?> = emptyList(),
)
/* ------------------------------------------------------------------ reducer */
private fun FirstOrNull(Items: List<Any?>): Any? = Items.firstOrNull()
fun ReduceModalScreen(
State0: ModalScreenStateSlice,
Action: Any,
): ModalScreenStateSlice = when (Action) {
is AddModal -> {
val ScreenId: Any? = Action.screenId
val Name = ScreenId.toString()
if (State0.ModalQueue.any { it.toString() == Name }) State0
else State0.copy(Current = ScreenId, ModalQueue = State0.ModalQueue + ScreenId)
}
is RemoveModal -> {
val Queue = State0.ModalQueue - Action.screenId
State0.copy(Current = FirstOrNull(Queue), ModalQueue = Queue)
}
is CloseCurrentModal -> {
val CurrentModal = State0.Current
if (CurrentModal == null) State0
else {
val Queue = State0.ModalQueue - CurrentModal
State0.copy(Current = FirstOrNull(Queue), ModalQueue = Queue)
}
}
is RemoveCurrentByName -> {
val CurrentModal = State0.Current
if (CurrentModal == null || CurrentModal.toString() != Action.name.toString()) State0
else {
val Queue = State0.ModalQueue - CurrentModal
State0.copy(Current = FirstOrNull(Queue), ModalQueue = Queue)
}
}
else -> State0
}
@@ -0,0 +1,35 @@
// PasswordChangeReducer.kt —— 改密流程切片(对应 module_10032 的 VD 状态 + WD.pr6)
//
// 证据链:
// 状态类 VD:3 个字段,toString 名 "PasswordChangeState(hash=…, uid=…, email=…)";copy 是 tr6(3 字段)。
// 默认:hash=""、uid=""、email=""。
// 根委派:`uu.pr6(t, n.lpv_1)`。
// reducer:
// UD(SetPasswordChangeHash) → hash(`n.tr6(t.fr6_1)`)
// HD(ShowEmailAndPasswordChangeDialog)→ `n.tr6(cd, e, r)`,其中 r=email、e=uid,即保持 hash
package App.State.Reducers
import App.State.SetPasswordChangeHash
import App.State.ShowEmailAndPasswordChangeDialog
/* ------------------------------------------------------------------ 状态 */
data class PasswordChangeStateSlice(
val Hash: String = "",
val Uid: String = "",
val Email: String = "",
)
/* ------------------------------------------------------------------ reducer */
fun ReducePasswordChange(
State0: PasswordChangeStateSlice,
Action: Any,
): PasswordChangeStateSlice = when (Action) {
is SetPasswordChangeHash -> State0.copy(Hash = Action.hash.toString())
is ShowEmailAndPasswordChangeDialog -> State0.copy(
Uid = Action.uid.toString(),
Email = Action.email.toString(),
)
else -> State0
}
@@ -0,0 +1,39 @@
// RankUpDialogReducer.kt —— 升级奖励弹窗切片(对应 module_10032 的 LO 状态 + GO.br3)
//
// 证据链:
// 状态类 LO:1 个字段,toString 名 "RankUpDialogState(availableItems=…)";copy 是 wr2(1 字段)。
// 默认:availableItems = md() = emptyList()。
// 根委派:`Se.br3(t, n.npw_1)`。
// reducer:
// UO(ShowAvailableItems)→ `n.wr2(Im(n.vr2_1, t.ar3_1))`,即在现有列表后追加(元素或整表)
// FO(无 toString) → `new LO()`,复位为空表
package App.State.Reducers
import App.State.ShowAvailableItems
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `FO`:清空升级奖励弹窗列表(`new LO()`)。 */
object ResetRankUpDialog
/* ------------------------------------------------------------------ 状态 */
data class RankUpDialogStateSlice(
val AvailableItems: List<Any?> = emptyList(),
)
/* ------------------------------------------------------------------ reducer */
/** 原 `Im`:列表追加;`Items` 本身是集合时做拼接,否则当作单个元素。 */
private fun PlusItems(Base: List<Any?>, Items: Any?): List<Any?> =
if (Items is List<*>) Base + Items else Base + listOf(Items)
fun ReduceRankUpDialog(
State0: RankUpDialogStateSlice,
Action: Any,
): RankUpDialogStateSlice = when (Action) {
is ShowAvailableItems ->
State0.copy(AvailableItems = PlusItems(State0.AvailableItems, Action.items))
is ResetRankUpDialog -> RankUpDialogStateSlice()
else -> State0
}
@@ -0,0 +1,33 @@
// RewardedVideosReducer.kt —— 激励视频切片(对应 module_10032 的 JQ 状态 + n1.zsl)
//
// 证据链:
// 状态类 JQ:1 个字段,toString 名 "RewardedVideosState(displayState=…)";copy 是 vsl(1 字段)。
// 默认:`XQ()` → `FQ()` 里的 `dh`,即枚举 NONE。
// reducer:`t instanceof t1 ? n.vsl(t.ysl_1) : n`,`t1` 是 SetDisplayState(displayState)。
// 枚举 `VQ`:NONE / LOADING / DISPLAY。
package App.State.Reducers
import App.State.SetDisplayState
/* ------------------------------------------------------------------ 状态 */
enum class RewardVideoDisplayState {
NONE,
LOADING,
DISPLAY,
}
data class RewardedVideosStateSlice(
val DisplayState: RewardVideoDisplayState = RewardVideoDisplayState.NONE,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceRewardedVideos(
State0: RewardedVideosStateSlice,
Action: Any,
): RewardedVideosStateSlice = when (Action) {
is SetDisplayState ->
State0.copy(DisplayState = Action.displayState as RewardVideoDisplayState)
else -> State0
}
@@ -0,0 +1,26 @@
// TipsStateReducer.kt —— 大厅提示条切片(对应 module_10032 的 dst 状态 + bst.vth)
//
// 证据链:
// 状态类 dst:1 个字段,toString 名 "TipsState(tips=…)";copy 是 su8(1 字段)。
// 默认:tips = md() = emptyList()。
// 根委派:`Hw.vth(t, n.spv_1)`。
// reducer:`t instanceof mst ? n.su8(t.yu8_1) : n`,`mst` 是 UpdateTips(tips),整体替换。
package App.State.Reducers
import App.State.UpdateTips
/* ------------------------------------------------------------------ 状态 */
data class TipsStateSlice(
val Tips: List<Any?> = emptyList(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceTips(
State0: TipsStateSlice,
Action: Any,
): TipsStateSlice = when (Action) {
is UpdateTips -> State0.copy(Tips = Action.tips as List<Any?>)
else -> State0
}
@@ -1,7 +1,7 @@
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
//
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
// 这里采用同一形状。已迁移的 33 个切片里,32 个真正调用 reducer,launchParams 只做类型收口:
// 这里采用同一形状。已迁移的 43 个切片里,42 个真正调用 reducer,launchParams 只做类型收口:
//
// inviteState → ReduceInvite(aD / pD.er5)
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
@@ -35,16 +35,31 @@
// mainQuest → ReduceMainQuest(WZ / ih.bsk,根委派 `(null == ih && new nQ(), ih).bsk(t, n.fpx_1)`)
// securitySettings → ReduceSecuritySettings(V2 / pc.qsv,根委派 `pc.qsv(t, n.apx_1)`)
// referralEvent → ReduceReferralEvent(pQ / lh.osk,根委派 `lh.osk(t, n.hpy_1)`)
// tipsState → ReduceTips(dst / Hw.vth,根委派 `Hw.vth(t, n.spv_1)`)
// rankUpDialogState → ReduceRankUpDialog(LO / Se.br3,根委派 `Se.br3(t, n.npw_1)`)
// battleResult → ReduceBattleResult(sR / Fi.aqp,根委派 `Fi.aqp(t, n.gpw_1)`)
// captchaState → ReduceCaptcha(jP / ar.iqt,根委派 `ar.iqt(t, n.kpv_1)`)
// modalScreen → ReduceModalScreen(nV / Fs.js1,根委派 `Fs.js1(t, n.apv_1)`)
// droppableGold → ReduceDroppableGold(Vet / Wp.kth,根委派 `Wp.kth(t, n.spw_1)`)
// eventGoods → ReduceEventGoods(W7 / Wc.esx,根委派 `Wc.esx(t, n.rpx_1)`)
// creatorTagState → ReduceCreatorTag(qO / ze.er2,根委派 `ze.er2(t, n.spx_1)`)
// rewardedVideosState → ReduceRewardedVideos(JQ / xh.zsl,根委派 `xh.zsl(t, n.upx_1)`)
// passwordChangeState → ReducePasswordChange(VD / uu.pr6,根委派 `uu.pr6(t, n.lpv_1)`)
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
//
// 其余 56 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 46 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
import App.State.Reducers.ReduceAccountSettings
import App.State.Reducers.ReduceAnnouncement
import App.State.Reducers.ReduceBattleResult
import App.State.Reducers.ReduceCaptcha
import App.State.Reducers.ReduceCreatorTag
import App.State.Reducers.ReduceDroppableGold
import App.State.Reducers.ReduceEntrance
import App.State.Reducers.ReduceEventGoods
import App.State.Reducers.ReduceGarage
import App.State.Reducers.ReduceGaragePreview
import App.State.Reducers.ReduceHome
@@ -57,21 +72,26 @@ import App.State.Reducers.ReduceLootBoxes
import App.State.Reducers.ReduceMainQuest
import App.State.Reducers.ReduceAvailabilityByRank
import App.State.Reducers.ReduceLobbyLoader
import App.State.Reducers.ReduceModalScreen
import App.State.Reducers.ReduceNews
import App.State.Reducers.ReduceNotificationByCountry
import App.State.Reducers.ReduceNotifications
import App.State.Reducers.ReducePasswordChange
import App.State.Reducers.ReduceProBattle
import App.State.Reducers.ReducePromoCode
import App.State.Reducers.ReducePurchaseDialog
import App.State.Reducers.ReduceQuestCategories
import App.State.Reducers.ReduceQuests
import App.State.Reducers.ReduceRankUpDialog
import App.State.Reducers.ReduceReferralEvent
import App.State.Reducers.ReduceRewardedVideos
import App.State.Reducers.ReduceRulesUpdated
import App.State.Reducers.ReduceScreen
import App.State.Reducers.ReduceSecuritySettings
import App.State.Reducers.ReduceSupportUrl
import App.State.Reducers.ReduceSettings
import App.State.Reducers.ReduceShop
import App.State.Reducers.ReduceTips
import App.State.Reducers.ReduceTwitch
import App.State.Reducers.ReduceUser
@@ -120,6 +140,16 @@ object RootReducer {
MainQuest = ReduceMainQuest(State0.MainQuest, Action),
SecuritySettings = ReduceSecuritySettings(State0.SecuritySettings, Action),
ReferralEvent = ReduceReferralEvent(State0.ReferralEvent, Action),
TipsState = ReduceTips(State0.TipsState, Action),
RankUpDialogState = ReduceRankUpDialog(State0.RankUpDialogState, Action),
BattleResult = ReduceBattleResult(State0.BattleResult, Action),
CaptchaState = ReduceCaptcha(State0.CaptchaState, Action),
ModalScreen = ReduceModalScreen(State0.ModalScreen, Action),
DroppableGold = ReduceDroppableGold(State0.DroppableGold, Action),
EventGoods = ReduceEventGoods(State0.EventGoods, Action),
CreatorTagState = ReduceCreatorTag(State0.CreatorTagState, Action),
RewardedVideosState = ReduceRewardedVideos(State0.RewardedVideosState, Action),
PasswordChangeState = ReducePasswordChange(State0.PasswordChangeState, Action),
)
}
@@ -7,7 +7,12 @@ package App.State
import App.State.Reducers.AccountSettingsStateSlice
import App.State.Reducers.AnnouncementStateSlice
import App.State.Reducers.AvailabilityByRankStateSlice
import App.State.Reducers.BattleResultStateSlice
import App.State.Reducers.CaptchaStateSlice
import App.State.Reducers.CreatorTagStateSlice
import App.State.Reducers.DroppableGoldStateSlice
import App.State.Reducers.EntranceStateSlice
import App.State.Reducers.EventGoodsStateSlice
import App.State.Reducers.GarageStateSlice
import App.State.Reducers.GaragePreviewStateSlice
import App.State.Reducers.HomeStateSlice
@@ -18,21 +23,26 @@ import App.State.Reducers.LocalBattleUserStateSlice
import App.State.Reducers.LoginByPasswordStateSlice
import App.State.Reducers.LootBoxesStateSlice
import App.State.Reducers.MainQuestStateSlice
import App.State.Reducers.ModalScreenStateSlice
import App.State.Reducers.NewsStateSlice
import App.State.Reducers.NotificationByCountryStateSlice
import App.State.Reducers.NotificationsStateSlice
import App.State.Reducers.PasswordChangeStateSlice
import App.State.Reducers.ProBattleStateSlice
import App.State.Reducers.PromoCodeStateSlice
import App.State.Reducers.PurchaseDialogStateSlice
import App.State.Reducers.QuestCategoriesStateSlice
import App.State.Reducers.QuestsStateSlice
import App.State.Reducers.RankUpDialogStateSlice
import App.State.Reducers.ReferralEventStateSlice
import App.State.Reducers.RewardedVideosStateSlice
import App.State.Reducers.RulesUpdatedStateSlice
import App.State.Reducers.ScreenStateSlice
import App.State.Reducers.SecuritySettingsStateSlice
import App.State.Reducers.SettingsStateSlice
import App.State.Reducers.ShopStateSlice
import App.State.Reducers.SupportUrlStateSlice
import App.State.Reducers.TipsStateSlice
import App.State.Reducers.TwitchStateSlice
import App.State.Reducers.UserStateSlice
@@ -45,8 +55,8 @@ data class RootState(
val User: UserStateSlice = UserStateSlice(),
/** 原 screen(WK / dY().ks5)。已迁移。 */
val Screen: ScreenStateSlice = ScreenStateSlice(),
/** 原 modalScreen(nV / Fs.js1)。未迁移:原样透传。 */
val ModalScreen: Any? = null,
/** 原 modalScreen(nV / Fs.js1)。已迁移。 */
val ModalScreen: ModalScreenStateSlice = ModalScreenStateSlice(),
/** 原 battleStatistics(jR / rr.fqr)。未迁移:原样透传。 */
val BattleStatistics: Any? = null,
/** 原 battleTutorial(rE / Q.sq8)。未迁移:原样透传。 */
@@ -65,10 +75,10 @@ data class RootState(
val MigrationState: Any? = null,
/** 原 inviteState(aD / Ke.er5)。已迁移。 */
val InviteState: InviteStateSlice = InviteStateSlice(),
/** 原 captchaState(jP / ar.iqt)。未迁移:原样透传。 */
val CaptchaState: Any? = null,
/** 原 passwordChangeState(VD / uu.pr6)。未迁移:原样透传。 */
val PasswordChangeState: Any? = null,
/** 原 captchaState(jP / ar.iqt)。已迁移。 */
val CaptchaState: CaptchaStateSlice = CaptchaStateSlice(),
/** 原 passwordChangeState(VD / uu.pr6)。已迁移。 */
val PasswordChangeState: PasswordChangeStateSlice = PasswordChangeStateSlice(),
/** 原 system(_9 / w9().xt9)。未迁移:原样透传。 */
val System: Any? = null,
/** 原 quests(Frt / Yrt.otg)。已迁移。 */
@@ -81,8 +91,8 @@ data class RootState(
val Clan: Any? = null,
/** 原 clansStorage(lO / me.vr1)。未迁移:原样透传。 */
val ClansStorage: Any? = null,
/** 原 tipsState(dst / Hw.vth)。未迁移:原样透传。 */
val TipsState: Any? = null,
/** 原 tipsState(dst / Hw.vth)。已迁移。 */
val TipsState: TipsStateSlice = TipsStateSlice(),
/** 原 news(ZX / E_.asd)。已迁移。 */
val News: NewsStateSlice = NewsStateSlice(),
/** 原 settings(Uut / xw.ltg)。已迁移。 */
@@ -109,8 +119,8 @@ data class RootState(
val BattleSearchState: Any? = null,
/** 原 lobbyLoader(KW / Bs.es1)。已迁移。 */
val LobbyLoader: LobbyLoaderStateSlice = LobbyLoaderStateSlice(),
/** 原 battleResult(sR / Fi.aqp)。未迁移:原样透传。 */
val BattleResult: Any? = null,
/** 原 battleResult(sR / Fi.aqp)。已迁移。 */
val BattleResult: BattleResultStateSlice = BattleResultStateSlice(),
/** 原 battleUsers(uP / sr.hqs)。未迁移:原样透传。 */
val BattleUsers: Any? = null,
/** 原 suppliesState(wE / $t.fq9)。未迁移:原样透传。 */
@@ -123,8 +133,8 @@ data class RootState(
val BattlePauseState: Any? = null,
/** 原 battleViewState(DT / sI.jqd)。未迁移:原样透传。 */
val BattleViewState: Any? = null,
/** 原 rankUpDialogState(LO / Se.br3)。未迁移:原样透传。 */
val RankUpDialogState: Any? = null,
/** 原 rankUpDialogState(LO / Se.br3)。已迁移。 */
val RankUpDialogState: RankUpDialogStateSlice = RankUpDialogStateSlice(),
/** 原 clanUserInfo(hO / de.gr1)。未迁移:原样透传。 */
val ClanUserInfo: Any? = null,
/** 原 tankActionsLogState(vI / wi.gqe)。未迁移:原样透传。 */
@@ -133,8 +143,8 @@ data class RootState(
val BattleMessage: Any? = null,
/** 原 battleChat(Q6 / Hf.wtg)。未迁移:原样透传。 */
val BattleChat: Any? = null,
/** 原 droppableGold(Vet / Wp.kth)。未迁移:原样透传。 */
val DroppableGold: Any? = null,
/** 原 droppableGold(Vet / Wp.kth)。已迁移。 */
val DroppableGold: DroppableGoldStateSlice = DroppableGoldStateSlice(),
/** 原 home(s1 / (inline@home))。已迁移。 */
val Home: HomeStateSlice = HomeStateSlice(),
/** 原 localBattleUserState(lE / (inline@localBattleUserState))。已迁移。 */
@@ -183,14 +193,14 @@ data class RootState(
val NotificationByCountry: NotificationByCountryStateSlice = NotificationByCountryStateSlice(),
/** 原 userRenameState(O8 / Rf.pte)。未迁移:原样透传。 */
val UserRenameState: Any? = null,
/** 原 eventGoods(W7 / Wc.esx)。未迁移:原样透传。 */
val EventGoods: Any? = null,
/** 原 creatorTagState(qO / ze.er2)。未迁移:原样透传。 */
val CreatorTagState: Any? = null,
/** 原 eventGoods(W7 / Wc.esx)。已迁移。 */
val EventGoods: EventGoodsStateSlice = EventGoodsStateSlice(),
/** 原 creatorTagState(qO / ze.er2)。已迁移。 */
val CreatorTagState: CreatorTagStateSlice = CreatorTagStateSlice(),
/** 原 twoStepVerificationState(x9 / _f.atb)。未迁移:原样透传。 */
val TwoStepVerificationState: Any? = null,
/** 原 rewardedVideosState(JQ / xh.zsl)。未迁移:原样透传。 */
val RewardedVideosState: Any? = null,
/** 原 rewardedVideosState(JQ / xh.zsl)。已迁移。 */
val RewardedVideosState: RewardedVideosStateSlice = RewardedVideosStateSlice(),
/** 原 twitchState(g9 / uf.gta)。已迁移。 */
val TwitchState: TwitchStateSlice = TwitchStateSlice(),
/** 原 icons(oH / vs.kru)。已迁移。 */
@@ -16,7 +16,7 @@ object RootWiring {
val All: List<SliceWiring> = listOf(
SliceWiring("user", "ypu_1", "t8", "M8().qtd", "M8().qtd(t, n.ypu_1)", listOf("AddCoins", "AddRubies", "ClearInDebt", "DisableLobbyTutorial", "SetBatteryLevel", "SetClanInfo", "SetCoins", "SetDisabledWithoutPremiumLocations", "SetHasTemporaryCash", "SetInDebt", "SetPremiumExpirationTime", "SetRankNames", "SetRubies", "SetUserId", "SetUserPropertiesAction", "SpendCoins", "SpendCrystals", "SpendRubies", "UpdateClanTag", "UpdateCrystals", "UpdateEventPass", "UpdateGearScore", "UpdatePremium", "UpdateRank", "UpdateScore", "UpdateUid", "UpdateUserRating")),
SliceWiring("screen", "zpu_1", "WK", "dY().ks5", "dY().ks5(t, n.zpu_1)", listOf("ClearScreenStack", "DelayNextGoTo", "DisableBack", "DisableForeground", "DisplayMainEntranceScreen", "DisplayReauthorizationMessage", "EnableBack", "EnableForeground", "FilterScreenStack", "GoTo", "PopScreen", "PressedAnyButton", "ResetScreenStackOnNextGoTo")),
SliceWiring("modalScreen", "apv_1", "nV", "Fs.js1", "Fs.js1(t, n.apv_1)", listOf("?XW", "AddModal", "RemoveCurrentByName", "RemoveModal")),
SliceWiring("modalScreen", "apv_1", "nV", "Fs.js1", "Fs.js1(t, n.apv_1)", listOf("AddModal", "CloseCurrentModal", "RemoveCurrentByName", "RemoveModal")),
SliceWiring("battleStatistics", "bpv_1", "jR", "rr.fqr", "rr.fqr(t, n.bpv_1)", listOf("?DR", "?HR", "?KR", "?PR", "?QR", "?UR", "?XR", "?YR", "?ZR", "?nP", "?tP", "Init", "InitBattleModeWithLevels", "SetBattleMode", "SetDisplayClientInfo", "SetFlagOrBallOwner", "SetRankRange", "SetReArmor", "SetRemainingTime", "SetRoundRestartTime", "UpdateFund", "UpdateScore")),
SliceWiring("battleTutorial", "cpv_1", "rE", "Q.sq8", "Q.sq8(t, n.cpv_1)", listOf("?oE", "?sE", "SetFragsCount", "ShowMessage")),
SliceWiring("garage", "dpv_1", "UB", "hF", "hF(t, n)", listOf("AddGarageItemCount", "AddNewItem", "AddOverdriveDescription", "AddRelatedBazooka", "AddRelatedResistanceModule", "AddToFavoriteItem", "BuyProtectionSlot", "ClearWaitServerItemMount", "GarageAddItemToDepot", "GarageApplyResistanceMount", "GarageBuyDevice", "GarageDeviceTrySetInfinityLifetime", "GarageDevicesLoadedAction", "GarageLoaded", "GarageMountItem", "GarageMountItems", "GarageRemoveDevice", "GarageRemoveDiscountForDevice", "GarageRemoveItemFromDepot", "GarageRemoveItemFromStore", "GarageResistanceUnMount", "GarageSelectAlteration", "GarageSelectItem", "GarageSelectShotSkin", "GarageSelectSkin", "GarageUnmountItem", "GarageUpdateItemCount", "GarageUpgradeItemAction", "GarageUpgradeItemConfirm", "GarageUpgradeItemCoolDownFinished", "GarageUpgradeItemCoolDownStarted", "GoTo", "HideVideoInDescription", "InitState", "RemoveFromFavoriteItem", "RemoveFromSale", "RemoveNewItem", "RemoveWaitServerItemMount", "ResetGarage", "ResetMountDelay", "SelectGarageCategory", "SetCooldownReduceEndTime", "SetGarageNotLoaded", "SetGarageObject", "SetGarageSkinsAction", "SetItemRarity", "SetItemsOnDepot", "SetItemsOnMarket", "SetMountedItems", "SetMountedShotSkinForItems", "SetMountedSkinForItems", "SetNewItems", "SetResistanceReadyToMount", "SetRestrictionMount", "ShowGarageDiscountNotification", "StartCooldownReduce", "SwitchingRowsScroll", "UpdateDiscount")),
@@ -26,7 +26,7 @@ object RootWiring {
SliceWiring("register", "hpv_1", "Cit", "cp.stg", "cp.stg(t, n.hpv_1)", listOf("?Bit", "?Dit", "?Fit", "?Git", "?Hit", "?Lit", "?Mit", "?Uit", "?_rt", "?hrt", "EnteredUidIsBusy", "ForceSetUid", "ReceiveOAuthUri", "SetOpenedPassword", "SetPassword", "SetPasswordRestrictions", "SetUid", "SocialNetworkAvailability", "SocialNetworkClientIds")),
SliceWiring("migrationState", "ipv_1", "\$it", "Uv.rtg", "Uv.rtg(t, n.ipv_1)", listOf("?Bit", "?Dit", "?Fit", "?Git", "?Hit", "?Lit", "?_rt", "EnteredUidIsBusy", "ForceSetUid", "SetPassword", "SetUid")),
SliceWiring("inviteState", "jpv_1", "aD", "Ke.er5", "Ke.er5(t, n.jpv_1)", listOf("ApplyInvite", "SetInviteNeed", "SetInviteStatus")),
SliceWiring("captchaState", "kpv_1", "jP", "ar.iqt", "ar.iqt(t, n.kpv_1)", listOf("?AP", "?IP", "?PP", "?RP", "?SP", "LocationsUpdated", "SetLocations")),
SliceWiring("captchaState", "kpv_1", "jP", "ar.iqt", "ar.iqt(t, n.kpv_1)", listOf("AddCaptchaLocationToReset", "AddVerifiedCaptchaLocation", "LocationsUpdated", "RemoveCaptchaLocationToReset", "RemoveVerifiedCaptchaLocation", "SetLocations", "ShowFakeCaptcha")),
SliceWiring("passwordChangeState", "lpv_1", "VD", "uu.pr6", "uu.pr6(t, n.lpv_1)", listOf("SetPasswordChangeHash", "ShowEmailAndPasswordChangeDialog")),
SliceWiring("system", "mpv_1", "_9", "w9().xt9", "w9().xt9(t, n.mpv_1)", listOf("DoShowMessage", "SetFatalError")),
SliceWiring("quests", "npv_1", "Frt", "Yrt.otg", "(null == zp && new Yrt(), zp).otg(t, n.npv_1)", listOf("GoTo", "SetCurrentCategory", "SetNotCompletedQuests")),
@@ -48,19 +48,19 @@ object RootWiring {
SliceWiring("battleCreateState", "dpw_1", "\$6", "Df.ttg", "Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)", listOf("?A6", "?B6", "?C6", "?D6", "?E6", "?F6", "?I6", "?L6", "?M6", "?N6", "?P6", "?R6", "?T6", "?U6", "Configure", "CreateBattle", "SetRestrictions")),
SliceWiring("battleSearchState", "epw_1", "Itt", "Htt().vtg", "Htt().vtg(t, n.epw_1)", listOf("?Dtt", "EnterToBattle", "Fight", "RemoveBattle", "SelectBattle", "SelectBattleFormat", "SetFilter", "SetSearch", "SetSuspiciousFilter")),
SliceWiring("lobbyLoader", "fpw_1", "KW", "Bs.es1", "Bs.es1(t, n.fpw_1)", listOf("HideLoader", "ShowLoader")),
SliceWiring("battleResult", "gpw_1", "sR", "Fi.aqp", "Fi.aqp(t, n.gpw_1)", listOf("?cR", "?fR", "?lR", "DoInit", "SetHasBattleResult", "SetRewards")),
SliceWiring("battleResult", "gpw_1", "sR", "Fi.aqp", "Fi.aqp(t, n.gpw_1)", listOf("DoInit", "ResetBattleRewards", "SetHasBattleResult", "SetIncreasedReward", "SetRewards", "SetSimpleReward")),
SliceWiring("battleUsers", "hpw_1", "uP", "sr.hqs", "sr.hqs(t, n.hpw_1)", listOf("?_P", "?hP", "?lP", "?oP", "?qP", "?sP", "InitSuspiciousState", "InitUserLevels", "RemoveTankInfo", "SetGearScore", "SetTankInfo", "SetTankResistance", "UpdateUserLevel", "UpdateUserRank", "UpdateUserScoreKillsAndDeaths", "UpdateUserSuspiciousState", "UpdateUsersClanTag", "UpdateUsersScoreKillsAndDeaths")),
SliceWiring("suppliesState", "ipw_1", "wE", "\$t.fq9", "\$t.fq9(t, n.ipw_1)", listOf("?qE")),
SliceWiring("matchmakingGroup", "jpw_1", "rW", "NW().is0", "NW().is0(t, n.jpw_1)", listOf("?zW", "AcceptedInviteToGroup", "AddUser", "AddUserToSelected", "InitMatchmakingGroup", "Invite", "RejectedInviteToGroup", "RemoveUserToSelected", "SetAllowedFromFriends", "SetSelectedUsers", "UpdateMountItems", "UpdateUserReadiness", "UserRemoved")),
SliceWiring("shop", "kpw_1", "j4", "(inline@shop)", "(function (t, n) { var i; if (t instanceof w4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, t.lt6_1); else if (t instanceof Yut) i = n.qt6(cd, cd, cd, cd, !0); else if (t instanceof F5) i = new j4(cd, cd, cd, cd, cd, cd, cd, n.qq3_1); else if (t instanceof h4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.st5_1); else if (t instanceof c4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ut5_1); else if (t instanceof u4) i = n.qt6( cd, cd, cd, cd, cd, cd, …", listOf("?x4", "AddCategoriesWithBonus", "AddItemToOrderProcessing", "BlockBackButton", "BraintreeLoadingInterrupt", "ClearPreviousScreen", "ClearTargetCategory", "ClearTargetProductId", "CurrentPayMode", "DeleteOneTimePurchaseItem", "InitAdditionalDescription", "InitAmountString", "InitCategory", "InitCoinPackage", "InitCompensation", "InitCrystalKitPackage", "InitFeatureItem", "InitGroup", "InitItemViewType", "InitKeysPackage", "InitKitBundle", "InitKitPackage", "InitLootBoxAndPaint", "InitOneTimePurchaseItem", "InitPayModeAction", "InitPaymentModeAsShopItem", "InitPaymentProviderItems", "InitPaymentViewType", "InitRestrictionByPayMode", "InitRewardedVideosBlocks", "InitShopCard", "InitShopCardDiscount", "InitShopCardView", "InitShopItem", "InitShopItemName", "InitSingleItemKit", "InitSpecialKitPackage", "MobilePaymentStateChanged", "PaymentProviderFinishFail", "PaymentProviderFinishSuccess", "PaymentProviderInProgress", "PaymentUrlReceiveFailed", "PaymentUrlReceived", "RemoveDiscountItemForItem", "RemoveItemFromOrderProcessing", "ResetPaymentState", "ResetShop", "SavePreviousScreen", "SaveTargetProductId", "SaveTargetVisitedCategory", "SelectCategory", "SelectCategoryId", "SelectItem", "SetBraintreeToken", "SetCoinPaymentProviderUrl", "SetGoToHomeAfterBuy", "SetInitShopItemId", "SetPurchaseCompleted", "SetStripeIntentParams", "SetupDiscountCategoryByItem", "SetupDiscountItem", "SetupDiscountItemForItem", "ShopCardExpired", "ShopCardRemoved", "ShopCardSold", "ShopLoaded", "ToggleShopActivity", "TryToBuyOneTimePurchase", "UnblockBackButton")),
SliceWiring("battlePauseState", "lpw_1", "dT", "NT().mqc", "NT().mqc(t, n.lpw_1)", listOf("?TT", "?xT", "OnPauseMode", "SetIdleKickPeriod")),
SliceWiring("battleViewState", "mpw_1", "DT", "sI.jqd", "(null == \$n && new sI(), \$n).jqd(t, n.mpw_1)", listOf("?JT", "?QT", "?WT", "?YT", "?ZT", "?eI", "?iI", "?nE", "?nI", "?rI", "?tE", "?tI", "SetElementsVisibility", "SetVisibility")),
SliceWiring("rankUpDialogState", "npw_1", "LO", "Se.br3", "Se.br3(t, n.npw_1)", listOf("?FO", "ShowAvailableItems")),
SliceWiring("rankUpDialogState", "npw_1", "LO", "Se.br3", "Se.br3(t, n.npw_1)", listOf("ResetRankUpDialog", "ShowAvailableItems")),
SliceWiring("clanUserInfo", "opw_1", "hO", "de.gr1", "de.gr1(t, n.opw_1)", listOf("Loaded")),
SliceWiring("tankActionsLogState", "ppw_1", "vI", "wi.gqe", "wi.gqe(t, n.ppw_1)", listOf("?dI", "OnUserJoinTheBattle", "OnUserLeaveTheBattle", "UpdateLog")),
SliceWiring("battleMessage", "qpw_1", "FE", "vT().rqb", "vT().rqb(t, n.qpw_1)", listOf("?sT", "DoAddLocalizedMessage", "DoAddLocalizedTeamMessage", "DoAddLocalizedUserMessage", "DoAddTextMessage", "HideMessage", "RemoveMessage")),
SliceWiring("battleChat", "rpw_1", "Q6", "Hf.wtg", "Hf.wtg(t, n.rpw_1)", listOf("?ett", "?rtt", "?stt", "?utt", "AddMessage", "ChatLoaded")),
SliceWiring("droppableGold", "spw_1", "Vet", "Wp.kth", "Wp.kth(t, n.spw_1)", listOf("?Ket", "?Yet")),
SliceWiring("droppableGold", "spw_1", "Vet", "Wp.kth", "Wp.kth(t, n.spw_1)", listOf("HideDroppableGoldAuthor", "ShowDroppableGoldAuthor")),
SliceWiring("home", "tpw_1", "s1", "(inline@home)", "(function (t, n) { return t instanceof o1 ? n.f7j(!0) : n; })(t, n.tpw_1)", listOf("ShowShopDiscountNotification")),
SliceWiring("localBattleUserState", "upw_1", "lE", "(inline@localBattleUserState)", "(function (t, n) { return t instanceof fE ? n.xq8(t.yq8_1) : t instanceof vE ? n.xq8(cd, t.zq8_1) : n; })(t, n.upw_1)", listOf("SetLocalWeaponResistanceProperty", "SetReArmorTemporaryDisabled")),
SliceWiring("soundSettings", "vpw_1", "Det", "Wet().jth", "Wet().jth(t, n.vpw_1)", listOf("?Uet", "?_C", "SetEntranceSoundEnabled", "UpdateSettings")),
@@ -86,7 +86,7 @@ object RootWiring {
SliceWiring("notificationByCountry", "ppx_1", "gJ", "R_.ssd", "R_.ssd(t, n.ppx_1)", listOf("Init", "MarkHomeScreenDialogShown")),
SliceWiring("userRenameState", "qpx_1", "O8", "Rf.pte", "Rf.pte(t, n.qpx_1)", listOf("?B8", "?F8", "?G8", "?Q8", "?U8", "ChangeUidInputState", "ResponseActual", "ResponseDelivered", "SetChangeUidParams", "SetRubiesForRename", "SetValidationStatus")),
SliceWiring("eventGoods", "rpx_1", "W7", "Wc.esx", "Wc.esx(t, n.rpx_1)", listOf("InitEventGoods")),
SliceWiring("creatorTagState", "spx_1", "qO", "ze.er2", "ze.er2(t, n.spx_1)", listOf("?jO", "SetAttachedCreatorTag", "SetCreatorTag", "SetCreatorTags")),
SliceWiring("creatorTagState", "spx_1", "qO", "ze.er2", "ze.er2(t, n.spx_1)", listOf("ClearCreatorTag", "SetAttachedCreatorTag", "SetCreatorTag", "SetCreatorTags")),
SliceWiring("twoStepVerificationState", "tpx_1", "x9", "_f.atb", "_f.atb(t, n.tpx_1)", listOf("ActivationStatus2FA", "Enable2FaRequest", "SetSecretAndQRCode", "SetUse2FA", "VerificationStatus")),
SliceWiring("rewardedVideosState", "upx_1", "JQ", "xh.zsl", "xh.zsl(t, n.upx_1)", listOf("SetDisplayState")),
SliceWiring("twitchState", "vpx_1", "g9", "uf.gta", "uf.gta(t, n.vpx_1)", listOf("SetAccount", "SetDaysForNextLinking", "SetTwitchEnabled")),
@@ -106,7 +106,7 @@ object RootWiring {
)
/** 已迁移(强类型;launchParams 为透传,不调用 reducer)的字段。 */
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams", "news", "notifications", "quests", "lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl", "rulesUpdated", "notificationByCountry", "availabilityByRank", "icons", "questCategories", "mainQuest", "securitySettings", "referralEvent")
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams", "news", "notifications", "quests", "lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl", "rulesUpdated", "notificationByCountry", "availabilityByRank", "icons", "questCategories", "mainQuest", "securitySettings", "referralEvent", "tipsState", "rankUpDialogState", "battleResult", "captchaState", "modalScreen", "droppableGold", "eventGoods", "creatorTagState", "rewardedVideosState", "passwordChangeState")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
@@ -9,11 +9,13 @@
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
// ⑧ 全目录动作类都能按 KClass 路由
// ⑨~㊸ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// ⑨~㊿/51-53 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage / GaragePreview / Entrance / User / Settings /
// AccountSettings / ProBattle / Language / Announcement / News / Notifications / Quests /
// LobbyLoader / PurchaseDialogState / PromoCode / SupportUrl / RulesUpdated / NotificationByCountry / AvailabilityByRank /
// Icons / QuestCategories / MainQuest / SecuritySettings / ReferralEvent;
// Icons / QuestCategories / MainQuest / SecuritySettings / ReferralEvent /
// TipsState / RankUpDialogState / BattleResult / CaptchaState / ModalScreen / DroppableGold / EventGoods /
// CreatorTagState / RewardedVideosState / PasswordChangeState;
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
@@ -388,6 +390,56 @@ import App.State.SetReferrals
import App.State.SetReferrer
import App.State.Reducers.ReduceReferralEvent
import App.State.Reducers.ReferralEventStateSlice
import App.State.UpdateTips
import App.State.Reducers.ReduceTips
import App.State.Reducers.TipsStateSlice
import App.State.ShowAvailableItems
import App.State.Reducers.RankUpDialogStateSlice
import App.State.Reducers.ReduceRankUpDialog
import App.State.Reducers.ResetRankUpDialog
import App.State.DoInit
import App.State.SetHasBattleResult
import App.State.Reducers.BattleResultStateSlice
import App.State.Reducers.ReduceBattleResult
import App.State.Reducers.ResetBattleRewards
import App.State.Reducers.SetIncreasedReward
import App.State.Reducers.SetSimpleReward
import App.State.LocationsUpdated
import App.State.SetLocations
import App.State.Reducers.AddCaptchaLocationToReset
import App.State.Reducers.AddVerifiedCaptchaLocation
import App.State.Reducers.CaptchaStateSlice
import App.State.Reducers.ReduceCaptcha
import App.State.Reducers.RemoveCaptchaLocationToReset
import App.State.Reducers.RemoveVerifiedCaptchaLocation
import App.State.Reducers.ShowFakeCaptcha
import App.State.AddModal
import App.State.RemoveCurrentByName
import App.State.RemoveModal
import App.State.Reducers.CloseCurrentModal
import App.State.Reducers.ModalScreenStateSlice
import App.State.Reducers.ReduceModalScreen
import App.State.Reducers.DroppableGoldStateSlice
import App.State.Reducers.HideDroppableGoldAuthor
import App.State.Reducers.ReduceDroppableGold
import App.State.Reducers.ShowDroppableGoldAuthor
import App.State.InitEventGoods
import App.State.Reducers.EventGoodsStateSlice
import App.State.Reducers.ReduceEventGoods
import App.State.SetAttachedCreatorTag
import App.State.SetCreatorTag
import App.State.SetCreatorTags
import App.State.Reducers.ClearCreatorTag
import App.State.Reducers.CreatorTagStateSlice
import App.State.Reducers.ReduceCreatorTag
import App.State.SetDisplayState
import App.State.Reducers.ReduceRewardedVideos
import App.State.Reducers.RewardVideoDisplayState
import App.State.Reducers.RewardedVideosStateSlice
import App.State.SetPasswordChangeHash
import App.State.ShowEmailAndPasswordChangeDialog
import App.State.Reducers.PasswordChangeStateSlice
import App.State.Reducers.ReducePasswordChange
import App.State.Reducers.UserRankBounds
import App.State.Reducers.UserStateSlice
import App.State.Reducers.ClearHullData
@@ -815,7 +867,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 33 个已迁移切片(32 归约 + launchParams 透传)
// ⑭ 根 reducer:89 个槽位的委派表 + 43 个已迁移切片(42 归约 + launchParams 透传)
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -879,7 +931,7 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 33 个已迁移字段,其余 56 个待迁",
"根:迁移表 = 43 个已迁移字段,其余 46 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
@@ -890,8 +942,11 @@ object StateCheck {
"lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl",
"rulesUpdated", "notificationByCountry", "availabilityByRank",
"icons", "questCategories", "mainQuest", "securitySettings", "referralEvent",
"tipsState", "rankUpDialogState", "battleResult", "captchaState",
"modalScreen", "droppableGold", "eventGoods", "creatorTagState",
"rewardedVideosState", "passwordChangeState",
) &&
RootWiring.Unmigrated.size == 56
RootWiring.Unmigrated.size == 46
)
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
@@ -947,7 +1002,7 @@ object StateCheck {
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 33/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 43/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -3664,6 +3719,407 @@ object StateCheck {
)
}
// ㊹ 切片 reducer:TipsState(dst / Hw.vth,1 字段;mst → UpdateTips)
run {
val S0 = TipsStateSlice()
Check("切片 TipsState:默认空列表", S0.Tips.isEmpty())
val S1 = ReduceTips(S0, UpdateTips(listOf("a", "b")))
Check("切片 TipsState:UpdateTips 整体替换", S1.Tips == listOf<Any?>("a", "b"))
Check("切片 TipsState:无关动作不改状态", ReduceTips(S1, AddFrags(1)) == S1)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, UpdateTips(listOf("x")))
Check(
"根:UpdateTips 只动 tipsState",
Root1.TipsState.Tips == listOf<Any?>("x") && Root1.BattleResult == Root0.BattleResult
)
val W = RootWiring.ByField("tipsState")!!
Check(
"根:tipsState 委派到 dst / Hw.vth",
W.SliceClass == "dst" && W.Reducer == "Hw.vth" && W.Actions == listOf("UpdateTips")
)
}
// ㊺ 切片 reducer:RankUpDialog(LO / Se.br3,1 字段;UO + FO)
run {
val S0 = RankUpDialogStateSlice()
Check("切片 RankUpDialog:默认空表", S0.AvailableItems.isEmpty())
val S1 = ReduceRankUpDialog(S0, ShowAvailableItems(listOf("a")))
Check(
"切片 RankUpDialog:ShowAvailableItems 追加整表",
S1.AvailableItems == listOf<Any?>("a")
)
val S2 = ReduceRankUpDialog(S1, ShowAvailableItems(listOf("b", "c")))
Check(
"切片 RankUpDialog:再次追加是拼接",
S2.AvailableItems == listOf<Any?>("a", "b", "c")
)
Check(
"切片 RankUpDialog:ResetRankUpDialog 清空列表",
ReduceRankUpDialog(S2, ResetRankUpDialog).AvailableItems.isEmpty()
)
Check(
"切片 RankUpDialog:无关动作不改状态",
ReduceRankUpDialog(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ShowAvailableItems(listOf("x")))
Check(
"根:ShowAvailableItems 只动 rankUpDialogState",
Root1.RankUpDialogState.AvailableItems == listOf<Any?>("x") &&
Root1.TipsState == Root0.TipsState
)
val W = RootWiring.ByField("rankUpDialogState")!!
Check(
"根:rankUpDialogState 委派到 LO / Se.br3 且 2 个动作",
W.SliceClass == "LO" && W.Reducer == "Se.br3" &&
W.Actions.toSet() == setOf("ShowAvailableItems", "ResetRankUpDialog")
)
}
// ㊻ 切片 reducer:BattleResult(sR / Fi.aqp,5 字段;6 个动作)
run {
val Item = ContainerGivenItem(
category = "cat", count = 1, garageItemCategory = "hull", garageItemId = 2,
image = null, name = "n", rewardType = "t",
)
val S0 = BattleResultStateSlice()
Check(
"切片 BattleResult:默认值完整",
!S0.HasBattleResult && S0.Rewards.isEmpty() &&
S0.InitialCurrentUserXP == null && S0.IncreasedReward == 0 &&
S0.SimpleReward == 0
)
val S1 = ReduceBattleResult(S0, SetRewards(listOf(Item)))
Check("切片 BattleResult:SetRewards 写奖励表", S1.Rewards == listOf(Item))
val S2 = ReduceBattleResult(S1, SetHasBattleResult(true))
Check("切片 BattleResult:SetHasBattleResult 写布尔", S2.HasBattleResult)
val S3 = ReduceBattleResult(
ReduceBattleResult(S2, SetIncreasedReward(15)),
SetSimpleReward(10),
)
Check(
"切片 BattleResult:cR / lR 分别写两个奖励数值",
S3.IncreasedReward == 15 && S3.SimpleReward == 10
)
Check(
"切片 BattleResult:ResetBattleRewards 只把两个数值归零",
ReduceBattleResult(S3, ResetBattleRewards) ==
S3.copy(IncreasedReward = 0, SimpleReward = 0)
)
val S4 = ReduceBattleResult(S3, DoInit(500))
Check(
"切片 BattleResult:DoInit 重建状态只带初始 XP",
!S4.HasBattleResult && S4.Rewards.isEmpty() &&
S4.InitialCurrentUserXP == 500 && S4.IncreasedReward == 0 &&
S4.SimpleReward == 0
)
Check(
"切片 BattleResult:无关动作不改状态",
ReduceBattleResult(S4, AddFrags(1)) == S4
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetHasBattleResult(true))
Check(
"根:SetHasBattleResult 只动 battleResult",
Root1.BattleResult.HasBattleResult && Root1.CaptchaState == Root0.CaptchaState
)
val W = RootWiring.ByField("battleResult")!!
Check(
"根:battleResult 委派到 sR / Fi.aqp 且 6 个动作",
W.SliceClass == "sR" && W.Reducer == "Fi.aqp" &&
W.Actions.toSet() == setOf(
"SetRewards", "SetHasBattleResult", "SetIncreasedReward",
"SetSimpleReward", "ResetBattleRewards", "DoInit",
)
)
}
// ㊼ 切片 reducer:Captcha(jP / ar.iqt,4 字段;7 个动作)
run {
val S0 = CaptchaStateSlice()
Check(
"切片 Captcha:默认不造假、三个集合为空",
!S0.UseFake && S0.EnabledLocations.isEmpty() &&
S0.VerifiedLocations.isEmpty() && S0.LocationsToReset.isEmpty()
)
val S1 = ReduceCaptcha(S0, ShowFakeCaptcha)
Check("切片 Captcha:ShowFakeCaptcha 置 useFake", S1.UseFake)
val S2 = ReduceCaptcha(S1, SetLocations(listOf("a", "b")))
Check(
"切片 Captcha:SetLocations 收成集合",
S2.EnabledLocations == setOf<Any?>("a", "b")
)
val S3 = ReduceCaptcha(
ReduceCaptcha(S2, AddVerifiedCaptchaLocation("a")),
AddVerifiedCaptchaLocation("b"),
)
Check(
"切片 Captcha:已验证集合加元素",
S3.VerifiedLocations == setOf<Any?>("a", "b")
)
Check(
"切片 Captcha:已验证集合删元素",
ReduceCaptcha(S3, RemoveVerifiedCaptchaLocation("a"))
.VerifiedLocations == setOf<Any?>("b")
)
val S4 = ReduceCaptcha(
ReduceCaptcha(S3, AddCaptchaLocationToReset("c")),
RemoveCaptchaLocationToReset("c"),
)
Check("切片 Captcha:待重置集合加减后为空", S4.LocationsToReset.isEmpty())
Check(
"切片 Captcha:LocationsUpdated 追加到启用集合",
ReduceCaptcha(S4, LocationsUpdated("d"))
.EnabledLocations == setOf<Any?>("a", "b", "d")
)
Check("切片 Captcha:无关动作不改状态", ReduceCaptcha(S4, AddFrags(1)) == S4)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ShowFakeCaptcha)
Check(
"根:ShowFakeCaptcha 只动 captchaState",
Root1.CaptchaState.UseFake && Root1.ModalScreen == Root0.ModalScreen
)
val W = RootWiring.ByField("captchaState")!!
Check(
"根:captchaState 委派到 jP / ar.iqt 且 7 个动作",
W.SliceClass == "jP" && W.Reducer == "ar.iqt" &&
W.Actions.toSet() == setOf(
"ShowFakeCaptcha", "SetLocations", "AddVerifiedCaptchaLocation",
"RemoveVerifiedCaptchaLocation", "AddCaptchaLocationToReset",
"RemoveCaptchaLocationToReset", "LocationsUpdated",
)
)
}
// ㊽ 切片 reducer:ModalScreen(nV / Fs.js1,2 字段;4 个动作)
run {
val S0 = ModalScreenStateSlice()
Check(
"切片 ModalScreen:默认无当前窗口、队列为空",
S0.Current == null && S0.ModalQueue.isEmpty()
)
val S1 = ReduceModalScreen(S0, AddModal(true))
Check(
"切片 ModalScreen:AddModal 写 current 并入队",
S1.Current == true && S1.ModalQueue == listOf<Any?>(true)
)
Check(
"切片 ModalScreen:同名窗口不重复入队",
ReduceModalScreen(S1, AddModal(true)) == S1
)
val S2 = ReduceModalScreen(S1, AddModal(false))
Check(
"切片 ModalScreen:第二个窗口排到队尾",
S2.Current == false && S2.ModalQueue == listOf<Any?>(true, false)
)
val S3 = ReduceModalScreen(S2, RemoveModal(true))
Check(
"切片 ModalScreen:RemoveModal 出队并把 current 交给队首",
S3.Current == false && S3.ModalQueue == listOf<Any?>(false)
)
Check(
"切片 ModalScreen:CloseCurrentModal 清掉当前窗口",
ReduceModalScreen(S3, CloseCurrentModal) == ModalScreenStateSlice()
)
Check(
"切片 ModalScreen:RemoveCurrentByName 名称不符时不动",
ReduceModalScreen(S3, RemoveCurrentByName("other")) == S3
)
Check(
"切片 ModalScreen:RemoveCurrentByName 名称匹配时关闭",
ReduceModalScreen(S3, RemoveCurrentByName("false")) == ModalScreenStateSlice()
)
Check(
"切片 ModalScreen:无关动作不改状态",
ReduceModalScreen(S3, AddFrags(1)) == S3
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, AddModal(true))
Check(
"根:AddModal 只动 modalScreen",
Root1.ModalScreen.Current == true &&
Root1.EventGoods == Root0.EventGoods
)
val W = RootWiring.ByField("modalScreen")!!
Check(
"根:modalScreen 委派到 nV / Fs.js1 且 4 个动作",
W.SliceClass == "nV" && W.Reducer == "Fs.js1" &&
W.Actions.toSet() == setOf(
"AddModal", "RemoveModal", "CloseCurrentModal", "RemoveCurrentByName",
)
)
}
// ㊾ 切片 reducer:DroppableGold(Vet / Wp.kth,1 字段;Ket/Yet 两个单例)
run {
val S0 = DroppableGoldStateSlice()
Check("切片 DroppableGold:默认显示作者署名", S0.ShowDroppableGoldAuthor)
Check(
"切片 DroppableGold:Hide 置 false、Show 置 true",
!ReduceDroppableGold(S0, HideDroppableGoldAuthor).ShowDroppableGoldAuthor &&
ReduceDroppableGold(S0, ShowDroppableGoldAuthor).ShowDroppableGoldAuthor
)
Check(
"切片 DroppableGold:无关动作不改状态",
ReduceDroppableGold(S0, AddFrags(1)) == S0
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, HideDroppableGoldAuthor)
Check(
"根:HideDroppableGoldAuthor 只动 droppableGold",
!Root1.DroppableGold.ShowDroppableGoldAuthor &&
Root1.CreatorTagState == Root0.CreatorTagState
)
val W = RootWiring.ByField("droppableGold")!!
Check(
"根:droppableGold 委派到 Vet / Wp.kth 且 2 个动作",
W.SliceClass == "Vet" && W.Reducer == "Wp.kth" &&
W.Actions.toSet() == setOf("ShowDroppableGoldAuthor", "HideDroppableGoldAuthor")
)
}
// ㊿ 切片 reducer:EventGoods(W7 / Wc.esx,1 字段;V7 → InitEventGoods)
run {
val S0 = EventGoodsStateSlice()
Check("切片 EventGoods:默认空表", S0.EventGoods.isEmpty())
val S1 = ReduceEventGoods(S0, InitEventGoods("spectatorMode", true))
Check(
"切片 EventGoods:InitEventGoods 写键值",
S1.EventGoods == mapOf<Any?, Any?>("spectatorMode" to true)
)
val S2 = ReduceEventGoods(S1, InitEventGoods("spectatorMode", null))
Check("切片 EventGoods:goods 为空时删键", S2.EventGoods.isEmpty())
Check(
"切片 EventGoods:无关动作不改状态",
ReduceEventGoods(S1, AddFrags(1)) == S1
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, InitEventGoods("k", 1))
Check(
"根:InitEventGoods 只动 eventGoods",
Root1.EventGoods.EventGoods == mapOf<Any?, Any?>("k" to 1) &&
Root1.RewardedVideosState == Root0.RewardedVideosState
)
val W = RootWiring.ByField("eventGoods")!!
Check(
"根:eventGoods 委派到 W7 / Wc.esx",
W.SliceClass == "W7" && W.Reducer == "Wc.esx" &&
W.Actions == listOf("InitEventGoods")
)
}
// 51 切片 reducer:CreatorTag(qO / ze.er2,2 字段;4 个动作)
run {
val S0 = CreatorTagStateSlice()
Check("切片 CreatorTag:默认两个字段都为 null", S0.CreatorTag == null && S0.CreatorTags == null)
val S1 = ReduceCreatorTag(S0, SetCreatorTags(listOf("a")))
Check("切片 CreatorTag:SetCreatorTags 写列表", S1.CreatorTags == listOf<Any?>("a"))
Check(
"切片 CreatorTag:SetCreatorTag 写当前标签",
ReduceCreatorTag(S1, SetCreatorTag("t1")).CreatorTag == "t1"
)
val S2 = ReduceCreatorTag(S1, SetAttachedCreatorTag("t2"))
Check(
"切片 CreatorTag:SetAttachedCreatorTag 覆盖当前标签",
S2.CreatorTag == "t2" && S2.CreatorTags == listOf<Any?>("a")
)
val S3 = ReduceCreatorTag(S2, ClearCreatorTag)
Check(
"切片 CreatorTag:ClearCreatorTag 只清当前标签",
S3.CreatorTag == null && S3.CreatorTags == listOf<Any?>("a")
)
Check("切片 CreatorTag:无关动作不改状态", ReduceCreatorTag(S3, AddFrags(1)) == S3)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetCreatorTag("x"))
Check(
"根:SetCreatorTag 只动 creatorTagState",
Root1.CreatorTagState.CreatorTag == "x" && Root1.EventGoods == Root0.EventGoods
)
val W = RootWiring.ByField("creatorTagState")!!
Check(
"根:creatorTagState 委派到 qO / ze.er2 且 4 个动作",
W.SliceClass == "qO" && W.Reducer == "ze.er2" &&
W.Actions.toSet() == setOf(
"SetCreatorTags", "SetCreatorTag", "SetAttachedCreatorTag", "ClearCreatorTag",
)
)
}
// 52 切片 reducer:RewardedVideos(JQ / xh.zsl,1 字段;t1 → SetDisplayState)
run {
val S0 = RewardedVideosStateSlice()
Check("切片 RewardedVideos:默认 NONE", S0.DisplayState == RewardVideoDisplayState.NONE)
Check(
"切片 RewardedVideos:SetDisplayState 写枚举",
ReduceRewardedVideos(S0, SetDisplayState(RewardVideoDisplayState.DISPLAY))
.DisplayState == RewardVideoDisplayState.DISPLAY
)
Check(
"切片 RewardedVideos:无关动作不改状态",
ReduceRewardedVideos(S0, AddFrags(1)) == S0
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetDisplayState(RewardVideoDisplayState.LOADING))
Check(
"根:SetDisplayState 只动 rewardedVideosState",
Root1.RewardedVideosState.DisplayState == RewardVideoDisplayState.LOADING &&
Root1.PasswordChangeState == Root0.PasswordChangeState
)
val W = RootWiring.ByField("rewardedVideosState")!!
Check(
"根:rewardedVideosState 委派到 JQ / xh.zsl",
W.SliceClass == "JQ" && W.Reducer == "xh.zsl" &&
W.Actions == listOf("SetDisplayState")
)
}
// 53 切片 reducer:PasswordChange(VD / uu.pr6,3 字段;UD + HD)
run {
val S0 = PasswordChangeStateSlice()
Check(
"切片 PasswordChange:默认三个空串",
S0.Hash == "" && S0.Uid == "" && S0.Email == ""
)
val S1 = ReducePasswordChange(S0, SetPasswordChangeHash("h"))
Check(
"切片 PasswordChange:SetPasswordChangeHash 只写 hash",
S1.Hash == "h" && S1.Uid == "" && S1.Email == ""
)
val S2 = ReducePasswordChange(S1, ShowEmailAndPasswordChangeDialog("e", "u", "r"))
Check(
"切片 PasswordChange:ShowEmailAndPasswordChangeDialog 写 uid/email 但保留 hash",
S2.Hash == "h" && S2.Uid == "u" && S2.Email == "e"
)
Check(
"切片 PasswordChange:无关动作不改状态",
ReducePasswordChange(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetPasswordChangeHash("x"))
Check(
"根:SetPasswordChangeHash 只动 passwordChangeState",
Root1.PasswordChangeState.Hash == "x" && Root1.TipsState == Root0.TipsState
)
val W = RootWiring.ByField("passwordChangeState")!!
Check(
"根:passwordChangeState 委派到 VD / uu.pr6 且 2 个动作",
W.SliceClass == "VD" && W.Reducer == "uu.pr6" &&
W.Actions.toSet() == setOf(
"SetPasswordChangeHash", "ShowEmailAndPasswordChangeDialog",
)
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)