feat(state): 迁移 battleCreateState,状态层 89/89 全部完成

This commit is contained in:
TermiNexus committed 2026-10-24 22:24:08 +08:00
1 parent d5f9d50e67
commit 21e9ffb04b
8 files changed
+273 -35

No files matched your search

+10 -5
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` 动作名。
- 已迁移切片 **88/89**:
- 已迁移切片 **89/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)
@@ -443,12 +443,17 @@
组员模型 `MatchmakingGroupMember(userId, isReady, mountItems)`(原包由 DI 服务构造,这里按 userId + 默认值收口);
`AddUser`(与战斗列表同名)改名 `AddGroupMember`;邀请 / 接受 / 拒绝维护 pending 与 members;
`UpdateUserReadiness` / `UpdateMountItems` 按 userId 命中后 copy;选中列表三臂 + `zW` 清空。
- `battleCreateState` → `$6` / `Df.ttg`(`BattleCreateStateReducer.kt`):18 字段,15 个分支。
`S6`(原名 `Configure`,与匹配大厅同名)改名 `SetBattleCreateConfig`;
`C6`/`E6`/`T6`/`I6`/`A6`/`R6`/`P6`/`N6`/`M6`/`L6`/`B6`/`U6` 都是表单字段 setter,
`D6`/`F6` 初始化表单、`G6`(CreateBattle)整片复位。
原 reducer 还读 maps(默认地图名)与 user(等级限制)两个切片;离线重建按「动作携带值直接写入」收口。
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 87 个有 reducer 的切片,
`launchParams` 与其余 **1 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 88/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **977 项全绿**,含 89 槽位完整性、87 个归约切片一次归约互不串片、未迁移字段透传,
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 88 个有 reducer 的切片,
`launchParams` 与其余 **0 个字段**(全部已迁移);
迁移进度用 `RootReducer.Coverage`(`已迁移 89/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **988 项全绿**,含 89 槽位完整性、88 个归约切片一次归约互不串片、未迁移字段透传,
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
+17 -18
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-10-24T14:15:45.383Z",
"generatedAt": "2026-10-24T14:20:06.478Z",
"module": "module_10032.js",
"RootState": {
"Class": "z6",
@@ -94,7 +94,8 @@
"clan",
"chatState",
"itemNotifications",
"matchmakingGroup"
"matchmakingGroup",
"battleCreateState"
],
"AnonymousActions": [
{
@@ -2443,26 +2444,24 @@
"Reducer": "Df.ttg",
"Delegate": "Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)",
"Actions": [
"?A6",
"?B6",
"?C6",
"?D6",
"?E6",
"?F6",
"?I6",
"?L6",
"?M6",
"?N6",
"?P6",
"?R6",
"?T6",
"?U6",
"Configure",
"CreateBattle",
"InitBattleCreate",
"RenameBattle",
"SelectBattleMap",
"SetBattleAvailableRankRange",
"SetBattleCreateConfig",
"SetBattleCreateExpandThemes",
"SetBattleCreateSearch",
"SetBattleFormat",
"SetBattleMapTheme",
"SetBattleMode2",
"SetBattleRankRange",
"SetBattleScoreLimit",
"SetBattleTimeLimit",
"SetRestrictions"
],
"Default": "new $6()",
"Migrated": false
"Migrated": true
},
{
"Field": "battleSearchState",
+17
View File
@@ -373,6 +373,22 @@ const AnonymousActionNames = new Map([
// matchmakingGroup 切片(rW / PW.is0):zW 没有 toString;lW 的 AddUser 与战斗列表同名类冲突。
['zW', 'ResetMatchmakingGroup'],
['lW', 'AddGroupMember'],
// battleCreateState 切片(\ / W6.ttg):15 个类没有 toString;S6 的 Configure 与匹配大厅同名类冲突。
['S6', 'SetBattleCreateConfig'],
['C6', 'RenameBattle'],
['E6', 'SetBattleMode2'],
['T6', 'SetBattleTimeLimit'],
['I6', 'SetBattleScoreLimit'],
['A6', 'SetBattleFormat'],
['R6', 'SetBattleRankRange'],
['P6', 'SetBattleAvailableRankRange'],
['N6', 'SelectBattleMap'],
['M6', 'SetBattleMapTheme'],
['D6', 'InitBattleCreate'],
['F6', 'InitBattleCreate'],
['L6', 'SetBattleMapTheme'],
['B6', 'SetBattleCreateSearch'],
['U6', 'SetBattleCreateExpandThemes'],
]);
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
@@ -865,6 +881,7 @@ const Migrated = {
chatState: { Type: 'ChatStateSlice', Reduce: 'ReduceChat' },
itemNotifications: { Type: 'ItemNotificationsStateSlice', Reduce: 'ReduceItemNotifications' },
matchmakingGroup: { Type: 'MatchmakingGroupStateSlice', Reduce: 'ReduceMatchmakingGroup' },
battleCreateState: { Type: 'BattleCreateStateSlice', Reduce: 'ReduceBattleCreate' },
};
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
@@ -0,0 +1,122 @@
// BattleCreateStateReducer.kt —— 创建战斗切片(对应 module_10032 的 $6 状态 + W6.ttg)
//
// 证据链:
// 状态类 $6:18 个字段,toString 名 "BattleCreateState(…)";copy 是 btj(18 字段 + 回调)。
// 根委派:`Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)`(还读 maps / user 两个切片)。
// reducer(18 个实例臂):
// S6(Configure) → config(同时按 config 的 limits 写上下限)
// C6(无 toString) → name
// E6(无 toString) → battleMode(并重算队伍人数上限)
// T6 / I6(无 toString) → timeLimitInMinutes / scoreLimit
// A6(无 toString) → format
// R6(无 toString) → selectedMinRank / selectedMaxRank
// P6(无 toString) → availableMinRank / availableMaxRank
// N6(无 toString) → 选中地图(mapId / map 名 / options)
// M6(无 toString) → 地图主题
// O6(SetRestrictions) → selectedOptions
// D6 / F6(无 toString) → 用 config(+ maps 默认地图)初始化
// G6(CreateBattle) → 整片复位
// L6 / B6 / U6(无 toString)→ mapTheme / search / isExpandThemesPanel
//
// 原 reducer 还要读 maps 切片(算默认地图名)与 user 切片(等级限制);离线重建里
// 这两个跨切片依赖按「动作携带的值直接写入」收口,默认地图 / 等级区间由调用方提供。
package App.State.Reducers
import App.State.CreateBattle
import App.State.SetRestrictions
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `S6`(原名 `Configure`,与匹配大厅同名类冲突):写创建战斗配置。 */
data class SetBattleCreateConfig(val config: Any?)
/** 原 `C6`:改战斗名。 */
data class RenameBattle(val name: Any?)
/** 原 `E6`:切换战斗模式。 */
data class SetBattleMode2(val mode: Any?)
/** 原 `T6` / `I6`:限时 / 分数上限。 */
data class SetBattleTimeLimit(val minutes: Any?)
data class SetBattleScoreLimit(val limit: Any?)
/** 原 `A6`:切换赛制。 */
data class SetBattleFormat(val format: Any?)
/** 原 `R6`:可选的段位区间。 */
data class SetBattleRankRange(val minRank: Any?, val maxRank: Any?)
/** 原 `P6`:可用的段位区间。 */
data class SetBattleAvailableRankRange(val minRank: Any?, val maxRank: Any?)
/** 原 `N6`:选中地图。 */
data class SelectBattleMap(val mapId: Any?, val mapName: Any?, val options: Any?)
/** 原 `M6`:地图主题。 */
data class SetBattleMapTheme(val theme: Any?)
/** 原 `D6` / `F6`:按配置(+ 默认地图)初始化表单。 */
data class InitBattleCreate(val config: Any?)
/** 原 `L6` / `B6` / `U6`:主题 / 搜索 / 展开主题面板。 */
data class SetBattleCreateSearch(val search: Any?)
data class SetBattleCreateExpandThemes(val expanded: Boolean)
/* ------------------------------------------------------------------ 状态 */
data class BattleCreateStateSlice(
val Config: Any? = null,
val Name: Any? = "",
val SelectedMinRank: Int = 1,
val SelectedMaxRank: Int = 2,
val AvailableMinRank: Int = 1,
val AvailableMaxRank: Int = 2,
val BattleMode: Any? = null,
val MaxPeopleInTeam: Int = 10,
val TimeLimitInMinutes: Any? = 30,
val ScoreLimit: Any? = 0,
val Format: Any? = null,
val Map: Any? = null,
val SelectedOptions: Any? = null,
val Creating: Boolean = false,
val MapTheme: Any? = null,
val Search: Any? = null,
val IsExpandThemesPanel: Boolean = false,
val CurrentPage: Any? = "",
)
/* ------------------------------------------------------------------ reducer */
private fun IntOf(Value: Any?, Fallback: Int): Int = (Value as? Number)?.toInt() ?: Fallback
fun ReduceBattleCreate(
State0: BattleCreateStateSlice,
Action: Any,
): BattleCreateStateSlice = when (Action) {
is SetBattleCreateConfig -> State0.copy(Config = Action.config)
is RenameBattle -> State0.copy(Name = Action.name)
is SetBattleMode2 -> State0.copy(BattleMode = Action.mode)
is SetBattleTimeLimit -> State0.copy(TimeLimitInMinutes = Action.minutes)
is SetBattleScoreLimit -> State0.copy(ScoreLimit = Action.limit)
is SetBattleFormat -> State0.copy(Format = Action.format)
is SetBattleRankRange -> State0.copy(
SelectedMinRank = IntOf(Action.minRank, State0.SelectedMinRank),
SelectedMaxRank = IntOf(Action.maxRank, State0.SelectedMaxRank),
)
is SetBattleAvailableRankRange -> State0.copy(
AvailableMinRank = IntOf(Action.minRank, State0.AvailableMinRank),
AvailableMaxRank = IntOf(Action.maxRank, State0.AvailableMaxRank),
)
is SelectBattleMap -> State0.copy(
Map = Action.mapId,
Name = Action.mapName ?: State0.Name,
SelectedOptions = Action.options,
)
is SetBattleMapTheme -> State0.copy(MapTheme = Action.theme)
is SetRestrictions -> State0.copy(SelectedOptions = Action.restrictions)
is InitBattleCreate -> State0.copy(Config = Action.config, Creating = false)
is SetBattleCreateSearch -> State0.copy(Search = Action.search)
is SetBattleCreateExpandThemes -> State0.copy(IsExpandThemesPanel = Action.expanded)
is CreateBattle -> BattleCreateStateSlice()
else -> State0
}
@@ -1,7 +1,7 @@
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
//
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
// 这里采用同一形状。已迁移的 88 个切片里,87 个真正调用 reducer,launchParams 只做类型收口:
// 这里采用同一形状。已迁移的 89 个切片里,88 个真正调用 reducer,launchParams 只做类型收口:
//
// inviteState → ReduceInvite(aD / pD.er5)
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
@@ -90,9 +90,10 @@
// chatState → ReduceChat(LP / $r.wqv,根委派 `$r.wqv(t, n.opv_1)`)
// itemNotifications → ReduceItemNotifications(_1 / A1().dso,根委派 `A1().dso(t, n.xpx_1)`)
// matchmakingGroup → ReduceMatchmakingGroup(rW / NW().is0,根委派 `NW().is0(t, n.jpw_1)`)
// battleCreateState → ReduceBattleCreate($6 / Df.ttg,根委派 `Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)`)
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
//
// 其余 1 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 0 个字段透传(全部已迁移)(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
@@ -102,6 +103,7 @@ import App.State.Reducers.ReduceAnnouncement
import App.State.Reducers.ReduceAntiAddiction
import App.State.Reducers.ReduceBattleResult
import App.State.Reducers.ReduceBattleChat
import App.State.Reducers.ReduceBattleCreate
import App.State.Reducers.ReduceBattleMessage
import App.State.Reducers.ReduceBattlePause
import App.State.Reducers.ReduceBattleSearch
@@ -285,6 +287,7 @@ object RootReducer {
ChatState = ReduceChat(State0.ChatState, Action),
ItemNotifications = ReduceItemNotifications(State0.ItemNotifications, Action),
MatchmakingGroup = ReduceMatchmakingGroup(State0.MatchmakingGroup, Action),
BattleCreateState = ReduceBattleCreate(State0.BattleCreateState, Action),
)
}
@@ -9,6 +9,7 @@ import App.State.Reducers.AnnouncementStateSlice
import App.State.Reducers.AntiAddictionStateSlice
import App.State.Reducers.AvailabilityByRankStateSlice
import App.State.Reducers.BattleChatStateSlice
import App.State.Reducers.BattleCreateStateSlice
import App.State.Reducers.BattleInviteStateSlice
import App.State.Reducers.BattleMessageStateSlice
import App.State.Reducers.BattlePauseStateSlice
@@ -158,8 +159,8 @@ data class RootState(
val Maps: MapsStateSlice = MapsStateSlice(),
/** 原 matchmaking(TH / JH().qrx)。已迁移。 */
val Matchmaking: MatchmakingStateSlice = MatchmakingStateSlice(),
/** 原 battleCreateState($6 / Df.ttg)。未迁移:原样透传。 */
val BattleCreateState: Any? = null,
/** 原 battleCreateState($6 / Df.ttg)。已迁移。 */
val BattleCreateState: BattleCreateStateSlice = BattleCreateStateSlice(),
/** 原 battleSearchState(Itt / Htt().vtg)。已迁移。 */
val BattleSearchState: BattleSearchStateSlice = BattleSearchStateSlice(),
/** 原 lobbyLoader(KW / Bs.es1)。已迁移。 */
@@ -45,7 +45,7 @@ object RootWiring {
SliceWiring("battlesSelect", "apw_1", "fA", "Mi.vqk", "Mi.vqk(t, n.apw_1)", listOf("AddBattle", "AddUser", "AddUsers", "ChangeName", "EnterToBattleInfo", "InitProBattleRewardLimit", "LimitReached", "MarkBattlesLoaded", "RemoveBattle", "RemoveUser", "ResetBattlesSelect", "RoundFinish", "RoundStarted", "SetReEnterProtectBattleId", "SwapTeam", "UpdateFund", "UpdateSuspicion", "UpdateTeamScore", "UpdateUserScore", "UpdateUserSuspicion")),
SliceWiring("maps", "bpw_1", "OA", "Oi.sql", "Oi.sql(t, n.bpw_1)", listOf("AddMap")),
SliceWiring("matchmaking", "cpw_1", "TH", "JH().qrx", "JH().qrx(t, n.cpw_1)", listOf("CancelLoadingBattle", "Configure", "EnterToBattleApply", "Play", "ResetMatchmakingProgress", "SelectMatchmakingMode", "SetRankIndexToMinGS", "StartLoadingBattle", "WasRegistered")),
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("battleCreateState", "dpw_1", "\$6", "Df.ttg", "Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)", listOf("CreateBattle", "InitBattleCreate", "RenameBattle", "SelectBattleMap", "SetBattleAvailableRankRange", "SetBattleCreateConfig", "SetBattleCreateExpandThemes", "SetBattleCreateSearch", "SetBattleFormat", "SetBattleMapTheme", "SetBattleMode2", "SetBattleRankRange", "SetBattleScoreLimit", "SetBattleTimeLimit", "SetRestrictions")),
SliceWiring("battleSearchState", "epw_1", "Itt", "Htt().vtg", "Htt().vtg(t, n.epw_1)", listOf("ClearSelectedBattle", "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("DoInit", "ResetBattleRewards", "SetHasBattleResult", "SetIncreasedReward", "SetRewards", "SetSimpleReward")),
@@ -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", "tipsState", "rankUpDialogState", "battleResult", "captchaState", "modalScreen", "droppableGold", "eventGoods", "creatorTagState", "rewardedVideosState", "passwordChangeState", "system", "contextTutorial", "battleTutorial", "spectator", "twoStepVerificationState", "antiAddiction", "moderation", "migrationState", "register", "userReportsState", "friends", "suppliesState", "clanUserInfo", "battleInviteState", "partner", "userRenameState", "battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial", "clanChatState", "battleSearchState", "battleStatistics", "soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings", "graphicsSettings", "controlSettings", "battleSettings", "contractState", "tankBuilderState", "battleMessage", "maps", "battleChat", "remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState", "itemNotifications", "matchmakingGroup")
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", "system", "contextTutorial", "battleTutorial", "spectator", "twoStepVerificationState", "antiAddiction", "moderation", "migrationState", "register", "userReportsState", "friends", "suppliesState", "clanUserInfo", "battleInviteState", "partner", "userRenameState", "battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial", "clanChatState", "battleSearchState", "battleStatistics", "soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings", "graphicsSettings", "controlSettings", "battleSettings", "contractState", "tankBuilderState", "battleMessage", "maps", "battleChat", "remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState", "itemNotifications", "matchmakingGroup", "battleCreateState")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
@@ -19,7 +19,7 @@
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
// Moderation / MigrationState / Register / UserReports /
// Friends / Supplies / ClanUserInfo / BattleInvite / Partner / UserRename /
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect / BattleUsers / Challenges / ClansStorage / Clan / ChatState / ItemNotifications / MatchmakingGroup;
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect / BattleUsers / Challenges / ClansStorage / Clan / ChatState / ItemNotifications / MatchmakingGroup / BattleCreate;
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
@@ -928,6 +928,23 @@ import App.State.Reducers.MatchmakingGroupMember
import App.State.Reducers.MatchmakingGroupStateSlice
import App.State.Reducers.ReduceMatchmakingGroup
import App.State.Reducers.ResetMatchmakingGroup
import App.State.CreateBattle
import App.State.SetRestrictions
import App.State.Reducers.BattleCreateStateSlice
import App.State.Reducers.InitBattleCreate
import App.State.Reducers.RenameBattle
import App.State.Reducers.ReduceBattleCreate
import App.State.Reducers.SelectBattleMap
import App.State.Reducers.SetBattleAvailableRankRange
import App.State.Reducers.SetBattleCreateConfig
import App.State.Reducers.SetBattleCreateExpandThemes
import App.State.Reducers.SetBattleCreateSearch
import App.State.Reducers.SetBattleFormat
import App.State.Reducers.SetBattleMapTheme
import App.State.Reducers.SetBattleMode2
import App.State.Reducers.SetBattleRankRange
import App.State.Reducers.SetBattleScoreLimit
import App.State.Reducers.SetBattleTimeLimit
import App.State.Reducers.UserRankBounds
import App.State.Reducers.UserStateSlice
import App.State.Reducers.ClearHullData
@@ -1355,7 +1372,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 88 个已迁移切片(87 归约 + launchParams 透传)
// ⑭ 根 reducer:89 个槽位的委派表 + 89 个已迁移切片(88 归约 + launchParams 透传)
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -1419,7 +1436,7 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 88 个已迁移字段,其余 1 个待迁",
"根:迁移表 = 89 个已迁移字段,其余 0 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
@@ -1444,9 +1461,9 @@ object StateCheck {
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
"tankBuilderState", "battleMessage", "maps", "battleChat",
"remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState", "itemNotifications",
"matchmakingGroup",
"matchmakingGroup", "battleCreateState",
) &&
RootWiring.Unmigrated.size == 1
RootWiring.Unmigrated.isEmpty()
)
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
@@ -1502,7 +1519,7 @@ object StateCheck {
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 88/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 89/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -7541,6 +7558,80 @@ object StateCheck {
)
}
// 99 切片 reducer:BattleCreate($6 / Df.ttg,18 字段;15 个动作)
run {
val S0 = BattleCreateStateSlice()
Check(
"切片 BattleCreate:默认值完整",
S0.Config == null && S0.Name == "" && S0.SelectedMinRank == 1 &&
S0.SelectedMaxRank == 2 && S0.BattleMode == null &&
S0.MaxPeopleInTeam == 10 && S0.TimeLimitInMinutes == 30 &&
S0.ScoreLimit == 0 && S0.Format == null && S0.Map == null &&
S0.SelectedOptions == null && !S0.Creating && S0.MapTheme == null &&
S0.Search == null && !S0.IsExpandThemesPanel
)
val S1 = ReduceBattleCreate(S0, SetBattleCreateConfig("cfg"))
Check("切片 BattleCreate:SetBattleCreateConfig 写配置", S1.Config == "cfg")
Check(
"切片 BattleCreate:改名 / 模式 / 限时 / 分数 / 赛制",
ReduceBattleCreate(S1, RenameBattle("b1")).Name == "b1" &&
ReduceBattleCreate(S1, SetBattleMode2("DM")).BattleMode == "DM" &&
ReduceBattleCreate(S1, SetBattleTimeLimit(15)).TimeLimitInMinutes == 15 &&
ReduceBattleCreate(S1, SetBattleScoreLimit(500)).ScoreLimit == 500 &&
ReduceBattleCreate(S1, SetBattleFormat("fmt")).Format == "fmt"
)
Check(
"切片 BattleCreate:两段位区间",
ReduceBattleCreate(S1, SetBattleRankRange(3, 5)).let {
it.SelectedMinRank == 3 && it.SelectedMaxRank == 5
} &&
ReduceBattleCreate(S1, SetBattleAvailableRankRange(4, 6)).let {
it.AvailableMinRank == 4 && it.AvailableMaxRank == 6
}
)
Check(
"切片 BattleCreate:选图写 map / 名称 / options",
ReduceBattleCreate(S1, SelectBattleMap(1L, "Desert", listOf("o"))).let {
it.Map == 1L && it.Name == "Desert" && it.SelectedOptions == listOf("o")
}
)
Check(
"切片 BattleCreate:主题 / 限制项 / 搜索 / 展开",
ReduceBattleCreate(S1, SetBattleMapTheme("summer")).MapTheme == "summer" &&
ReduceBattleCreate(S1, SetRestrictions(listOf("r"))).SelectedOptions ==
listOf<Any?>("r") &&
ReduceBattleCreate(S1, SetBattleCreateSearch("q")).Search == "q" &&
ReduceBattleCreate(S1, SetBattleCreateExpandThemes(true)).IsExpandThemesPanel
)
Check(
"切片 BattleCreate:InitBattleCreate 写配置并清 creating",
ReduceBattleCreate(S1, InitBattleCreate("cfg2")).let {
it.Config == "cfg2" && !it.Creating
}
)
Check(
"切片 BattleCreate:CreateBattle 整片复位",
ReduceBattleCreate(S1, CreateBattle(listOf("o"))) == BattleCreateStateSlice()
)
Check(
"切片 BattleCreate:无关动作不改状态",
ReduceBattleCreate(S1, AddFrags(1)) == S1
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, RenameBattle("x"))
Check(
"根:RenameBattle 只动 battleCreateState",
Root1.BattleCreateState.Name == "x" &&
Root1.MatchmakingGroup == Root0.MatchmakingGroup
)
val W = RootWiring.ByField("battleCreateState")!!
Check(
"根:battleCreateState 委派到 \$6 / Df.ttg 且 15 个动作",
W.SliceClass == "\$6" && W.Reducer == "Df.ttg" && W.Actions.size == 15
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)