feat(state): 迁移补给箱切片(18 个动作 + ContainerType)
This commit is contained in:
1 parent
974a9f49ad
commit
f66f431670
13 files changed
+606
-35
No files matched your search
@@ -143,7 +143,7 @@
|
|||||||
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
||||||
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
||||||
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
||||||
- 已迁移切片 **7/89**:
|
- 已迁移切片 **8/89**:
|
||||||
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
||||||
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
||||||
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
||||||
@@ -158,9 +158,15 @@
|
|||||||
`current` 换成目标屏并清 `isResetBackStackOnNextGoTo` / `isDelayedGoto`;`screen_show` analytics 是副作用,纯 reducer 只保留状态。
|
`current` 换成目标屏并清 `isResetBackStackOnNextGoTo` / `isDelayedGoto`;`screen_show` analytics 是副作用,纯 reducer 只保留状态。
|
||||||
屏幕身份用 `App.State.ScreenId`(`EntranceScreen` / `LoaderScreen` / `NamedScreen(name)`);
|
屏幕身份用 `App.State.ScreenId`(`EntranceScreen` / `LoaderScreen` / `NamedScreen(name)`);
|
||||||
`LX` / `RX` 是屏幕标识类,已从动作表剔除并记入 `root_wiring.json` 的 `Markers`(`screen-id-class`)。
|
`LX` / `RX` 是屏幕标识类,已从动作表剔除并记入 `root_wiring.json` 的 `Markers`(`screen-id-class`)。
|
||||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 只归约上述七片,其余 **82 个字段原样透传**;
|
- `lootBoxes` → `aH` / `EH.wrv`(`LootBoxesReducer.kt`):18 字段,18 个 `instanceof` 臂全量翻译。
|
||||||
迁移进度用 `RootReducer.Coverage`(`已迁移 7/89`)与 `RootWiring.Unmigrated` 暴露。
|
`timeToGiveLootBox` 是 Long 毫秒,`SetTimeToGiveLootBox` 按 `Date.now() + seconds * 1000` 写入;
|
||||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **141 项全绿**,含 89 槽位完整性、七片一次归约互不串片、未迁移字段透传,
|
`AddKeys` 沿用原 `Map.getValue` 语义(缺键抛错),`SetRewards` 同时把 `isReopening` 置 false;
|
||||||
|
`ClearRewards` 清空 `rewards` / `shownRewards`,`ShownRewards` 是追加(原 `List.plus`)。
|
||||||
|
补给箱类型用 `App.State.ContainerType`(`STANDARD/RARE/EPIC/LEGENDARY`,默认 `STANDARD`);
|
||||||
|
无 `toString` 的 `mH/kH/zH/gH/CH/SH` 按效果定名(`SetTimeToGiveLootBox`/`SetPeriodOfGiveLootBox`/…)。
|
||||||
|
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 只归约上述八片,其余 **81 个字段原样透传**;
|
||||||
|
迁移进度用 `RootReducer.Coverage`(`已迁移 8/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||||
|
- 自检:`TankiOffline/tools/RunStateTest.ps1` **163 项全绿**,含 89 槽位完整性、八片一次归约互不串片、未迁移字段透传,
|
||||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||||
@@ -168,7 +174,8 @@
|
|||||||
- 无 `toString` 的默认动作按证据定名(`root_wiring.json` 的 `AnonymousActions`):
|
- 无 `toString` 的默认动作按证据定名(`root_wiring.json` 的 `AnonymousActions`):
|
||||||
`o1` → `ShowShopDiscountNotification`(`$_$.gl` 单例,由 module_18334 的 `Qx.qge` 派发;
|
`o1` → `ShowShopDiscountNotification`(`$_$.gl` 单例,由 module_18334 的 `Qx.qge` 派发;
|
||||||
效果是把 `HomeScreen.shopDiscountNotification` 置 true,对应主菜单商店项的折扣提示);
|
效果是把 `HomeScreen.shopDiscountNotification` 置 true,对应主菜单商店项的折扣提示);
|
||||||
shop 的 17 个匿名类按 reducer 效果定名并登记(`Yut`→`ShopLoaded`、`F5`→`ResetShop` 等)。
|
shop 的 17 个匿名类按 reducer 效果定名并登记(`Yut`→`ShopLoaded`、`F5`→`ResetShop` 等);
|
||||||
|
screen 的 9 个、lootBoxes 的 6 个匿名类同样按效果定名并登记。
|
||||||
|
|
||||||
### S4 引擎核心
|
### S4 引擎核心
|
||||||
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
|
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generatedAt": "2026-10-22T19:22:01.390Z",
|
"generatedAt": "2026-10-23T04:19:26.216Z",
|
||||||
"module": "module_10032.js",
|
"module": "module_10032.js",
|
||||||
"RootState": {
|
"RootState": {
|
||||||
"Class": "z6",
|
"Class": "z6",
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
"home",
|
"home",
|
||||||
"localBattleUserState",
|
"localBattleUserState",
|
||||||
"shop",
|
"shop",
|
||||||
"screen"
|
"screen",
|
||||||
|
"lootBoxes"
|
||||||
],
|
],
|
||||||
"AnonymousActions": [
|
"AnonymousActions": [
|
||||||
{
|
{
|
||||||
@@ -178,6 +179,42 @@
|
|||||||
"Name": "PopScreen",
|
"Name": "PopScreen",
|
||||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
"Effect": "screenStack = dropLast(1)(`f$(stack, 1)` 是 Kotlin dropLast)"
|
"Effect": "screenStack = dropLast(1)(`f$(stack, 1)` 是 Kotlin dropLast)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "mH",
|
||||||
|
"Name": "SetTimeToGiveLootBox",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "timeToGiveLootBox = Date.now() + seconds * 1000(第 9 个构造参数 `Fg(Md(), Qk.y2o(Wg(t.zrv_1)))`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "kH",
|
||||||
|
"Name": "SetPeriodOfGiveLootBox",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "periodOfGiveLootBoxInSec = action.yrv_1(第 10 个构造参数)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "zH",
|
||||||
|
"Name": "ClearRewards",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "rewards = emptyList()、shownRewards = emptyList()(第 6、7 个构造参数 `md(), md()`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "gH",
|
||||||
|
"Name": "LootBoxOpeningFailed",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "openingFailed = true(第 11 个构造参数 !0)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "CH",
|
||||||
|
"Name": "SetIsReopening",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isReopening = true(第 17 个构造参数 !0)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "SH",
|
||||||
|
"Name": "SetIsBlockOpenButtons",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isBlockOpenButtons = action.xrv_1(第 18 个构造参数)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Markers": [
|
"Markers": [
|
||||||
@@ -798,27 +835,27 @@
|
|||||||
"Reducer": "ws.wrv",
|
"Reducer": "ws.wrv",
|
||||||
"Delegate": "ws.wrv(t, n.wpv_1)",
|
"Delegate": "ws.wrv(t, n.wpv_1)",
|
||||||
"Actions": [
|
"Actions": [
|
||||||
"?CH",
|
|
||||||
"?SH",
|
|
||||||
"?gH",
|
|
||||||
"?kH",
|
|
||||||
"?mH",
|
|
||||||
"?zH",
|
|
||||||
"AddKeys",
|
"AddKeys",
|
||||||
|
"ClearRewards",
|
||||||
|
"LootBoxOpeningFailed",
|
||||||
"SelectCategory",
|
"SelectCategory",
|
||||||
"SetContainerData",
|
"SetContainerData",
|
||||||
"SetCurrentContainer",
|
"SetCurrentContainer",
|
||||||
"SetGoToGarage",
|
"SetGoToGarage",
|
||||||
"SetIsAccelerated",
|
"SetIsAccelerated",
|
||||||
|
"SetIsBlockOpenButtons",
|
||||||
"SetIsOpening",
|
"SetIsOpening",
|
||||||
|
"SetIsReopening",
|
||||||
"SetIsShopVisited",
|
"SetIsShopVisited",
|
||||||
"SetKeys",
|
"SetKeys",
|
||||||
|
"SetPeriodOfGiveLootBox",
|
||||||
"SetPossibleRewards",
|
"SetPossibleRewards",
|
||||||
"SetRewards",
|
"SetRewards",
|
||||||
|
"SetTimeToGiveLootBox",
|
||||||
"ShownRewards"
|
"ShownRewards"
|
||||||
],
|
],
|
||||||
"Default": "new aH()",
|
"Default": "new aH()",
|
||||||
"Migrated": false
|
"Migrated": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Field": "notifications",
|
"Field": "notifications",
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
{
|
||||||
|
"generatedAt": "2026-10-23T03:54:25.170Z",
|
||||||
|
"module": "module_10032.js",
|
||||||
|
"Field": "lootBoxes",
|
||||||
|
"Slot": "wpv_1",
|
||||||
|
"Class": "aH",
|
||||||
|
"ClassSource": "new aH()",
|
||||||
|
"Params": [
|
||||||
|
"t",
|
||||||
|
"n",
|
||||||
|
"i",
|
||||||
|
"r",
|
||||||
|
"e",
|
||||||
|
"u",
|
||||||
|
"s",
|
||||||
|
"o",
|
||||||
|
"_",
|
||||||
|
"h",
|
||||||
|
"a",
|
||||||
|
"c",
|
||||||
|
"l",
|
||||||
|
"f",
|
||||||
|
"v",
|
||||||
|
"p",
|
||||||
|
"w",
|
||||||
|
"d"
|
||||||
|
],
|
||||||
|
"Fields": [
|
||||||
|
{
|
||||||
|
"Field": "rewardsForKeys",
|
||||||
|
"Slot": "lru_1",
|
||||||
|
"Kotlin": "RewardsForKeys",
|
||||||
|
"Default": "md()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "currentContainerType",
|
||||||
|
"Slot": "mru_1",
|
||||||
|
"Kotlin": "CurrentContainerType",
|
||||||
|
"Default": "Ly()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "keys",
|
||||||
|
"Slot": "nru_1",
|
||||||
|
"Kotlin": "Keys",
|
||||||
|
"Default": "ag()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "containerNames",
|
||||||
|
"Slot": "oru_1",
|
||||||
|
"Kotlin": "ContainerNames",
|
||||||
|
"Default": "ag()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "containerDescriptions",
|
||||||
|
"Slot": "pru_1",
|
||||||
|
"Kotlin": "ContainerDescriptions",
|
||||||
|
"Default": "ag()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "rewards",
|
||||||
|
"Slot": "qru_1",
|
||||||
|
"Kotlin": "Rewards",
|
||||||
|
"Default": "md()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "shownRewards",
|
||||||
|
"Slot": "rru_1",
|
||||||
|
"Kotlin": "ShownRewards",
|
||||||
|
"Default": "md()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "gotoGarage",
|
||||||
|
"Slot": "sru_1",
|
||||||
|
"Kotlin": "GotoGarage",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "timeToGiveLootBox",
|
||||||
|
"Slot": "tru_1",
|
||||||
|
"Kotlin": "TimeToGiveLootBox",
|
||||||
|
"Default": "new Dd(0, 0)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "periodOfGiveLootBoxInSec",
|
||||||
|
"Slot": "uru_1",
|
||||||
|
"Kotlin": "PeriodOfGiveLootBoxInSec",
|
||||||
|
"Default": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "openingFailed",
|
||||||
|
"Slot": "vru_1",
|
||||||
|
"Kotlin": "OpeningFailed",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "adsAvailable",
|
||||||
|
"Slot": "wru_1",
|
||||||
|
"Kotlin": "AdsAvailable",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "currentCategory",
|
||||||
|
"Slot": "xru_1",
|
||||||
|
"Kotlin": "CurrentCategory",
|
||||||
|
"Default": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "isOpening",
|
||||||
|
"Slot": "yru_1",
|
||||||
|
"Kotlin": "IsOpening",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "isAccelerated",
|
||||||
|
"Slot": "zru_1",
|
||||||
|
"Kotlin": "IsAccelerated",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "isShopVisited",
|
||||||
|
"Slot": "arv_1",
|
||||||
|
"Kotlin": "IsShopVisited",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "isReopening",
|
||||||
|
"Slot": "brv_1",
|
||||||
|
"Kotlin": "IsReopening",
|
||||||
|
"Default": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "isBlockOpenButtons",
|
||||||
|
"Slot": "crv_1",
|
||||||
|
"Kotlin": "IsBlockOpenButtons",
|
||||||
|
"Default": "false"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"CopyMethod": null,
|
||||||
|
"Branches": [
|
||||||
|
{
|
||||||
|
"Guard": "(具名 reducer)",
|
||||||
|
"Locals": [],
|
||||||
|
"Kind": "named",
|
||||||
|
"Expr": "ws.wrv(t, n.wpv_1)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"SubReducers": [],
|
||||||
|
"Counts": {
|
||||||
|
"TotalInstanceof": null,
|
||||||
|
"TopLevelBranches": 1,
|
||||||
|
"SubReducerArms": 0
|
||||||
|
},
|
||||||
|
"Locals": {},
|
||||||
|
"Delegate": "ws.wrv(t, n.wpv_1)"
|
||||||
|
}
|
||||||
@@ -569,6 +569,11 @@ function InferDeep(Expr, Mod, Off, Depth) {
|
|||||||
// 类型定义见 TankiOffline/.../App/State/ScreenId.kt。
|
// 类型定义见 TankiOffline/.../App/State/ScreenId.kt。
|
||||||
const FieldTypeOverrides = new Map([
|
const FieldTypeOverrides = new Map([
|
||||||
['GoTo.screen', 'ScreenId'],
|
['GoTo.screen', 'ScreenId'],
|
||||||
|
// lootBoxes 切片:lH/qH/yH 的字段在别处的调用点只能给出 Boolean / Any?,但 reducer 里分别是
|
||||||
|
// ContainerType(默认 Ly()=STANDARD)、可空 ContainerType、及补给箱奖励列表。
|
||||||
|
['SetCurrentContainer.type', 'ContainerType'],
|
||||||
|
['SelectCategory.containerCategory', 'ContainerType?'],
|
||||||
|
['ShownRewards.rewards', 'List<ContainerGivenItem>'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const Emitted = [];
|
const Emitted = [];
|
||||||
|
|||||||
@@ -68,6 +68,13 @@ const AnonymousActionNames = new Map([
|
|||||||
['lY', 'EnableBack'],
|
['lY', 'EnableBack'],
|
||||||
['cY', 'DisableBack'],
|
['cY', 'DisableBack'],
|
||||||
['fY', 'PopScreen'],
|
['fY', 'PopScreen'],
|
||||||
|
// 补给箱切片(aH / EH.wrv):没有 toString / 不在 action_inventory 的 6 个类。
|
||||||
|
['mH', 'SetTimeToGiveLootBox'],
|
||||||
|
['kH', 'SetPeriodOfGiveLootBox'],
|
||||||
|
['zH', 'ClearRewards'],
|
||||||
|
['gH', 'LootBoxOpeningFailed'],
|
||||||
|
['CH', 'SetIsReopening'],
|
||||||
|
['SH', 'SetIsBlockOpenButtons'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||||
@@ -433,6 +440,7 @@ const Migrated = {
|
|||||||
localBattleUserState: { Type: 'LocalBattleUserStateSlice', Reduce: 'ReduceLocalBattleUser' },
|
localBattleUserState: { Type: 'LocalBattleUserStateSlice', Reduce: 'ReduceLocalBattleUser' },
|
||||||
shop: { Type: 'ShopStateSlice', Reduce: 'ReduceShop' },
|
shop: { Type: 'ShopStateSlice', Reduce: 'ReduceShop' },
|
||||||
screen: { Type: 'ScreenStateSlice', Reduce: 'ReduceScreen' },
|
screen: { Type: 'ScreenStateSlice', Reduce: 'ReduceScreen' },
|
||||||
|
lootBoxes: { Type: 'LootBoxesStateSlice', Reduce: 'ReduceLootBoxes' },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||||
@@ -487,6 +495,16 @@ const ScreenAnonymousEffects = {
|
|||||||
fY: 'screenStack = dropLast(1)(`f$(stack, 1)` 是 Kotlin dropLast)',
|
fY: 'screenStack = dropLast(1)(`f$(stack, 1)` 是 Kotlin dropLast)',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 补给箱切片(aH / EH.wrv)的匿名动作:按 reducer 分支效果定名。
|
||||||
|
const LootBoxesAnonymousEffects = {
|
||||||
|
mH: 'timeToGiveLootBox = Date.now() + seconds * 1000(第 9 个构造参数 `Fg(Md(), Qk.y2o(Wg(t.zrv_1)))`)',
|
||||||
|
kH: 'periodOfGiveLootBoxInSec = action.yrv_1(第 10 个构造参数)',
|
||||||
|
zH: 'rewards = emptyList()、shownRewards = emptyList()(第 6、7 个构造参数 `md(), md()`)',
|
||||||
|
gH: 'openingFailed = true(第 11 个构造参数 !0)',
|
||||||
|
CH: 'isReopening = true(第 17 个构造参数 !0)',
|
||||||
|
SH: 'isBlockOpenButtons = action.xrv_1(第 18 个构造参数)',
|
||||||
|
};
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(P, 'names', 'root_wiring.json'),
|
path.join(P, 'names', 'root_wiring.json'),
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
@@ -515,6 +533,12 @@ fs.writeFileSync(
|
|||||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||||
Effect,
|
Effect,
|
||||||
})),
|
})),
|
||||||
|
...Object.entries(LootBoxesAnonymousEffects).map(([Local, Effect]) => ({
|
||||||
|
Local,
|
||||||
|
Name: AnonymousActionNames.get(Local),
|
||||||
|
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||||
|
Effect,
|
||||||
|
})),
|
||||||
],
|
],
|
||||||
Markers: [
|
Markers: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -346,14 +346,14 @@ object ActionCatalog {
|
|||||||
ActionMeta("SetIcons", SetIcons::class, listOf(ActionFieldMeta("icons", "List<IconData>"))),
|
ActionMeta("SetIcons", SetIcons::class, listOf(ActionFieldMeta("icons", "List<IconData>"))),
|
||||||
ActionMeta("LootBoxes", LootBoxes::class, listOf(ActionFieldMeta("rewardsForKeys", "Any?"), ActionFieldMeta("currentContainerType", "Any?"), ActionFieldMeta("keys", "Any?"), ActionFieldMeta("containerNames", "Any?"), ActionFieldMeta("containerDescriptions", "Any?"), ActionFieldMeta("rewards", "Any?"), ActionFieldMeta("shownRewards", "Any?"), ActionFieldMeta("gotoGarage", "Any?"), ActionFieldMeta("timeToGiveLootBox", "Any?"), ActionFieldMeta("periodOfGiveLootBoxInSec", "Any?"), ActionFieldMeta("openingFailed", "Any?"), ActionFieldMeta("adsAvailable", "Any?"), ActionFieldMeta("currentCategory", "Any?"), ActionFieldMeta("isOpening", "Any?"), ActionFieldMeta("isAccelerated", "Any?"), ActionFieldMeta("isShopVisited", "Any?"), ActionFieldMeta("isReopening", "Any?"), ActionFieldMeta("isBlockOpenButtons", "Any?"))),
|
ActionMeta("LootBoxes", LootBoxes::class, listOf(ActionFieldMeta("rewardsForKeys", "Any?"), ActionFieldMeta("currentContainerType", "Any?"), ActionFieldMeta("keys", "Any?"), ActionFieldMeta("containerNames", "Any?"), ActionFieldMeta("containerDescriptions", "Any?"), ActionFieldMeta("rewards", "Any?"), ActionFieldMeta("shownRewards", "Any?"), ActionFieldMeta("gotoGarage", "Any?"), ActionFieldMeta("timeToGiveLootBox", "Any?"), ActionFieldMeta("periodOfGiveLootBoxInSec", "Any?"), ActionFieldMeta("openingFailed", "Any?"), ActionFieldMeta("adsAvailable", "Any?"), ActionFieldMeta("currentCategory", "Any?"), ActionFieldMeta("isOpening", "Any?"), ActionFieldMeta("isAccelerated", "Any?"), ActionFieldMeta("isShopVisited", "Any?"), ActionFieldMeta("isReopening", "Any?"), ActionFieldMeta("isBlockOpenButtons", "Any?"))),
|
||||||
ActionMeta("SetPossibleRewards", SetPossibleRewards::class, listOf(ActionFieldMeta("rewards", "Any?"))),
|
ActionMeta("SetPossibleRewards", SetPossibleRewards::class, listOf(ActionFieldMeta("rewards", "Any?"))),
|
||||||
ActionMeta("SetCurrentContainer", SetCurrentContainer::class, listOf(ActionFieldMeta("type", "Boolean"))),
|
ActionMeta("SetCurrentContainer", SetCurrentContainer::class, listOf(ActionFieldMeta("type", "ContainerType"))),
|
||||||
ActionMeta("SetKeys", SetKeys::class, listOf(ActionFieldMeta("keys", "Map<String, Int>"))),
|
ActionMeta("SetKeys", SetKeys::class, listOf(ActionFieldMeta("keys", "Map<String, Int>"))),
|
||||||
ActionMeta("AddKeys", AddKeys::class, listOf(ActionFieldMeta("keyType", "String"), ActionFieldMeta("delta", "Int"))),
|
ActionMeta("AddKeys", AddKeys::class, listOf(ActionFieldMeta("keyType", "String"), ActionFieldMeta("delta", "Int"))),
|
||||||
ActionMeta("SetContainerData", SetContainerData::class, listOf(ActionFieldMeta("type", "String"), ActionFieldMeta("name", "String"), ActionFieldMeta("description", "String"))),
|
ActionMeta("SetContainerData", SetContainerData::class, listOf(ActionFieldMeta("type", "String"), ActionFieldMeta("name", "String"), ActionFieldMeta("description", "String"))),
|
||||||
ActionMeta("SetGoToGarage", SetGoToGarage::class, listOf(ActionFieldMeta("gotoGarage", "Boolean"))),
|
ActionMeta("SetGoToGarage", SetGoToGarage::class, listOf(ActionFieldMeta("gotoGarage", "Boolean"))),
|
||||||
ActionMeta("OpenContainer", OpenContainer::class, listOf(ActionFieldMeta("amount", "Any?"), ActionFieldMeta("type", "Any?"))),
|
ActionMeta("OpenContainer", OpenContainer::class, listOf(ActionFieldMeta("amount", "Any?"), ActionFieldMeta("type", "Any?"))),
|
||||||
ActionMeta("SelectCategory", SelectCategory::class, listOf(ActionFieldMeta("containerCategory", "Any?"))),
|
ActionMeta("SelectCategory", SelectCategory::class, listOf(ActionFieldMeta("containerCategory", "ContainerType?"))),
|
||||||
ActionMeta("ShownRewards", ShownRewards::class, listOf(ActionFieldMeta("rewards", "Any?"))),
|
ActionMeta("ShownRewards", ShownRewards::class, listOf(ActionFieldMeta("rewards", "List<ContainerGivenItem>"))),
|
||||||
ActionMeta("SetIsOpening", SetIsOpening::class, listOf(ActionFieldMeta("isOpening", "Boolean"))),
|
ActionMeta("SetIsOpening", SetIsOpening::class, listOf(ActionFieldMeta("isOpening", "Boolean"))),
|
||||||
ActionMeta("SetIsAccelerated", SetIsAccelerated::class, listOf(ActionFieldMeta("isAccelerated", "Boolean"))),
|
ActionMeta("SetIsAccelerated", SetIsAccelerated::class, listOf(ActionFieldMeta("isAccelerated", "Boolean"))),
|
||||||
ActionMeta("SetIsShopVisited", SetIsShopVisited::class, listOf(ActionFieldMeta("isShopVisited", "Boolean"))),
|
ActionMeta("SetIsShopVisited", SetIsShopVisited::class, listOf(ActionFieldMeta("isShopVisited", "Boolean"))),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package App.State
|
|||||||
|
|
||||||
import Core.Vector3
|
import Core.Vector3
|
||||||
|
|
||||||
// 动作/状态类共 816 个,字段 2150 个(已定类型 509 个)
|
// 动作/状态类共 816 个,字段 2150 个(已定类型 511 个)
|
||||||
data class AnnouncementState(
|
data class AnnouncementState(
|
||||||
val isEnabled: Any?, // 仅 copy 转发(无类型信息)
|
val isEnabled: Any?, // 仅 copy 转发(无类型信息)
|
||||||
val modelLoaded: Any?, // 仅 copy 转发(无类型信息)
|
val modelLoaded: Any?, // 仅 copy 转发(无类型信息)
|
||||||
@@ -2019,7 +2019,7 @@ data class SetPossibleRewards(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class SetCurrentContainer(
|
data class SetCurrentContainer(
|
||||||
val type: Boolean, // callsite
|
val type: ContainerType, // override
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetKeys(
|
data class SetKeys(
|
||||||
@@ -2047,11 +2047,11 @@ data class OpenContainer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class SelectCategory(
|
data class SelectCategory(
|
||||||
val containerCategory: Any?, // 调用点: i | null
|
val containerCategory: ContainerType?, // override
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ShownRewards(
|
data class ShownRewards(
|
||||||
val rewards: Any?, // 调用点: t.state.rewards | HT(n)
|
val rewards: List<ContainerGivenItem>, // override
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetIsOpening(
|
data class SetIsOpening(
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// LootBoxesModels.kt —— 补给箱切片用到的枚举(App.State 包内,供 Inventory.kt 的字段类型引用)
|
||||||
|
//
|
||||||
|
// 证据链:module_88693 的 `Vt()` 初始化
|
||||||
|
// `(u = new Kt("STANDARD", 0)), (s = new Kt("RARE", 1)), (o = new Kt("EPIC", 2)), (_ = new Kt("LEGENDARY", 3))`
|
||||||
|
// `Yt()` 返回 `u`(STANDARD)——module_10032 的 `Ly()` 正是 `Yt`,即 aH.currentContainerType 的默认值。
|
||||||
|
package App.State
|
||||||
|
|
||||||
|
/** 原 `Kt`:补给箱类型,顺序与枚举 ordinal 一致(STANDARD=0 … LEGENDARY=3)。 */
|
||||||
|
enum class ContainerType {
|
||||||
|
STANDARD,
|
||||||
|
RARE,
|
||||||
|
EPIC,
|
||||||
|
LEGENDARY,
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** 运行期是 JS 枚举对象,toString 就是成员名;认不出时与原包默认值一致落到 STANDARD。 */
|
||||||
|
fun FromText(Text: Any?): ContainerType =
|
||||||
|
entries.firstOrNull { it.name == Text?.toString() } ?: STANDARD
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
// LootBoxesReducer.kt —— 补给箱切片(对应 module_10032 的 aH 状态 + EH.wrv reducer)
|
||||||
|
//
|
||||||
|
// 证据链:
|
||||||
|
// 状态类 aH:`function aH(t, n, i, r, e, u, s, o, _, h, a, c, l, f, v, p, w, d)`,18 个字段,
|
||||||
|
// toString 名为 `LootBoxes(rewardsForKeys=…, currentContainerType=…, …)`。默认值取自构造:
|
||||||
|
// rewardsForKeys = md()(emptyList)、currentContainerType = Ly()(ContainerType.STANDARD,
|
||||||
|
// 见 module_88693 的 `Vt()` / `Yt()`)、keys / containerNames / containerDescriptions = ag()(emptyMap)、
|
||||||
|
// rewards / shownRewards = md()、gotoGarage = false、timeToGiveLootBox = new Dd(0, 0)(Long 0)、
|
||||||
|
// periodOfGiveLootBoxInSec = 0、openingFailed / adsAvailable = false、currentCategory = null、
|
||||||
|
// 其余布尔 = false。
|
||||||
|
// reducer:`ld(EH).wrv`(module_10032 第 33150 行)18 个 instanceof 臂:
|
||||||
|
//
|
||||||
|
// cH SetPossibleRewards → rewardsForKeys = action.rewards
|
||||||
|
// lH SetCurrentContainer → currentContainerType = action.type
|
||||||
|
// fH SetKeys → keys = action.keys
|
||||||
|
// vH AddKeys → keys[action.keyType] += action.delta(原 `nm` 是 Map.getValue,
|
||||||
|
// 缺键抛 "Key … is missing in the map.";加法按 32 位 Int 截断)
|
||||||
|
// pH SetContainerData → containerNames[type] = name、containerDescriptions[type] = description
|
||||||
|
// (原 `cg(map, Vd(k, v))` 是 Map.plus(Pair))
|
||||||
|
// wH SetGoToGarage → gotoGarage = action.gotoGarage
|
||||||
|
// bH SetRewards → rewards = action.rewards,同时 isReopening = false(第 17 个构造参数 !1)
|
||||||
|
// mH SetTimeToGiveLootBox → timeToGiveLootBox = Date.now() + action.seconds * 1000
|
||||||
|
// (原 `Fg(Md(), Qk.y2o(Wg(sec)))`:Md()=Date.now()、Wg=Int→Long、
|
||||||
|
// Qk.y2o=秒→毫秒、Fg=Long 加法)
|
||||||
|
// kH SetPeriodOfGiveLootBox → periodOfGiveLootBoxInSec = action.periodOfGiveLootBoxInSec
|
||||||
|
// zH ClearRewards → rewards = emptyList()、shownRewards = emptyList()
|
||||||
|
// gH LootBoxOpeningFailed → openingFailed = true
|
||||||
|
// qH SelectCategory → currentCategory = action.containerCategory
|
||||||
|
// yH ShownRewards → shownRewards += action.rewards(原 `Im(list, list)` 是 List.plus)
|
||||||
|
// $H SetIsOpening → isOpening = action.isOpening
|
||||||
|
// xH SetIsAccelerated → isAccelerated = action.isAccelerated
|
||||||
|
// jH SetIsShopVisited → isShopVisited = action.isShopVisited
|
||||||
|
// CH SetIsReopening → isReopening = true
|
||||||
|
// SH SetIsBlockOpenButtons → isBlockOpenButtons = action.isBlockOpenButtons
|
||||||
|
//
|
||||||
|
// 无 toString / 不在 action_inventory 的 6 个类按 reducer 效果定名,原名写在注释里;名字同时登记在
|
||||||
|
// 3DTank/tools/gen_root_wiring.js 的 AnonymousActionNames,重新生成 RootWiring 后显示。
|
||||||
|
package App.State.Reducers
|
||||||
|
|
||||||
|
import App.State.AddKeys
|
||||||
|
import App.State.ContainerGivenItem
|
||||||
|
import App.State.ContainerType
|
||||||
|
import App.State.SelectCategory
|
||||||
|
import App.State.SetContainerData
|
||||||
|
import App.State.SetCurrentContainer
|
||||||
|
import App.State.SetGoToGarage
|
||||||
|
import App.State.SetIsAccelerated
|
||||||
|
import App.State.SetIsOpening
|
||||||
|
import App.State.SetIsShopVisited
|
||||||
|
import App.State.SetKeys
|
||||||
|
import App.State.SetPossibleRewards
|
||||||
|
import App.State.SetRewards
|
||||||
|
import App.State.ShownRewards
|
||||||
|
|
||||||
|
/** 原 `mH`:补给箱倒计时(秒);归约后写到 timeToGiveLootBox = now + seconds * 1000。 */
|
||||||
|
data class SetTimeToGiveLootBox(val seconds: Int)
|
||||||
|
|
||||||
|
/** 原 `kH`:补给箱发放周期(秒)。 */
|
||||||
|
data class SetPeriodOfGiveLootBox(val periodOfGiveLootBoxInSec: Int)
|
||||||
|
|
||||||
|
/** 原 `zH`:无字段;清空 rewards 与 shownRewards。 */
|
||||||
|
object ClearRewards
|
||||||
|
|
||||||
|
/** 原 `gH`:无字段;openingFailed = true。 */
|
||||||
|
object LootBoxOpeningFailed
|
||||||
|
|
||||||
|
/** 原 `CH`:无字段;isReopening = true。 */
|
||||||
|
object SetIsReopening
|
||||||
|
|
||||||
|
/** 原 `SH`:isBlockOpenButtons = action.xrv_1。 */
|
||||||
|
data class SetIsBlockOpenButtons(val isBlockOpenButtons: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原 `aH` / `LootBoxes`:`LootBoxes(rewardsForKeys, currentContainerType, keys, containerNames,
|
||||||
|
* containerDescriptions, rewards, shownRewards, gotoGarage, timeToGiveLootBox, periodOfGiveLootBoxInSec,
|
||||||
|
* openingFailed, adsAvailable, currentCategory, isOpening, isAccelerated, isShopVisited, isReopening,
|
||||||
|
* isBlockOpenButtons)`。
|
||||||
|
*/
|
||||||
|
data class LootBoxesStateSlice(
|
||||||
|
/** 本 reducer 只 copy 转发、从不读;原构造默认 md()(emptyList),元素类型没有证据,保留 Any?。 */
|
||||||
|
val RewardsForKeys: Any? = emptyList<ContainerGivenItem>(),
|
||||||
|
val CurrentContainerType: ContainerType = ContainerType.STANDARD,
|
||||||
|
val Keys: Map<String, Int> = emptyMap(),
|
||||||
|
val ContainerNames: Map<String, String> = emptyMap(),
|
||||||
|
val ContainerDescriptions: Map<String, String> = emptyMap(),
|
||||||
|
val Rewards: List<ContainerGivenItem> = emptyList(),
|
||||||
|
val ShownRewards: List<ContainerGivenItem> = emptyList(),
|
||||||
|
val GotoGarage: Boolean = false,
|
||||||
|
val TimeToGiveLootBox: Long = 0L,
|
||||||
|
val PeriodOfGiveLootBoxInSec: Int = 0,
|
||||||
|
val OpeningFailed: Boolean = false,
|
||||||
|
val AdsAvailable: Boolean = false,
|
||||||
|
val CurrentCategory: ContainerType? = null,
|
||||||
|
val IsOpening: Boolean = false,
|
||||||
|
val IsAccelerated: Boolean = false,
|
||||||
|
val IsShopVisited: Boolean = false,
|
||||||
|
val IsReopening: Boolean = false,
|
||||||
|
val IsBlockOpenButtons: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 原 `Md()`:`ve(Date.now())`,即当前毫秒(Long)。 */
|
||||||
|
private fun CurrentTimeMillis(): Long = kotlin.js.Date.now().toLong()
|
||||||
|
|
||||||
|
/** 根 reducer 的 lootBoxes 分支(原 `ws.wrv(t, n.wpv_1)`)。 */
|
||||||
|
fun ReduceLootBoxes(State0: LootBoxesStateSlice, A: Any): LootBoxesStateSlice = when (A) {
|
||||||
|
is SetPossibleRewards -> State0.copy(RewardsForKeys = A.rewards)
|
||||||
|
is SetCurrentContainer -> State0.copy(CurrentContainerType = A.type)
|
||||||
|
is SetKeys -> State0.copy(Keys = A.keys)
|
||||||
|
|
||||||
|
is AddKeys -> State0.copy(
|
||||||
|
Keys = State0.Keys + (A.keyType to (State0.Keys.getValue(A.keyType) + A.delta))
|
||||||
|
)
|
||||||
|
|
||||||
|
is SetContainerData -> State0.copy(
|
||||||
|
ContainerNames = State0.ContainerNames + (A.type to A.name),
|
||||||
|
ContainerDescriptions = State0.ContainerDescriptions + (A.type to A.description),
|
||||||
|
)
|
||||||
|
|
||||||
|
is SetGoToGarage -> State0.copy(GotoGarage = A.gotoGarage)
|
||||||
|
is SetRewards -> State0.copy(Rewards = A.rewards, IsReopening = false)
|
||||||
|
is SetTimeToGiveLootBox -> State0.copy(
|
||||||
|
TimeToGiveLootBox = CurrentTimeMillis() + A.seconds.toLong() * 1000L
|
||||||
|
)
|
||||||
|
is SetPeriodOfGiveLootBox -> State0.copy(PeriodOfGiveLootBoxInSec = A.periodOfGiveLootBoxInSec)
|
||||||
|
is ClearRewards -> State0.copy(Rewards = emptyList(), ShownRewards = emptyList())
|
||||||
|
is LootBoxOpeningFailed -> State0.copy(OpeningFailed = true)
|
||||||
|
is SelectCategory -> State0.copy(CurrentCategory = A.containerCategory)
|
||||||
|
is ShownRewards -> State0.copy(ShownRewards = State0.ShownRewards + A.rewards)
|
||||||
|
is SetIsOpening -> State0.copy(IsOpening = A.isOpening)
|
||||||
|
is SetIsAccelerated -> State0.copy(IsAccelerated = A.isAccelerated)
|
||||||
|
is SetIsShopVisited -> State0.copy(IsShopVisited = A.isShopVisited)
|
||||||
|
is SetIsReopening -> State0.copy(IsReopening = true)
|
||||||
|
is SetIsBlockOpenButtons -> State0.copy(IsBlockOpenButtons = A.isBlockOpenButtons)
|
||||||
|
else -> State0
|
||||||
|
}
|
||||||
@@ -10,8 +10,9 @@
|
|||||||
// localBattleUserState → ReduceLocalBattleUser(lE / `(inline@localBattleUserState)` 分支)
|
// localBattleUserState → ReduceLocalBattleUser(lE / `(inline@localBattleUserState)` 分支)
|
||||||
// shop → ReduceShop(j4 / `(inline@shop)` 分支)
|
// shop → ReduceShop(j4 / `(inline@shop)` 分支)
|
||||||
// screen → ReduceScreen(WK / dY().ks5)
|
// screen → ReduceScreen(WK / dY().ks5)
|
||||||
|
// lootBoxes → ReduceLootBoxes(aH / EH.wrv)
|
||||||
//
|
//
|
||||||
// 其余 82 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
// 其余 81 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||||
package App.State
|
package App.State
|
||||||
@@ -20,6 +21,7 @@ import App.State.Reducers.ReduceHome
|
|||||||
import App.State.Reducers.ReduceInvite
|
import App.State.Reducers.ReduceInvite
|
||||||
import App.State.Reducers.ReduceLocalBattleUser
|
import App.State.Reducers.ReduceLocalBattleUser
|
||||||
import App.State.Reducers.ReduceLoginByPassword
|
import App.State.Reducers.ReduceLoginByPassword
|
||||||
|
import App.State.Reducers.ReduceLootBoxes
|
||||||
import App.State.Reducers.ReduceScreen
|
import App.State.Reducers.ReduceScreen
|
||||||
import App.State.Reducers.ReduceShop
|
import App.State.Reducers.ReduceShop
|
||||||
import App.State.Reducers.ReduceTwitch
|
import App.State.Reducers.ReduceTwitch
|
||||||
@@ -34,7 +36,7 @@ object RootReducer {
|
|||||||
val Coverage: String get() = "已迁移 ${Migrated.size}/${RootWiring.All.size}"
|
val Coverage: String get() = "已迁移 ${Migrated.size}/${RootWiring.All.size}"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 归约一次。未迁移字段原样带过去(`copy` 只改已迁移的三个)。
|
* 归约一次。未迁移字段原样带过去(`copy` 只改已迁移的字段)。
|
||||||
*/
|
*/
|
||||||
fun Reduce(State0: RootState, Action: Any): RootState = State0.copy(
|
fun Reduce(State0: RootState, Action: Any): RootState = State0.copy(
|
||||||
InviteState = ReduceInvite(State0.InviteState, Action),
|
InviteState = ReduceInvite(State0.InviteState, Action),
|
||||||
@@ -44,6 +46,7 @@ object RootReducer {
|
|||||||
LocalBattleUserState = ReduceLocalBattleUser(State0.LocalBattleUserState, Action),
|
LocalBattleUserState = ReduceLocalBattleUser(State0.LocalBattleUserState, Action),
|
||||||
Shop = ReduceShop(State0.Shop, Action),
|
Shop = ReduceShop(State0.Shop, Action),
|
||||||
Screen = ReduceScreen(State0.Screen, Action),
|
Screen = ReduceScreen(State0.Screen, Action),
|
||||||
|
LootBoxes = ReduceLootBoxes(State0.LootBoxes, Action),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import App.State.Reducers.HomeStateSlice
|
|||||||
import App.State.Reducers.InviteStateSlice
|
import App.State.Reducers.InviteStateSlice
|
||||||
import App.State.Reducers.LocalBattleUserStateSlice
|
import App.State.Reducers.LocalBattleUserStateSlice
|
||||||
import App.State.Reducers.LoginByPasswordStateSlice
|
import App.State.Reducers.LoginByPasswordStateSlice
|
||||||
|
import App.State.Reducers.LootBoxesStateSlice
|
||||||
import App.State.Reducers.ScreenStateSlice
|
import App.State.Reducers.ScreenStateSlice
|
||||||
import App.State.Reducers.ShopStateSlice
|
import App.State.Reducers.ShopStateSlice
|
||||||
import App.State.Reducers.TwitchStateSlice
|
import App.State.Reducers.TwitchStateSlice
|
||||||
@@ -65,8 +66,8 @@ data class RootState(
|
|||||||
val Settings: Any? = null,
|
val Settings: Any? = null,
|
||||||
/** 原 accountSettings(oet / Lp.mtg)。未迁移:原样透传。 */
|
/** 原 accountSettings(oet / Lp.mtg)。未迁移:原样透传。 */
|
||||||
val AccountSettings: Any? = null,
|
val AccountSettings: Any? = null,
|
||||||
/** 原 lootBoxes(aH / ws.wrv)。未迁移:原样透传。 */
|
/** 原 lootBoxes(aH / ws.wrv)。已迁移。 */
|
||||||
val LootBoxes: Any? = null,
|
val LootBoxes: LootBoxesStateSlice = LootBoxesStateSlice(),
|
||||||
/** 原 notifications(? / B_.ise)。未迁移:原样透传。 */
|
/** 原 notifications(? / B_.ise)。未迁移:原样透传。 */
|
||||||
val Notifications: Any? = null,
|
val Notifications: Any? = null,
|
||||||
/** 原 friends(KD / NL().cr9)。未迁移:原样透传。 */
|
/** 原 friends(KD / NL().cr9)。未迁移:原样透传。 */
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ object RootWiring {
|
|||||||
SliceWiring("news", "tpv_1", "ZX", "E_.asd", "E_.asd(t, n.tpv_1)", listOf("?hJ", "AddNewsItemAction", "RemoveNewsItemAction", "SetNewsItemAction", "SetUnreadNewsCounter")),
|
SliceWiring("news", "tpv_1", "ZX", "E_.asd", "E_.asd(t, n.tpv_1)", listOf("?hJ", "AddNewsItemAction", "RemoveNewsItemAction", "SetNewsItemAction", "SetUnreadNewsCounter")),
|
||||||
SliceWiring("settings", "upv_1", "Uut", "xw.ltg", "xw.ltg(t, n.upv_1)", listOf("?Fut", "?Gut")),
|
SliceWiring("settings", "upv_1", "Uut", "xw.ltg", "xw.ltg(t, n.upv_1)", listOf("?Fut", "?Gut")),
|
||||||
SliceWiring("accountSettings", "vpv_1", "oet", "Lp.mtg", "Lp.mtg(t, n.vpv_1)", listOf("?aet", "?cet", "?det", "?fet", "?get", "?ket", "?met", "?pet", "?vet", "?wet", "?xet", "ClearInputUid", "SetAccountRemovalDays")),
|
SliceWiring("accountSettings", "vpv_1", "oet", "Lp.mtg", "Lp.mtg(t, n.vpv_1)", listOf("?aet", "?cet", "?det", "?fet", "?get", "?ket", "?met", "?pet", "?vet", "?wet", "?xet", "ClearInputUid", "SetAccountRemovalDays")),
|
||||||
SliceWiring("lootBoxes", "wpv_1", "aH", "ws.wrv", "ws.wrv(t, n.wpv_1)", listOf("?CH", "?SH", "?gH", "?kH", "?mH", "?zH", "AddKeys", "SelectCategory", "SetContainerData", "SetCurrentContainer", "SetGoToGarage", "SetIsAccelerated", "SetIsOpening", "SetIsShopVisited", "SetKeys", "SetPossibleRewards", "SetRewards", "ShownRewards")),
|
SliceWiring("lootBoxes", "wpv_1", "aH", "ws.wrv", "ws.wrv(t, n.wpv_1)", listOf("AddKeys", "ClearRewards", "LootBoxOpeningFailed", "SelectCategory", "SetContainerData", "SetCurrentContainer", "SetGoToGarage", "SetIsAccelerated", "SetIsBlockOpenButtons", "SetIsOpening", "SetIsReopening", "SetIsShopVisited", "SetKeys", "SetPeriodOfGiveLootBox", "SetPossibleRewards", "SetRewards", "SetTimeToGiveLootBox", "ShownRewards")),
|
||||||
SliceWiring("notifications", "xpv_1", "?", "B_.ise", "B_.ise(t, n.xpv_1)", listOf("?PJ", "?RJ", "AddNotification", "InitNotificationSound")),
|
SliceWiring("notifications", "xpv_1", "?", "B_.ise", "B_.ise(t, n.xpv_1)", listOf("?PJ", "?RJ", "AddNotification", "InitNotificationSound")),
|
||||||
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("?_L", "?hL", "AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetLimits", "SetValidationStatus")),
|
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("?_L", "?hL", "AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetLimits", "SetValidationStatus")),
|
||||||
SliceWiring("remoteUserDataStorage", "zpv_1", "\$Q", "vh.isl", "vh.isl(t, n.zpv_1)", listOf("UpdateClanTags", "UpdateOnlineStatus", "UpdatePremiumStatus", "UpdateRanks", "UpdateUids", "UserEnterBattle", "UserLeaveBattle", "UserLeaveGroup")),
|
SliceWiring("remoteUserDataStorage", "zpv_1", "\$Q", "vh.isl", "vh.isl(t, n.zpv_1)", listOf("UpdateClanTags", "UpdateOnlineStatus", "UpdatePremiumStatus", "UpdateRanks", "UpdateUids", "UserEnterBattle", "UserLeaveBattle", "UserLeaveGroup")),
|
||||||
@@ -106,7 +106,7 @@ object RootWiring {
|
|||||||
)
|
)
|
||||||
|
|
||||||
/** 已迁移(由 RootReducer 真正归约)的字段。 */
|
/** 已迁移(由 RootReducer 真正归约)的字段。 */
|
||||||
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen")
|
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes")
|
||||||
|
|
||||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
|
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
|
||||||
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
|
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
|
||||||
// ⑧ 全目录动作类都能按 KClass 路由
|
// ⑧ 全目录动作类都能按 KClass 路由
|
||||||
// ⑨~⑰ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
// ⑨~⑱ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
||||||
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen;⑭ 根 reducer 的 89 槽位委派
|
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes;⑭ 根 reducer 的 89 槽位委派
|
||||||
//
|
//
|
||||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||||
package App
|
package App
|
||||||
@@ -144,6 +144,28 @@ import App.State.Reducers.PopScreen
|
|||||||
import App.State.Reducers.ReduceScreen
|
import App.State.Reducers.ReduceScreen
|
||||||
import App.State.Reducers.ResetScreenStackOnNextGoTo
|
import App.State.Reducers.ResetScreenStackOnNextGoTo
|
||||||
import App.State.Reducers.ScreenStateSlice
|
import App.State.Reducers.ScreenStateSlice
|
||||||
|
import App.State.AddKeys
|
||||||
|
import App.State.ContainerGivenItem
|
||||||
|
import App.State.ContainerType
|
||||||
|
import App.State.SelectCategory
|
||||||
|
import App.State.SetContainerData
|
||||||
|
import App.State.SetCurrentContainer
|
||||||
|
import App.State.SetGoToGarage
|
||||||
|
import App.State.SetIsAccelerated
|
||||||
|
import App.State.SetIsOpening
|
||||||
|
import App.State.SetIsShopVisited
|
||||||
|
import App.State.SetKeys
|
||||||
|
import App.State.SetPossibleRewards
|
||||||
|
import App.State.SetRewards
|
||||||
|
import App.State.ShownRewards
|
||||||
|
import App.State.Reducers.ClearRewards
|
||||||
|
import App.State.Reducers.LootBoxOpeningFailed
|
||||||
|
import App.State.Reducers.LootBoxesStateSlice
|
||||||
|
import App.State.Reducers.ReduceLootBoxes
|
||||||
|
import App.State.Reducers.SetIsBlockOpenButtons
|
||||||
|
import App.State.Reducers.SetIsReopening
|
||||||
|
import App.State.Reducers.SetPeriodOfGiveLootBox
|
||||||
|
import App.State.Reducers.SetTimeToGiveLootBox
|
||||||
|
|
||||||
private data class AddFrags(val fragsCount: Int)
|
private data class AddFrags(val fragsCount: Int)
|
||||||
private data class SetScreen(val screen: String)
|
private data class SetScreen(val screen: String)
|
||||||
@@ -517,7 +539,7 @@ object StateCheck {
|
|||||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⑭ 根 reducer:89 个槽位的委派表 + 六个已迁移切片一次归约
|
// ⑭ 根 reducer:89 个槽位的委派表 + 八个已迁移切片一次归约
|
||||||
run {
|
run {
|
||||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||||
Check(
|
Check(
|
||||||
@@ -581,13 +603,16 @@ object StateCheck {
|
|||||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||||
)
|
)
|
||||||
Check(
|
Check(
|
||||||
"根:迁移表 = 七个已迁移字段,其余 82 个待迁",
|
"根:迁移表 = 八个已迁移字段,其余 81 个待迁",
|
||||||
RootReducer.Migrated ==
|
RootReducer.Migrated ==
|
||||||
setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen") &&
|
setOf(
|
||||||
RootWiring.Unmigrated.size == 82
|
"inviteState", "loginByPassword", "twitchState", "home",
|
||||||
|
"localBattleUserState", "shop", "screen", "lootBoxes",
|
||||||
|
) &&
|
||||||
|
RootWiring.Unmigrated.size == 81
|
||||||
)
|
)
|
||||||
|
|
||||||
// 一次 ReduceRoot 里六个切片各归约自己的动作,互不串片
|
// 一次 ReduceRoot 里八个切片各归约自己的动作,互不串片
|
||||||
val S0 = RootState(User = "user-1", Screen = ScreenStateSlice(Current = NamedScreen("Preloader")))
|
val S0 = RootState(User = "user-1", Screen = ScreenStateSlice(Current = NamedScreen("Preloader")))
|
||||||
val S1 = ReduceRoot(S0, ApplyInvite("uid-1"))
|
val S1 = ReduceRoot(S0, ApplyInvite("uid-1"))
|
||||||
Check(
|
Check(
|
||||||
@@ -640,7 +665,7 @@ object StateCheck {
|
|||||||
S5.User == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
S5.User == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||||
)
|
)
|
||||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 7/89")
|
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 8/89")
|
||||||
|
|
||||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||||
@@ -1067,6 +1092,164 @@ object StateCheck {
|
|||||||
).all { it in W.Actions }
|
).all { it in W.Actions }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ⑱ 切片 reducer:LootBoxes(原 module_10032 的 aH / EH.wrv,18 个 instanceof 臂)
|
||||||
|
run {
|
||||||
|
val Item = { Name: String ->
|
||||||
|
ContainerGivenItem("INVENTORY", 1, "INVENTORY", 10007277, null, Name, "mine")
|
||||||
|
}
|
||||||
|
val S0 = LootBoxesStateSlice()
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:默认值与原构造一致",
|
||||||
|
S0.RewardsForKeys == emptyList<ContainerGivenItem>() &&
|
||||||
|
S0.CurrentContainerType == ContainerType.STANDARD &&
|
||||||
|
S0.Keys.isEmpty() && S0.ContainerNames.isEmpty() &&
|
||||||
|
S0.ContainerDescriptions.isEmpty() && S0.Rewards.isEmpty() &&
|
||||||
|
S0.ShownRewards.isEmpty() && !S0.GotoGarage && S0.TimeToGiveLootBox == 0L &&
|
||||||
|
S0.PeriodOfGiveLootBoxInSec == 0 && !S0.OpeningFailed && !S0.AdsAvailable &&
|
||||||
|
S0.CurrentCategory == null && !S0.IsOpening && !S0.IsAccelerated &&
|
||||||
|
!S0.IsShopVisited && !S0.IsReopening && !S0.IsBlockOpenButtons
|
||||||
|
)
|
||||||
|
|
||||||
|
val Rewards = listOf(Item("mine"))
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetPossibleRewards / SetCurrentContainer 各写本字段",
|
||||||
|
ReduceLootBoxes(S0, SetPossibleRewards(Rewards)).RewardsForKeys == Rewards &&
|
||||||
|
ReduceLootBoxes(S0, SetCurrentContainer(ContainerType.EPIC))
|
||||||
|
.CurrentContainerType == ContainerType.EPIC &&
|
||||||
|
ReduceLootBoxes(S0, SetCurrentContainer(ContainerType.EPIC))
|
||||||
|
.CurrentContainerType != ContainerType.RARE
|
||||||
|
)
|
||||||
|
|
||||||
|
val Keyed = ReduceLootBoxes(S0, SetKeys(mapOf("STANDARD" to 3)))
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetKeys 覆盖整张表",
|
||||||
|
Keyed.Keys == mapOf("STANDARD" to 3) &&
|
||||||
|
ReduceLootBoxes(S0, SetKeys(emptyMap())).Keys.isEmpty()
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:AddKeys 在原值上加 delta",
|
||||||
|
ReduceLootBoxes(Keyed, AddKeys("STANDARD", 2)).Keys == mapOf("STANDARD" to 5) &&
|
||||||
|
ReduceLootBoxes(Keyed, AddKeys("STANDARD", -3)).Keys == mapOf("STANDARD" to 0)
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:AddKeys 缺键抛错(原 nm 是 Map.getValue)",
|
||||||
|
runCatching { ReduceLootBoxes(Keyed, AddKeys("MISSING", 1)) }.isFailure
|
||||||
|
)
|
||||||
|
|
||||||
|
val Described = ReduceLootBoxes(S0, SetContainerData("STANDARD", "标准箱", "普通补给箱"))
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetContainerData 同时写名字与描述两张表",
|
||||||
|
Described.ContainerNames == mapOf("STANDARD" to "标准箱") &&
|
||||||
|
Described.ContainerDescriptions == mapOf("STANDARD" to "普通补给箱") &&
|
||||||
|
Described.Keys.isEmpty() && Described.Rewards.isEmpty()
|
||||||
|
)
|
||||||
|
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetGoToGarage 写 gotoGarage",
|
||||||
|
ReduceLootBoxes(S0, SetGoToGarage(true)).GotoGarage &&
|
||||||
|
!ReduceLootBoxes(S0.copy(GotoGarage = true), SetGoToGarage(false)).GotoGarage
|
||||||
|
)
|
||||||
|
|
||||||
|
val Rewarded = ReduceLootBoxes(
|
||||||
|
S0.copy(IsReopening = true, ShownRewards = listOf(Item("old"))),
|
||||||
|
SetRewards(Rewards)
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetRewards 换奖励并把 isReopening 归 false",
|
||||||
|
Rewarded.Rewards == Rewards && !Rewarded.IsReopening &&
|
||||||
|
Rewarded.ShownRewards == listOf(Item("old"))
|
||||||
|
)
|
||||||
|
|
||||||
|
val Before = kotlin.js.Date.now().toLong()
|
||||||
|
val Timed = ReduceLootBoxes(S0, SetTimeToGiveLootBox(3))
|
||||||
|
val After = kotlin.js.Date.now().toLong()
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetTimeToGiveLootBox = 当前毫秒 + 秒 * 1000",
|
||||||
|
Timed.TimeToGiveLootBox >= Before + 3000L && Timed.TimeToGiveLootBox <= After + 3000L
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetPeriodOfGiveLootBox 写周期(秒)",
|
||||||
|
ReduceLootBoxes(S0, SetPeriodOfGiveLootBox(3600)).PeriodOfGiveLootBoxInSec == 3600
|
||||||
|
)
|
||||||
|
|
||||||
|
val Cleared = ReduceLootBoxes(
|
||||||
|
S0.copy(Rewards = Rewards, ShownRewards = Rewards, OpeningFailed = true),
|
||||||
|
ClearRewards
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:ClearRewards 只清两个奖励表",
|
||||||
|
Cleared.Rewards.isEmpty() && Cleared.ShownRewards.isEmpty() && Cleared.OpeningFailed
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:LootBoxOpeningFailed 置 openingFailed",
|
||||||
|
ReduceLootBoxes(S0, LootBoxOpeningFailed).OpeningFailed
|
||||||
|
)
|
||||||
|
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SelectCategory 写可空 currentCategory",
|
||||||
|
ReduceLootBoxes(S0, SelectCategory(ContainerType.RARE)).CurrentCategory == ContainerType.RARE &&
|
||||||
|
ReduceLootBoxes(S0.copy(CurrentCategory = ContainerType.RARE), SelectCategory(null))
|
||||||
|
.CurrentCategory == null
|
||||||
|
)
|
||||||
|
|
||||||
|
val Shown = ReduceLootBoxes(S0.copy(ShownRewards = listOf(Item("old"))), ShownRewards(Rewards))
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:ShownRewards 追加到 shownRewards(原 Im 是 List.plus)",
|
||||||
|
Shown.ShownRewards == listOf(Item("old"), Item("mine")) && Shown.Rewards.isEmpty()
|
||||||
|
)
|
||||||
|
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:四个布尔动作各写本字段",
|
||||||
|
!ReduceLootBoxes(S0.copy(IsOpening = true), SetIsOpening(false)).IsOpening &&
|
||||||
|
ReduceLootBoxes(S0, SetIsOpening(true)).IsOpening &&
|
||||||
|
ReduceLootBoxes(S0, SetIsAccelerated(true)).IsAccelerated &&
|
||||||
|
ReduceLootBoxes(S0, SetIsShopVisited(true)).IsShopVisited
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 LootBoxes:SetIsReopening 置 true;SetIsBlockOpenButtons 写值",
|
||||||
|
ReduceLootBoxes(S0, SetIsReopening).IsReopening &&
|
||||||
|
ReduceLootBoxes(S0, SetIsBlockOpenButtons(true)).IsBlockOpenButtons &&
|
||||||
|
!ReduceLootBoxes(S0.copy(IsBlockOpenButtons = true), SetIsBlockOpenButtons(false))
|
||||||
|
.IsBlockOpenButtons
|
||||||
|
)
|
||||||
|
Check("切片 LootBoxes:无关动作不改状态", ReduceLootBoxes(S0, AddFrags(1)) == S0)
|
||||||
|
|
||||||
|
// 根:lootBoxes 动作只动 lootBoxes
|
||||||
|
val R0 = RootState()
|
||||||
|
val R1 = ReduceRoot(R0, SetCurrentContainer(ContainerType.LEGENDARY))
|
||||||
|
Check(
|
||||||
|
"根:SetCurrentContainer 只动 lootBoxes",
|
||||||
|
R1.LootBoxes.CurrentContainerType == ContainerType.LEGENDARY &&
|
||||||
|
R1.Shop == R0.Shop && R1.Screen == R0.Screen &&
|
||||||
|
R1.InviteState == R0.InviteState && R1.Home == R0.Home
|
||||||
|
)
|
||||||
|
val R2 = ReduceRoot(R1, SetRewards(listOf(Item("root"))))
|
||||||
|
Check(
|
||||||
|
"根:SetRewards 只动 lootBoxes,且保留前一次归约",
|
||||||
|
R2.LootBoxes.Rewards == listOf(Item("root")) &&
|
||||||
|
R2.LootBoxes.CurrentContainerType == ContainerType.LEGENDARY &&
|
||||||
|
R2.Shop == R1.Shop
|
||||||
|
)
|
||||||
|
Check("根:lootBoxes 迁移后无关动作仍返回等值状态", ReduceRoot(R2, AddFrags(1)) == R2)
|
||||||
|
|
||||||
|
val W = RootWiring.ByField("lootBoxes")!!
|
||||||
|
Check(
|
||||||
|
"根:lootBoxes 委派到 aH / ws.wrv 且 18 个动作全部具名",
|
||||||
|
W.SliceClass == "aH" && W.Reducer == "ws.wrv" && W.Actions.size == 18 &&
|
||||||
|
W.Actions.none { it.startsWith("?") }
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"根:lootBoxes 的动作名覆盖 18 个 instanceof 臂",
|
||||||
|
listOf(
|
||||||
|
"SetPossibleRewards", "SetCurrentContainer", "SetKeys", "AddKeys",
|
||||||
|
"SetContainerData", "SetGoToGarage", "SetRewards", "SetTimeToGiveLootBox",
|
||||||
|
"SetPeriodOfGiveLootBox", "ClearRewards", "LootBoxOpeningFailed",
|
||||||
|
"SelectCategory", "ShownRewards", "SetIsOpening", "SetIsAccelerated",
|
||||||
|
"SetIsShopVisited", "SetIsReopening", "SetIsBlockOpenButtons",
|
||||||
|
).all { it in W.Actions }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val Failed = Results.filter { !it.second }
|
val Failed = Results.filter { !it.second }
|
||||||
|
|||||||
Reference in new issue
Block a user