feat(state): 迁移 soundSettings/gameSettings/keyboardSettings/touchscreenHudSettings

This commit is contained in:
TermiNexus committed 2026-10-24 17:13:24 +08:00
1 parent 86979eb168
commit cade4020a5
11 files changed
+604 -51

No files matched your search

+18 -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` 动作名。
- 已迁移切片 **66/89**:
- 已迁移切片 **70/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)
@@ -350,12 +350,23 @@
- `battleStatistics` → `jR` / `rr.fqr`(`BattleStatisticsReducer.kt`):27 字段,22 个分支。
`FR`(14 字段的 Init)与通知切片的同名类冲突,改名为 `InitBattle`(contractState 之后复用);
其余为战斗元信息 setter + 回合 / 统计面板 / 菜单 / 旗主 / 加载位;`SetRemainingTime` 用 `$m` 把时长转秒。
- `soundSettings` → `Det` / `Wet().jth`(`SoundSettingsReducer.kt`):10 字段,4 个分支。
音量缺省 100/100/50/50/50/50/50,三个开关 false/false/true;`Bet`/`Fet` 都改名为
`ApplySoundSettings` / `SetEntranceSoundSettings`(原名都是 `UpdateSettings`,与其它设置切片同名)。
storage 读写属 storage port,`_C`→`ResetSettingsFromStorage` 保留现有状态。
- `gameSettings` → `c2` / `g2().zss`(`GameSettingsReducer.kt`):6 字段,2 个分支。
五个显示开关缺省 true、`mobilePadding=null`;`v2` 同样因 `UpdateSettings` 重名改名为 `ApplyGameSettings`。
- `keyboardSettings` → `Sut` / `bw.oth`(`KeyboardSettingsReducer.kt`):1 字段 `Map<action, Map<index, keyState>>`,5 个分支。
`ChangeAction` 先按 keyState 从其它 index 解绑再写入新绑定;`RemoveAction` 解绑单个 index;
`ResetAction` 依赖外部 helper,纯 reducer 保留原状态;两个默认复位清空绑定表。
- `touchscreenHudSettings` → `j7` / `N7().xsv`(`TouchscreenHudSettingsReducer.kt`):1 字段,3 个分支。
`C7`(原名 `UpdateSettings`)改名为 `ApplyTouchscreenHudSettings`;`E7`→`ResetTouchscreenHudMapping` 恢复默认布局。
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 65 个有 reducer 的切片,
`launchParams` 与其余 **23 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 66/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **755 项全绿**,含 89 槽位完整性、65 个归约切片一次归约互不串片、未迁移字段透传,
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 69 个有 reducer 的切片,
`launchParams` 与其余 **19 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 70/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **787 项全绿**,含 89 槽位完整性、69 个归约切片一次归约互不串片、未迁移字段透传,
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
@@ -377,6 +388,8 @@
battlePauseState 的 2 个、battleViewState 的 12 个、tankActionsLogState 的 1 个同样按效果定名。
clanChatState 的 3 个(`$nt`/`xnt`/`Tnt`)、battleSearchState 的 1 个也已登记。
battleStatistics 的 12 个(含改名的 `FR`→`InitBattle`)同样按效果定名。
设置类切片的 `_C`(共用重载)+ `Bet`/`Fet`/`Uet`/`v2`/`C7`/`E7`/`Iut` 也已登记,
其中四个原名都是 `UpdateSettings` 的类按切片改名为 `Apply*`,避免生成物同名冲突。
### S4 引擎核心
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
+74 -22
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-10-24T08:50:07.796Z",
"generatedAt": "2026-10-24T09:04:37.952Z",
"module": "module_10032.js",
"RootState": {
"Class": "z6",
@@ -72,7 +72,11 @@
"suppliesTutorial",
"clanChatState",
"battleSearchState",
"battleStatistics"
"battleStatistics",
"soundSettings",
"gameSettings",
"keyboardSettings",
"touchscreenHudSettings"
],
"AnonymousActions": [
{
@@ -1281,6 +1285,54 @@
"Name": "ResetBattleStatistics",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "new jR() 整片复位"
},
{
"Local": "_C",
"Name": "ResetSettingsFromStorage",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "各设置切片共用的重载动作:sound/game/control 读 localStorage,keyboard/touchscreen 恢复默认表"
},
{
"Local": "Bet",
"Name": "ApplySoundSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "听到设置整体替换(原名 UpdateSettings)"
},
{
"Local": "Fet",
"Name": "SetEntranceSoundSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "入口音效开关后整体替换设置(原名 SetEntranceSoundEnabled)"
},
{
"Local": "Uet",
"Name": "EnableEntranceSound",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isEntranceSoundOn=true(存储缺省)"
},
{
"Local": "v2",
"Name": "ApplyGameSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "游戏内显示设置整体替换(原名 UpdateSettings)"
},
{
"Local": "C7",
"Name": "ApplyTouchscreenHudSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "触屏布局整体替换(原名 UpdateSettings)"
},
{
"Local": "E7",
"Name": "ResetTouchscreenHudMapping",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "恢复默认触屏布局(`new j7()`)"
},
{
"Local": "Iut",
"Name": "ResetKeyMappingFromDefaults",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "恢复键位默认绑定(`rut().mu7_1`)"
}
],
"Markers": [
@@ -1726,7 +1778,6 @@
"Actions": [
"?QP",
"?XP",
"?_C",
"?bN",
"?fN",
"?lN",
@@ -1740,6 +1791,7 @@
"CleanUsersMessage",
"Configure",
"IgnoreUserMessages",
"ResetSettingsFromStorage",
"SaveChatMessage",
"SendMessageToServer",
"SetMessages",
@@ -2444,13 +2496,13 @@
"Reducer": "Wet().jth",
"Delegate": "Wet().jth(t, n.vpw_1)",
"Actions": [
"?Uet",
"?_C",
"SetEntranceSoundEnabled",
"UpdateSettings"
"ApplySoundSettings",
"EnableEntranceSound",
"ResetSettingsFromStorage",
"SetEntranceSoundSettings"
],
"Default": "new Det()",
"Migrated": false
"Migrated": true
},
{
"Field": "gameSettings",
@@ -2459,11 +2511,11 @@
"Reducer": "g2().zss",
"Delegate": "g2().zss(t, n.wpw_1)",
"Actions": [
"?_C",
"UpdateSettings"
"ApplyGameSettings",
"ResetSettingsFromStorage"
],
"Default": "new c2()",
"Migrated": false
"Migrated": true
},
{
"Field": "battleSettings",
@@ -2472,7 +2524,7 @@
"Reducer": "X0().esq",
"Delegate": "X0().esq(t, n.xpw_1)",
"Actions": [
"?_C",
"ResetSettingsFromStorage",
"UpdateSettings"
],
"Default": "new W0()",
@@ -2497,7 +2549,7 @@
"Reducer": "_2().ess",
"Delegate": "_2().ess(t, n.zpw_1)",
"Actions": [
"?_C",
"ResetSettingsFromStorage",
"UpdateSettings"
],
"Default": "new i2()",
@@ -2553,14 +2605,14 @@
"Reducer": "bw.oth",
"Delegate": "bw.oth(t, n.dpx_1)",
"Actions": [
"?Iut",
"?_C",
"ChangeAction",
"RemoveAction",
"ResetAction"
"ResetAction",
"ResetKeyMappingFromDefaults",
"ResetSettingsFromStorage"
],
"Default": "new Sut()",
"Migrated": false
"Migrated": true
},
{
"Field": "touchscreenHudSettings",
@@ -2569,12 +2621,12 @@
"Reducer": "N7().xsv",
"Delegate": "N7().xsv(t, n.epx_1)",
"Actions": [
"?E7",
"?_C",
"UpdateSettings"
"ApplyTouchscreenHudSettings",
"ResetSettingsFromStorage",
"ResetTouchscreenHudMapping"
],
"Default": "new j7()",
"Migrated": false
"Migrated": true
},
{
"Field": "mainQuest",
@@ -2864,10 +2916,10 @@
"Delegate": "A1().dso(t, n.xpx_1)",
"Actions": [
"?S1",
"?_C",
"AddItemNotification",
"AddItemNotifications",
"RemoveItemNotification",
"ResetSettingsFromStorage",
"UpdateItemNotification"
],
"Default": "new _1()",
+38
View File
@@ -294,6 +294,19 @@ const AnonymousActionNames = new Map([
['tP', 'MarkBattleLoaded'],
['nP', 'MarkBattleUnloaded'],
['HR', 'ResetBattleStatistics'],
// 设置类切片的共用匿名动作与同名类:_C 是各设置切片共用的「从存储重载」动作。
['_C', 'ResetSettingsFromStorage'],
// soundSettings 切片(Det / Het.jth):Bet/Fet 的 toString 名都是 UpdateSettings/…,与其它设置切片同名。
['Bet', 'ApplySoundSettings'],
['Fet', 'SetEntranceSoundSettings'],
['Uet', 'EnableEntranceSound'],
// gameSettings 切片(c2 / d2.zss):v2 的 toString 名也是 UpdateSettings。
['v2', 'ApplyGameSettings'],
// touchscreenHudSettings 切片(j7 / P7.xsv):C7 的 toString 名也是 UpdateSettings;E7 没有 toString。
['C7', 'ApplyTouchscreenHudSettings'],
['E7', 'ResetTouchscreenHudMapping'],
// keyboardSettings 切片(Sut / Nut.oth):Iut 没有 toString。
['Iut', 'ResetKeyMappingFromDefaults'],
]);
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
@@ -753,6 +766,13 @@ const Migrated = {
clanChatState: { Type: 'ClanChatStateSlice', Reduce: 'ReduceClanChat' },
battleSearchState: { Type: 'BattleSearchStateSlice', Reduce: 'ReduceBattleSearch' },
battleStatistics: { Type: 'BattleStatisticsStateSlice', Reduce: 'ReduceBattleStatistics' },
soundSettings: { Type: 'SoundSettingsStateSlice', Reduce: 'ReduceSoundSettings' },
gameSettings: { Type: 'GameSettingsStateSlice', Reduce: 'ReduceGameSettings' },
keyboardSettings: { Type: 'KeyboardSettingsStateSlice', Reduce: 'ReduceKeyboardSettings' },
touchscreenHudSettings: {
Type: 'TouchscreenHudSettingsStateSlice',
Reduce: 'ReduceTouchscreenHudSettings',
},
};
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
@@ -1143,6 +1163,18 @@ const BattleStatisticsAnonymousEffects = {
HR: 'new jR() 整片复位',
};
// 设置类切片:_C 是共用的「从存储重载」,其余是各切片里与 UpdateSettings 同名的动作。
const AppSettingsAnonymousEffects = {
_C: '各设置切片共用的重载动作:sound/game/control 读 localStorage,keyboard/touchscreen 恢复默认表',
Bet: '听到设置整体替换(原名 UpdateSettings)',
Fet: '入口音效开关后整体替换设置(原名 SetEntranceSoundEnabled)',
Uet: 'isEntranceSoundOn=true(存储缺省)',
v2: '游戏内显示设置整体替换(原名 UpdateSettings)',
C7: '触屏布局整体替换(原名 UpdateSettings)',
E7: '恢复默认触屏布局(`new j7()`)',
Iut: '恢复键位默认绑定(`rut().mu7_1`)',
};
fs.writeFileSync(
path.join(P, 'names', 'root_wiring.json'),
JSON.stringify(
@@ -1405,6 +1437,12 @@ fs.writeFileSync(
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(AppSettingsAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
],
Markers: [
{
@@ -0,0 +1,39 @@
// GameSettingsReducer.kt —— 游戏内显示设置切片(对应 module_10032 的 c2 状态 + d2.zss)
//
// 证据链:
// 状态类 c2:6 个字段,toString 名 "GameSettings(…)"。
// 默认:5 个显示开关全 true、mobilePadding=null。
// 根委派:`g2().zss(t, n.wpw_1)`。
// reducer:
// _C(ResetSettingsFromStorage)→ 从 localStorage 重新读
// v2(UpdateSettings) → 整体替换设置对象
//
// storage 读写属 storage port 职责:`_C` 保留现有状态,`v2` 用动作携带的设置对象替换。
package App.State.Reducers
/* ------------------------------------------------------------------ 动作 */
/** 原 `v2`(`UpdateSettings`,与其它设置切片同名):整体替换游戏内显示设置。 */
data class ApplyGameSettings(val settings: GameSettingsStateSlice)
/* ------------------------------------------------------------------ 状态 */
data class GameSettingsStateSlice(
val ShowDropZones: Boolean = true,
val ShowYourDrone: Boolean = true,
val ShowChat: Boolean = true,
val ShowNotifications: Boolean = true,
val ShowOtherDrone: Boolean = true,
val MobilePadding: Any? = null,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceGameSettings(
State0: GameSettingsStateSlice,
Action: Any,
): GameSettingsStateSlice = when (Action) {
is ApplyGameSettings -> Action.settings
is ResetSettingsFromStorage -> State0
else -> State0
}
@@ -0,0 +1,73 @@
// KeyboardSettingsReducer.kt —— 键盘按键绑定切片(对应 module_10032 的 Sut 状态 + Nut.oth)
//
// 证据链:
// 状态类 Sut:1 个字段,toString 名 "KeyboardSettings(mapping=…)"。
// 默认:mapping = rut().mu7_1(内置默认绑定表)。
// 根委派:`bw.oth(t, n.dpx_1)`。
// reducer:
// _C(ResetSettingsFromStorage)→ 恢复内置默认绑定(`Mut()`)
// Iut(无 toString) → 另一个默认来源(`rut().mu7_1`)
// Cut(ChangeAction) → 把 keyState 从其它 index 上解绑,再绑到 (action, index)
// Eut(RemoveAction) → 解绑 (action, index);action 不存在时 no-op
// Tut(ResetAction) → `Aut(...)`:把某个 action 的绑定重置为默认(外部 helper)
//
// mapping 形状:`Map<action, Map<index, keyState>>`(源码里外层 key 是 `v2()`、内层是 `Vd(index, keyState)`)。
// storage / 输入服务的写回属 side effect,`Tut` 在纯 reducer 里保留原状态。
package App.State.Reducers
import App.State.ChangeAction
import App.State.RemoveAction
import App.State.ResetAction
/* ------------------------------------------------------------------ 动作 */
/** 原 `Iut`:从另一个默认来源恢复绑定。 */
object ResetKeyMappingFromDefaults
/* ------------------------------------------------------------------ 状态 */
data class KeyboardSettingsStateSlice(
val Mapping: Map<Any?, Any?> = emptyMap(),
)
/* ------------------------------------------------------------------ reducer */
@Suppress("UNCHECKED_CAST")
private fun InnerOf(Mapping: Map<Any?, Any?>, Action: Any?): Map<Any?, Any?> =
Mapping[Action] as? Map<Any?, Any?> ?: emptyMap()
/** 原 `Cut` 的映射重建:每个 action 的绑定表先去掉占用同一 keyState 的项,再写入新绑定。 */
private fun ChangeBinding(
Mapping: Map<Any?, Any?>,
Action: ChangeAction,
): Map<Any?, Any?> {
val Result = LinkedHashMap<Any?, Any?>()
for ((ActionKey, Inner) in Mapping) {
val Kept = LinkedHashMap<Any?, Any?>()
for ((Index, KeyState) in (Inner as? Map<Any?, Any?> ?: emptyMap())) {
if (KeyState != Action.keyState) Kept[Index] = KeyState
}
Result[ActionKey] = Kept
}
val Target = Result[Action.action] as? Map<Any?, Any?> ?: emptyMap()
Result[Action.action] = Target + (Action.index to Action.keyState)
return Result
}
fun ReduceKeyboardSettings(
State0: KeyboardSettingsStateSlice,
Action: Any,
): KeyboardSettingsStateSlice = when (Action) {
// 两个默认来源在离线重建里都收口为空绑定表(storage/输入服务不在纯 reducer 内)。
is ResetSettingsFromStorage -> KeyboardSettingsStateSlice()
is ResetKeyMappingFromDefaults -> KeyboardSettingsStateSlice()
is ChangeAction -> State0.copy(Mapping = ChangeBinding(State0.Mapping, Action))
is RemoveAction -> {
if (State0.Mapping.containsKey(Action.action)) {
val Inner = InnerOf(State0.Mapping, Action.action) - Action.index
State0.copy(Mapping = State0.Mapping + (Action.action to Inner))
} else State0
}
is ResetAction -> State0
else -> State0
}
@@ -0,0 +1,58 @@
// SoundSettingsReducer.kt —— 音量设置切片(对应 module_10032 的 Det 状态 + Het.jth)
//
// 证据链:
// 状态类 Det:10 个字段,toString 名 "SoundSettings(…)"。
// 默认:master/ui=100,music/ambient/sfx/hitmarkPlayer/hitmarkEnemy=50,
// mute=false、muteInBackground=false、isEntranceSoundOn=true。
// 根委派:`Wet().jth(t, n.vpw_1)`。
// reducer:
// _C(ResetSettingsFromStorage) → 从 localStorage 重新读整套音量
// Bet(UpdateSettings) → 整体替换设置对象
// Uet(无 toString) → isEntranceSoundOn=true(从存储读默认)
// Fet(SetEntranceSoundEnabled) → 用动作里的设置对象替换(并写回存储)
//
// storage 相关读写属 storage port 职责,纯 reducer 不做 IO:
// `_C` 保留现有状态,`Bet`/`Fet` 用动作携带的设置对象整体替换。
package App.State.Reducers
/* ------------------------------------------------------------------ 动作 */
/** 原 `Bet`(`UpdateSettings`,与其它设置切片同名):整体替换音量设置。 */
data class ApplySoundSettings(val settings: SoundSettingsStateSlice)
/** 原 `Fet`(`SetEntranceSoundEnabled`):入口音效开关后整体替换设置。 */
data class SetEntranceSoundSettings(val settings: SoundSettingsStateSlice)
/** 原 `Uet`:把入口音效打开(存储缺省 true)。 */
object EnableEntranceSound
/** 原 `_C`:从存储重载设置;没有 storage port 时保留现有状态。 */
object ResetSettingsFromStorage
/* ------------------------------------------------------------------ 状态 */
data class SoundSettingsStateSlice(
val MasterVolume: Double = 100.0,
val UiVolume: Double = 100.0,
val MusicVolume: Double = 50.0,
val AmbientVolume: Double = 50.0,
val SfxVolume: Double = 50.0,
val HitmarkPlayerVolume: Double = 50.0,
val HitmarkEnemyVolume: Double = 50.0,
val Mute: Boolean = false,
val MuteInBackground: Boolean = false,
val IsEntranceSoundOn: Boolean = true,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceSoundSettings(
State0: SoundSettingsStateSlice,
Action: Any,
): SoundSettingsStateSlice = when (Action) {
is ApplySoundSettings -> Action.settings
is SetEntranceSoundSettings -> Action.settings
is EnableEntranceSound -> State0.copy(IsEntranceSoundOn = true)
is ResetSettingsFromStorage -> State0
else -> State0
}
@@ -0,0 +1,39 @@
// TouchscreenHudSettingsReducer.kt —— 触屏 HUD 布局切片(对应 module_10032 的 j7 状态 + P7.xsv)
//
// 证据链:
// 状态类 j7:1 个字段,toString 名 "TouchscreenHudSettings(mapping=…)"。
// 默认:mapping = c7().rsv_1。
// 根委派:`N7().xsv(t, n.epx_1)`。
// reducer:
// _C(ResetSettingsFromStorage)→ 从存储读默认布局(`T7(this)`)
// C7(UpdateSettings) → 整体替换 mapping(并写回存储)
// E7(无 toString) → 恢复默认 mapping(`new j7()`)
//
// storage 读写属 storage port 职责;`_C` 与 `E7` 都收口为「恢复空 mapping」。
package App.State.Reducers
/* ------------------------------------------------------------------ 动作 */
/** 原 `C7`(`UpdateSettings`,与其它设置切片同名):整体替换触屏布局。 */
data class ApplyTouchscreenHudSettings(val settings: TouchscreenHudSettingsStateSlice)
/** 原 `E7`:恢复默认触屏布局。 */
object ResetTouchscreenHudMapping
/* ------------------------------------------------------------------ 状态 */
data class TouchscreenHudSettingsStateSlice(
val Mapping: Map<Any?, Any?> = emptyMap(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceTouchscreenHudSettings(
State0: TouchscreenHudSettingsStateSlice,
Action: Any,
): TouchscreenHudSettingsStateSlice = when (Action) {
is ApplyTouchscreenHudSettings -> Action.settings
is ResetTouchscreenHudMapping -> TouchscreenHudSettingsStateSlice()
is ResetSettingsFromStorage -> TouchscreenHudSettingsStateSlice()
else -> State0
}
@@ -1,7 +1,7 @@
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
//
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
// 这里采用同一形状。已迁移的 66 个切片里,65 个真正调用 reducer,launchParams 只做类型收口:
// 这里采用同一形状。已迁移的 70 个切片里,69 个真正调用 reducer,launchParams 只做类型收口:
//
// inviteState → ReduceInvite(aD / pD.er5)
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
@@ -68,9 +68,13 @@
// clanChatState → ReduceClanChat(qnt / yv.qtg,根委派 `yv.qtg(t, n.ppv_1)`)
// battleSearchState → ReduceBattleSearch(Itt / Htt().vtg,根委派 `Htt().vtg(t, n.epw_1)`)
// battleStatistics → ReduceBattleStatistics(jR / rr.fqr,根委派 `rr.fqr(t, n.bpv_1)`)
// soundSettings → ReduceSoundSettings(Det / Wet().jth,根委派 `Wet().jth(t, n.vpw_1)`)
// gameSettings → ReduceGameSettings(c2 / g2().zss,根委派 `g2().zss(t, n.wpw_1)`)
// keyboardSettings → ReduceKeyboardSettings(Sut / bw.oth,根委派 `bw.oth(t, n.dpx_1)`)
// touchscreenHudSettings → ReduceTouchscreenHudSettings(j7 / N7().xsv,根委派 `N7().xsv(t, n.epx_1)`)
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
//
// 其余 23 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 19 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
@@ -96,9 +100,11 @@ import App.State.Reducers.ReduceEventGoods
import App.State.Reducers.ReduceGarage
import App.State.Reducers.ReduceGaragePreview
import App.State.Reducers.ReduceFriends
import App.State.Reducers.ReduceGameSettings
import App.State.Reducers.ReduceHome
import App.State.Reducers.ReduceIcons
import App.State.Reducers.ReduceInvite
import App.State.Reducers.ReduceKeyboardSettings
import App.State.Reducers.ReduceLanguage
import App.State.Reducers.ReduceLocalBattleUser
import App.State.Reducers.ReduceLoginByPassword
@@ -130,11 +136,13 @@ import App.State.Reducers.ReduceSpectator
import App.State.Reducers.ReduceSupportUrl
import App.State.Reducers.ReduceSettings
import App.State.Reducers.ReduceShop
import App.State.Reducers.ReduceSoundSettings
import App.State.Reducers.ReduceSupplies
import App.State.Reducers.ReduceSuppliesTutorial
import App.State.Reducers.ReduceSystem
import App.State.Reducers.ReduceTankActionsLog
import App.State.Reducers.ReduceTips
import App.State.Reducers.ReduceTouchscreenHudSettings
import App.State.Reducers.ReduceTwitch
import App.State.Reducers.ReduceTwoStepVerification
import App.State.Reducers.ReduceUser
@@ -219,6 +227,10 @@ object RootReducer {
ClanChatState = ReduceClanChat(State0.ClanChatState, Action),
BattleSearchState = ReduceBattleSearch(State0.BattleSearchState, Action),
BattleStatistics = ReduceBattleStatistics(State0.BattleStatistics, Action),
SoundSettings = ReduceSoundSettings(State0.SoundSettings, Action),
GameSettings = ReduceGameSettings(State0.GameSettings, Action),
KeyboardSettings = ReduceKeyboardSettings(State0.KeyboardSettings, Action),
TouchscreenHudSettings = ReduceTouchscreenHudSettings(State0.TouchscreenHudSettings, Action),
)
}
@@ -24,11 +24,13 @@ import App.State.Reducers.DroppableGoldStateSlice
import App.State.Reducers.EntranceStateSlice
import App.State.Reducers.EventGoodsStateSlice
import App.State.Reducers.FriendsStateSlice
import App.State.Reducers.GameSettingsStateSlice
import App.State.Reducers.GarageStateSlice
import App.State.Reducers.GaragePreviewStateSlice
import App.State.Reducers.HomeStateSlice
import App.State.Reducers.IconsStateSlice
import App.State.Reducers.InviteStateSlice
import App.State.Reducers.KeyboardSettingsStateSlice
import App.State.Reducers.LobbyLoaderStateSlice
import App.State.Reducers.LocalBattleUserStateSlice
import App.State.Reducers.LoginByPasswordStateSlice
@@ -56,6 +58,7 @@ import App.State.Reducers.ScreenStateSlice
import App.State.Reducers.SecuritySettingsStateSlice
import App.State.Reducers.SettingsStateSlice
import App.State.Reducers.ShopStateSlice
import App.State.Reducers.SoundSettingsStateSlice
import App.State.Reducers.SpectatorStateSlice
import App.State.Reducers.SuppliesStateSlice
import App.State.Reducers.SuppliesTutorialStateSlice
@@ -63,6 +66,7 @@ import App.State.Reducers.SupportUrlStateSlice
import App.State.Reducers.SystemStateSlice
import App.State.Reducers.TankActionsLogStateSlice
import App.State.Reducers.TipsStateSlice
import App.State.Reducers.TouchscreenHudSettingsStateSlice
import App.State.Reducers.TwitchStateSlice
import App.State.Reducers.TwoStepVerificationStateSlice
import App.State.Reducers.UserStateSlice
@@ -172,10 +176,10 @@ data class RootState(
val Home: HomeStateSlice = HomeStateSlice(),
/** 原 localBattleUserState(lE / (inline@localBattleUserState))。已迁移。 */
val LocalBattleUserState: LocalBattleUserStateSlice = LocalBattleUserStateSlice(),
/** 原 soundSettings(Det / Wet().jth)。未迁移:原样透传。 */
val SoundSettings: Any? = null,
/** 原 gameSettings(c2 / g2().zss)。未迁移:原样透传。 */
val GameSettings: Any? = null,
/** 原 soundSettings(Det / Wet().jth)。已迁移。 */
val SoundSettings: SoundSettingsStateSlice = SoundSettingsStateSlice(),
/** 原 gameSettings(c2 / g2().zss)。已迁移。 */
val GameSettings: GameSettingsStateSlice = GameSettingsStateSlice(),
/** 原 battleSettings(W0 / X0().esq)。未迁移:原样透传。 */
val BattleSettings: Any? = null,
/** 原 graphicsSettings(? / lc.isu)。未迁移:原样透传。 */
@@ -188,10 +192,10 @@ data class RootState(
val PurchaseDialogState: PurchaseDialogStateSlice = PurchaseDialogStateSlice(),
/** 原 battleInviteState(att / Ttt().nth)。已迁移。 */
val BattleInviteState: BattleInviteStateSlice = BattleInviteStateSlice(),
/** 原 keyboardSettings(Sut / bw.oth)。未迁移:原样透传。 */
val KeyboardSettings: Any? = null,
/** 原 touchscreenHudSettings(j7 / N7().xsv)。未迁移:原样透传。 */
val TouchscreenHudSettings: Any? = null,
/** 原 keyboardSettings(Sut / bw.oth)。已迁移。 */
val KeyboardSettings: KeyboardSettingsStateSlice = KeyboardSettingsStateSlice(),
/** 原 touchscreenHudSettings(j7 / N7().xsv)。已迁移。 */
val TouchscreenHudSettings: TouchscreenHudSettingsStateSlice = TouchscreenHudSettingsStateSlice(),
/** 原 mainQuest(WZ / ih.bsk)。已迁移。 */
val MainQuest: MainQuestStateSlice = MainQuestStateSlice(),
/** 原 language(? / m6().jtg)。已迁移。 */
@@ -30,7 +30,7 @@ object RootWiring {
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")),
SliceWiring("chatState", "opv_1", "LP", "\$r.wqv", "\$r.wqv(t, n.opv_1)", listOf("?QP", "?XP", "?_C", "?bN", "?fN", "?lN", "?nN", "?pN", "?qN", "?sN", "?vN", "?wN", "ChangeChannel", "CleanUsersMessage", "Configure", "IgnoreUserMessages", "SaveChatMessage", "SendMessageToServer", "SetMessages", "SetSelectedUid", "ShowContextMenu", "ShowUserMessages", "UpdateTypingSpeedAntifloodParams", "UpdateUsers")),
SliceWiring("chatState", "opv_1", "LP", "\$r.wqv", "\$r.wqv(t, n.opv_1)", listOf("?QP", "?XP", "?bN", "?fN", "?lN", "?nN", "?pN", "?qN", "?sN", "?vN", "?wN", "ChangeChannel", "CleanUsersMessage", "Configure", "IgnoreUserMessages", "ResetSettingsFromStorage", "SaveChatMessage", "SendMessageToServer", "SetMessages", "SetSelectedUid", "ShowContextMenu", "ShowUserMessages", "UpdateTypingSpeedAntifloodParams", "UpdateUsers")),
SliceWiring("clanChatState", "ppv_1", "qnt", "yv.qtg", "yv.qtg(t, n.ppv_1)", listOf("CloseClanChat", "Configure", "DoAddMessages", "OpenClanChat", "ResetClanChat")),
SliceWiring("clan", "qpv_1", "jN", "we.hr0", "we.hr0(t, n.qpv_1)", listOf("?FM", "?IM", "?KM", "?PM", "?RM", "?VM", "?YM", "?iM", "AcceptedUserViewed", "AddIncomingClan", "AddNewIncoming", "AddNewIncomingClan", "AddOutgoingClan", "AddUsersToAccepted", "AddUsersToNewAccepted", "ClanBanned", "ClanInfoLoaded", "ClanLoaded", "ClanModalVisible", "IncomingAdded", "IncomingRemoved", "IncomingUserViewed", "MembersFetched", "NewAcceptedUserNotification", "NewClanInviteNotification", "NewIncomingUserNotification", "OutgoingAdded", "OutgoingRemoved", "RemoveIncomingClan", "RemoveNewIncoming", "RemoveNewIncomingClan", "RemoveOutgoingClan", "RemoveUserFromAccepted", "RemoveUsersFromNewAccepted", "SetClanEditPrice", "SetMinRankForCreate", "UpdateClanLicense", "UpdateClanNameValidation", "UpdateClanSearchValidation", "UpdateClanTagValidation", "UpdateHasClan", "UpdateTimeToJoinRestriction", "UpdateValidation")),
SliceWiring("clansStorage", "rpv_1", "lO", "me.vr1", "me.vr1(t, n.rpv_1)", listOf("?mO", "AddUser", "AddUsersToNewAccepted", "BuyAdditionalPlace", "ClanLoaded", "ClanUnloaded", "ForeignClanLoaded", "MinimalRankChanged", "RecruitmentChanged", "RemoveUser", "RemoveUsersFromNewAccepted", "ShowForeignClan", "UpdateClan")),
@@ -63,16 +63,16 @@ object RootWiring {
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")),
SliceWiring("gameSettings", "wpw_1", "c2", "g2().zss", "g2().zss(t, n.wpw_1)", listOf("?_C", "UpdateSettings")),
SliceWiring("battleSettings", "xpw_1", "W0", "X0().esq", "X0().esq(t, n.xpw_1)", listOf("?_C", "UpdateSettings")),
SliceWiring("soundSettings", "vpw_1", "Det", "Wet().jth", "Wet().jth(t, n.vpw_1)", listOf("ApplySoundSettings", "EnableEntranceSound", "ResetSettingsFromStorage", "SetEntranceSoundSettings")),
SliceWiring("gameSettings", "wpw_1", "c2", "g2().zss", "g2().zss(t, n.wpw_1)", listOf("ApplyGameSettings", "ResetSettingsFromStorage")),
SliceWiring("battleSettings", "xpw_1", "W0", "X0().esq", "X0().esq(t, n.xpw_1)", listOf("ResetSettingsFromStorage", "UpdateSettings")),
SliceWiring("graphicsSettings", "ypw_1", "?", "lc.isu", "lc.isu(t, n.ypw_1)", listOf("UpdateSettings")),
SliceWiring("controlSettings", "zpw_1", "i2", "_2().ess", "_2().ess(t, n.zpw_1)", listOf("?_C", "UpdateSettings")),
SliceWiring("controlSettings", "zpw_1", "i2", "_2().ess", "_2().ess(t, n.zpw_1)", listOf("ResetSettingsFromStorage", "UpdateSettings")),
SliceWiring("securitySettings", "apx_1", "V2", "pc.qsv", "pc.qsv(t, n.apx_1)", listOf("SetProfileBlockStatus")),
SliceWiring("purchaseDialogState", "bpx_1", "CO", "je.ur2", "je.ur2(t, n.bpx_1)", listOf("ChangeEmailStatus", "ResetPurchaseDialog", "SuccessfullyPurchase")),
SliceWiring("battleInviteState", "cpx_1", "att", "Ttt().nth", "Ttt().nth(t, n.cpx_1)", listOf("AddInvitedUser", "ClearSelectedForInvite", "EnableSetBattleInviteNotifications", "InvitationResponse", "RemoveUserFromInvited", "SelectUserToInvite")),
SliceWiring("keyboardSettings", "dpx_1", "Sut", "bw.oth", "bw.oth(t, n.dpx_1)", listOf("?Iut", "?_C", "ChangeAction", "RemoveAction", "ResetAction")),
SliceWiring("touchscreenHudSettings", "epx_1", "j7", "N7().xsv", "N7().xsv(t, n.epx_1)", listOf("?E7", "?_C", "UpdateSettings")),
SliceWiring("keyboardSettings", "dpx_1", "Sut", "bw.oth", "bw.oth(t, n.dpx_1)", listOf("ChangeAction", "RemoveAction", "ResetAction", "ResetKeyMappingFromDefaults", "ResetSettingsFromStorage")),
SliceWiring("touchscreenHudSettings", "epx_1", "j7", "N7().xsv", "N7().xsv(t, n.epx_1)", listOf("ApplyTouchscreenHudSettings", "ResetSettingsFromStorage", "ResetTouchscreenHudMapping")),
SliceWiring("mainQuest", "fpx_1", "WZ", "ih.bsk", "ih.bsk(t, n.fpx_1)", listOf("AddRewardedQuest", "QuestsLoaded", "ResetQuestsLoaded", "SetBattleUpdatedQuests", "SetEventPassShopItemId", "SetQuests", "UpdateQuests")),
SliceWiring("language", "gpx_1", "?", "m6().jtg", "m6().jtg(t, n.gpx_1)", listOf("SetLanguage")),
SliceWiring("launchParams", "hpx_1", "?", "(pass-through)", "n.hpx_1", listOf()),
@@ -91,7 +91,7 @@ object RootWiring {
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")),
SliceWiring("icons", "wpx_1", "oH", "vs.kru", "vs.kru(t, n.wpx_1)", listOf("SetIcons")),
SliceWiring("itemNotifications", "xpx_1", "_1", "A1().dso", "A1().dso(t, n.xpx_1)", listOf("?S1", "?_C", "AddItemNotification", "AddItemNotifications", "RemoveItemNotification", "UpdateItemNotification")),
SliceWiring("itemNotifications", "xpx_1", "_1", "A1().dso", "A1().dso(t, n.xpx_1)", listOf("?S1", "AddItemNotification", "AddItemNotifications", "RemoveItemNotification", "ResetSettingsFromStorage", "UpdateItemNotification")),
SliceWiring("contextTutorial", "ypx_1", "Mst", "Jst().tth", "Jst().tth(t, n.ypx_1)", listOf("LoadCurrentElement", "MarkElementAsViewed", "SetLoadedElements", "SetMissionToElement", "SetUnViewedElements")),
SliceWiring("suppliesTutorial", "zpx_1", "ust", "Uw.uth", "Uw.uth(t, n.zpx_1)", listOf("ActivatedInCurrentBattle", "SetLoadedElement")),
SliceWiring("questCategories", "apy_1", "zZ", "th.lsh", "th.lsh(t, n.apy_1)", listOf("RemoveQuestCategory", "RequestQuestCategoriesStatistic", "SetQuestCategories", "SetQuestCategoriesStatistic")),
@@ -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")
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")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
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;
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings;
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
@@ -667,6 +667,25 @@ import App.State.Reducers.ResetBattleStatistics
import App.State.Reducers.ResetRoundState
import App.State.Reducers.ShowStat
import App.State.Reducers.ToggleStatMenu
import App.State.ChangeAction
import App.State.RemoveAction
import App.State.ResetAction
import App.State.Reducers.ApplyGameSettings
import App.State.Reducers.ApplySoundSettings
import App.State.Reducers.ApplyTouchscreenHudSettings
import App.State.Reducers.EnableEntranceSound
import App.State.Reducers.GameSettingsStateSlice
import App.State.Reducers.KeyboardSettingsStateSlice
import App.State.Reducers.ReduceGameSettings
import App.State.Reducers.ReduceKeyboardSettings
import App.State.Reducers.ReduceSoundSettings
import App.State.Reducers.ReduceTouchscreenHudSettings
import App.State.Reducers.ResetKeyMappingFromDefaults
import App.State.Reducers.ResetSettingsFromStorage
import App.State.Reducers.ResetTouchscreenHudMapping
import App.State.Reducers.SetEntranceSoundSettings
import App.State.Reducers.SoundSettingsStateSlice
import App.State.Reducers.TouchscreenHudSettingsStateSlice
import App.State.Reducers.UserRankBounds
import App.State.Reducers.UserStateSlice
import App.State.Reducers.ClearHullData
@@ -1094,7 +1113,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 66 个已迁移切片(65 归约 + launchParams 透传)
// ⑭ 根 reducer:89 个槽位的委派表 + 70 个已迁移切片(69 归约 + launchParams 透传)
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -1158,7 +1177,7 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 66 个已迁移字段,其余 23 个待迁",
"根:迁移表 = 70 个已迁移字段,其余 19 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
@@ -1179,8 +1198,9 @@ object StateCheck {
"partner", "userRenameState",
"battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial",
"clanChatState", "battleSearchState", "battleStatistics",
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
) &&
RootWiring.Unmigrated.size == 23
RootWiring.Unmigrated.size == 19
)
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
@@ -1236,7 +1256,7 @@ object StateCheck {
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 66/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 70/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -5742,6 +5762,211 @@ object StateCheck {
)
}
// 77 切片 reducer:SoundSettings(Det / Wet().jth,10 字段;4 个动作)
run {
val S0 = SoundSettingsStateSlice()
Check(
"切片 SoundSettings:默认音量与开关",
S0.MasterVolume == 100.0 && S0.UiVolume == 100.0 &&
S0.MusicVolume == 50.0 && S0.AmbientVolume == 50.0 &&
S0.SfxVolume == 50.0 && S0.HitmarkPlayerVolume == 50.0 &&
S0.HitmarkEnemyVolume == 50.0 && !S0.Mute &&
!S0.MuteInBackground && S0.IsEntranceSoundOn
)
val Custom = SoundSettingsStateSlice(
MasterVolume = 10.0, UiVolume = 20.0, MusicVolume = 30.0,
AmbientVolume = 40.0, SfxVolume = 25.0, HitmarkPlayerVolume = 15.0,
HitmarkEnemyVolume = 5.0, Mute = true, MuteInBackground = true,
IsEntranceSoundOn = false,
)
Check(
"切片 SoundSettings:ApplySoundSettings 整体替换",
ReduceSoundSettings(S0, ApplySoundSettings(Custom)) == Custom
)
Check(
"切片 SoundSettings:SetEntranceSoundSettings 整体替换",
ReduceSoundSettings(S0, SetEntranceSoundSettings(Custom)) == Custom
)
Check(
"切片 SoundSettings:EnableEntranceSound 只开入口音效",
ReduceSoundSettings(Custom, EnableEntranceSound)
.let { it.IsEntranceSoundOn && it.Mute }
)
Check(
"切片 SoundSettings:ResetSettingsFromStorage 不做 IO、保留状态",
ReduceSoundSettings(Custom, ResetSettingsFromStorage) == Custom
)
Check(
"切片 SoundSettings:无关动作不改状态",
ReduceSoundSettings(Custom, AddFrags(1)) == Custom
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, EnableEntranceSound)
Check(
"根:EnableEntranceSound 只动 soundSettings",
Root1.SoundSettings.IsEntranceSoundOn &&
Root1.GameSettings == Root0.GameSettings
)
val W = RootWiring.ByField("soundSettings")!!
Check(
"根:soundSettings 委派到 Det / Wet().jth 且 4 个动作",
W.SliceClass == "Det" && W.Reducer == "Wet().jth" &&
W.Actions.toSet() == setOf(
"ApplySoundSettings", "SetEntranceSoundSettings", "EnableEntranceSound",
"ResetSettingsFromStorage",
)
)
}
// 78 切片 reducer:GameSettings(c2 / g2().zss,6 字段;2 个动作)
run {
val S0 = GameSettingsStateSlice()
Check(
"切片 GameSettings:默认五个开关全开、padding 为 null",
S0.ShowDropZones && S0.ShowYourDrone && S0.ShowChat &&
S0.ShowNotifications && S0.ShowOtherDrone && S0.MobilePadding == null
)
val Custom = GameSettingsStateSlice(
ShowDropZones = false, ShowYourDrone = false, ShowChat = false,
ShowNotifications = false, ShowOtherDrone = false, MobilePadding = 32,
)
Check(
"切片 GameSettings:ApplyGameSettings 整体替换",
ReduceGameSettings(S0, ApplyGameSettings(Custom)) == Custom
)
Check(
"切片 GameSettings:ResetSettingsFromStorage 保留状态",
ReduceGameSettings(Custom, ResetSettingsFromStorage) == Custom
)
Check(
"切片 GameSettings:无关动作不改状态",
ReduceGameSettings(Custom, AddFrags(1)) == Custom
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ApplyGameSettings(Custom))
Check(
"根:ApplyGameSettings 只动 gameSettings",
Root1.GameSettings == Custom && Root1.KeyboardSettings == Root0.KeyboardSettings
)
val W = RootWiring.ByField("gameSettings")!!
Check(
"根:gameSettings 委派到 c2 / g2().zss 且 2 个动作",
W.SliceClass == "c2" && W.Reducer == "g2().zss" &&
W.Actions.toSet() == setOf("ApplyGameSettings", "ResetSettingsFromStorage")
)
}
// 79 切片 reducer:KeyboardSettings(Sut / bw.oth,1 字段;5 个动作)
run {
val S0 = KeyboardSettingsStateSlice()
Check("切片 KeyboardSettings:默认空绑定表", S0.Mapping.isEmpty())
val S1 = ReduceKeyboardSettings(S0, ChangeAction("fire", 0, "KeyF"))
Check(
"切片 KeyboardSettings:ChangeAction 写入 action→index→keyState",
S1.Mapping == mapOf<Any?, Any?>(
"fire" to mapOf<Any?, Any?>(0 to "KeyF"),
)
)
val S2 = ReduceKeyboardSettings(S1, ChangeAction("fire", 1, "KeyG"))
Check(
"切片 KeyboardSettings:同一 action 不同 index 追加绑定",
S2.Mapping == mapOf<Any?, Any?>(
"fire" to mapOf<Any?, Any?>(0 to "KeyF", 1 to "KeyG"),
)
)
Check(
"切片 KeyboardSettings:同一 keyState 从别的 action 上解绑",
ReduceKeyboardSettings(
ReduceKeyboardSettings(S0, ChangeAction("fire", 0, "KeyF")),
ChangeAction("jump", 0, "KeyF"),
).Mapping == mapOf<Any?, Any?>(
"fire" to emptyMap<Any?, Any?>(),
"jump" to mapOf<Any?, Any?>(0 to "KeyF"),
)
)
Check(
"切片 KeyboardSettings:RemoveAction 解绑一个 index",
ReduceKeyboardSettings(S2, RemoveAction("fire", 1)).Mapping ==
mapOf<Any?, Any?>("fire" to mapOf<Any?, Any?>(0 to "KeyF"))
)
Check(
"切片 KeyboardSettings:RemoveAction 对未知 action 是 no-op",
ReduceKeyboardSettings(S2, RemoveAction("nope", 0)) == S2
)
Check(
"切片 KeyboardSettings:ResetAction 保留状态(外部 helper)",
ReduceKeyboardSettings(S2, ResetAction("fire")) == S2
)
Check(
"切片 KeyboardSettings:两个默认复位都清空绑定表",
ReduceKeyboardSettings(S2, ResetSettingsFromStorage) ==
KeyboardSettingsStateSlice() &&
ReduceKeyboardSettings(S2, ResetKeyMappingFromDefaults) ==
KeyboardSettingsStateSlice()
)
Check(
"切片 KeyboardSettings:无关动作不改状态",
ReduceKeyboardSettings(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, RemoveAction("fire", 1))
Check(
"根:RemoveAction 只动 keyboardSettings",
Root1.KeyboardSettings == KeyboardSettingsStateSlice() &&
Root1.TouchscreenHudSettings == Root0.TouchscreenHudSettings
)
val W = RootWiring.ByField("keyboardSettings")!!
Check(
"根:keyboardSettings 委派到 Sut / bw.oth 且 5 个动作",
W.SliceClass == "Sut" && W.Reducer == "bw.oth" && W.Actions.size == 5
)
}
// 80 切片 reducer:TouchscreenHudSettings(j7 / N7().xsv,1 字段;3 个动作)
run {
val S0 = TouchscreenHudSettingsStateSlice()
Check("切片 TouchscreenHudSettings:默认空布局", S0.Mapping.isEmpty())
val Custom = TouchscreenHudSettingsStateSlice(mapOf<Any?, Any?>("fire" to 1))
Check(
"切片 TouchscreenHudSettings:ApplyTouchscreenHudSettings 整体替换",
ReduceTouchscreenHudSettings(S0, ApplyTouchscreenHudSettings(Custom)) == Custom
)
Check(
"切片 TouchscreenHudSettings:ResetTouchscreenHudMapping 恢复默认",
ReduceTouchscreenHudSettings(Custom, ResetTouchscreenHudMapping) ==
TouchscreenHudSettingsStateSlice()
)
Check(
"切片 TouchscreenHudSettings:ResetSettingsFromStorage 恢复默认",
ReduceTouchscreenHudSettings(Custom, ResetSettingsFromStorage) ==
TouchscreenHudSettingsStateSlice()
)
Check(
"切片 TouchscreenHudSettings:无关动作不改状态",
ReduceTouchscreenHudSettings(Custom, AddFrags(1)) == Custom
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ResetTouchscreenHudMapping)
Check(
"根:ResetTouchscreenHudMapping 只动 touchscreenHudSettings",
Root1.TouchscreenHudSettings == TouchscreenHudSettingsStateSlice() &&
Root1.SoundSettings == Root0.SoundSettings
)
val W = RootWiring.ByField("touchscreenHudSettings")!!
Check(
"根:touchscreenHudSettings 委派到 j7 / N7().xsv 且 3 个动作",
W.SliceClass == "j7" && W.Reducer == "N7().xsv" &&
W.Actions.toSet() == setOf(
"ApplyTouchscreenHudSettings", "ResetTouchscreenHudMapping",
"ResetSettingsFromStorage",
)
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)