feat(state): 迁移 graphicsSettings/controlSettings/battleSettings

This commit is contained in:
TermiNexus committed 2026-10-24 17:24:01 +08:00
1 parent cade4020a5
commit 0d1e2d00c5
10 files changed
+408 -34

No files matched your search

+16 -6
View File
@@ -143,7 +143,7 @@
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
- 已迁移切片 **70/89**:
- 已迁移切片 **73/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)
@@ -361,12 +361,21 @@
`ResetAction` 依赖外部 helper,纯 reducer 保留原状态;两个默认复位清空绑定表。
- `touchscreenHudSettings` → `j7` / `N7().xsv`(`TouchscreenHudSettingsReducer.kt`):1 字段,3 个分支。
`C7`(原名 `UpdateSettings`)改名为 `ApplyTouchscreenHudSettings`;`E7`→`ResetTouchscreenHudMapping` 恢复默认布局。
- `graphicsSettings` → `L2` / `lc.isu`(`GraphicsSettingsReducer.kt`):17 字段,1 个分支。
`B2`(原名 `UpdateSettings`)改名为 `ApplyGraphicsSettings` 整体替换;画质枚举按 `Any?` 收口,
vsync 变化触发的重启提示是副作用,留给中间件层。
- `controlSettings` → `i2` / `_2().ess`(`ControlSettingsReducer.kt`):7 字段,2 个分支。
嵌套模型收口为 `ControlMouseSettings` / `ControlGamepadSettings` / `ControlTouchscreenSettings`
(缺省 50/true、true/13/13、50/true/false/true/true/true/true);
`u2`(原名 `UpdateSettings`)改名为 `ApplyControlSettings`。
- `battleSettings` → `W0` / `X0().esq`(`BattleSettingsReducer.kt`):11 字段,2 个分支。
`K0`(原名 `UpdateSettings`)改名为 `ApplyBattleSettings`;`_C` 的旧键迁移属 storage port,保留现状态。
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 69 个有 reducer 的切片,
`launchParams` 与其余 **19 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 70/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **787 项全绿**,含 89 槽位完整性、69 个归约切片一次归约互不串片、未迁移字段透传,
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 72 个有 reducer 的切片,
`launchParams` 与其余 **16 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 73/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **804 项全绿**,含 89 槽位完整性、72 个归约切片一次归约互不串片、未迁移字段透传,
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
@@ -389,7 +398,8 @@
clanChatState 的 3 个(`$nt`/`xnt`/`Tnt`)、battleSearchState 的 1 个也已登记。
battleStatistics 的 12 个(含改名的 `FR`→`InitBattle`)同样按效果定名。
设置类切片的 `_C`(共用重载)+ `Bet`/`Fet`/`Uet`/`v2`/`C7`/`E7`/`Iut` 也已登记,
其中四个原名都是 `UpdateSettings` 的类按切片改名为 `Apply*`,避免生成物同名冲突。
其中原名都是 `UpdateSettings` 的类按切片改名为 `Apply*`,避免生成物同名冲突
(`Bet`/`v2`/`B2`/`u2`/`K0`/`C7` 共 6 个)。
### S4 引擎核心
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
+32 -11
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-10-24T09:04:37.952Z",
"generatedAt": "2026-10-24T09:19:04.205Z",
"module": "module_10032.js",
"RootState": {
"Class": "z6",
@@ -76,7 +76,10 @@
"soundSettings",
"gameSettings",
"keyboardSettings",
"touchscreenHudSettings"
"touchscreenHudSettings",
"graphicsSettings",
"controlSettings",
"battleSettings"
],
"AnonymousActions": [
{
@@ -1333,6 +1336,24 @@
"Name": "ResetKeyMappingFromDefaults",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "恢复键位默认绑定(`rut().mu7_1`)"
},
{
"Local": "B2",
"Name": "ApplyGraphicsSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "画质设置整体替换(原名 UpdateSettings)"
},
{
"Local": "u2",
"Name": "ApplyControlSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "操作设置整体替换(原名 UpdateSettings)"
},
{
"Local": "K0",
"Name": "ApplyBattleSettings",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "战斗显示设置整体替换(原名 UpdateSettings)"
}
],
"Markers": [
@@ -2524,23 +2545,23 @@
"Reducer": "X0().esq",
"Delegate": "X0().esq(t, n.xpw_1)",
"Actions": [
"ResetSettingsFromStorage",
"UpdateSettings"
"ApplyBattleSettings",
"ResetSettingsFromStorage"
],
"Default": "new W0()",
"Migrated": false
"Migrated": true
},
{
"Field": "graphicsSettings",
"Slot": "ypw_1",
"SliceClass": null,
"SliceClass": "L2",
"Reducer": "lc.isu",
"Delegate": "lc.isu(t, n.ypw_1)",
"Actions": [
"UpdateSettings"
"ApplyGraphicsSettings"
],
"Default": "vc.dsv()",
"Migrated": false
"Migrated": true
},
{
"Field": "controlSettings",
@@ -2549,11 +2570,11 @@
"Reducer": "_2().ess",
"Delegate": "_2().ess(t, n.zpw_1)",
"Actions": [
"ResetSettingsFromStorage",
"UpdateSettings"
"ApplyControlSettings",
"ResetSettingsFromStorage"
],
"Default": "new i2()",
"Migrated": false
"Migrated": true
},
{
"Field": "securitySettings",
+15
View File
@@ -307,6 +307,10 @@ const AnonymousActionNames = new Map([
['E7', 'ResetTouchscreenHudMapping'],
// keyboardSettings 切片(Sut / Nut.oth):Iut 没有 toString。
['Iut', 'ResetKeyMappingFromDefaults'],
// graphicsSettings / controlSettings / battleSettings 的 UpdateSettings 同名类。
['B2', 'ApplyGraphicsSettings'],
['u2', 'ApplyControlSettings'],
['K0', 'ApplyBattleSettings'],
]);
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
@@ -773,6 +777,14 @@ const Migrated = {
Type: 'TouchscreenHudSettingsStateSlice',
Reduce: 'ReduceTouchscreenHudSettings',
},
// 根构造里 graphicsSettings 的默认值不是 `new <Class>()`,生成器认不出切片类,这里显式补。
graphicsSettings: {
Type: 'GraphicsSettingsStateSlice',
SliceClass: 'L2',
Reduce: 'ReduceGraphicsSettings',
},
controlSettings: { Type: 'ControlSettingsStateSlice', Reduce: 'ReduceControlSettings' },
battleSettings: { Type: 'BattleSettingsStateSlice', Reduce: 'ReduceBattleSettings' },
};
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
@@ -1173,6 +1185,9 @@ const AppSettingsAnonymousEffects = {
C7: '触屏布局整体替换(原名 UpdateSettings)',
E7: '恢复默认触屏布局(`new j7()`)',
Iut: '恢复键位默认绑定(`rut().mu7_1`)',
B2: '画质设置整体替换(原名 UpdateSettings)',
u2: '操作设置整体替换(原名 UpdateSettings)',
K0: '战斗显示设置整体替换(原名 UpdateSettings)',
};
fs.writeFileSync(
@@ -0,0 +1,46 @@
// BattleSettingsReducer.kt —— 战斗显示设置切片(对应 module_10032 的 W0 状态 + Y0.esq)
//
// 证据链:
// 状态类 W0:11 个字段,toString 名 "BattleSettings(…)"。
// 默认:6 个显示开关 true(dot/damage/tankPointers/buttonLabels/autoSuicide/statusName),
// isCenteredLayout=false、battleUISize=50、aimhelperDotOpacity=50、
// aimhelperDotScale=1、autoSupply=true。
// 根委派:`X0().esq(t, n.xpw_1)`。
// reducer:
// _C(ResetSettingsFromStorage)→ 从 localStorage 读旧键迁移并重建整套设置
// K0(UpdateSettings) → 整体替换设置对象(并写回存储)
//
// storage 迁移 / 写回属 storage port 职责,纯 reducer 不做 IO(`_C` 保留现有状态)。
package App.State.Reducers
/* ------------------------------------------------------------------ 动作 */
/** 原 `K0`(原名 `UpdateSettings`,与其它设置切片同名):整体替换战斗显示设置。 */
data class ApplyBattleSettings(val settings: BattleSettingsStateSlice)
/* ------------------------------------------------------------------ 状态 */
data class BattleSettingsStateSlice(
val ShowAimHelperDot: Boolean = true,
val ShowDamage: Boolean = true,
val ShowTankPointers: Boolean = true,
val ShowButtonLabels: Boolean = true,
val AutoSuicide: Boolean = true,
val ShowStatusName: Boolean = true,
val IsCenteredLayout: Boolean = false,
val BattleUISize: Double = 50.0,
val AimhelperDotOpacity: Double = 50.0,
val AimhelperDotScale: Double = 1.0,
val AutoSupply: Boolean = true,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceBattleSettings(
State0: BattleSettingsStateSlice,
Action: Any,
): BattleSettingsStateSlice = when (Action) {
is ApplyBattleSettings -> Action.settings
is ResetSettingsFromStorage -> State0
else -> State0
}
@@ -0,0 +1,70 @@
// ControlSettingsReducer.kt —— 操作设置切片(对应 module_10032 的 i2 状态 + o2.ess)
//
// 证据链:
// 状态类 i2:7 个字段,toString 名 "ControlSettings(…)"。
// 默认:availableInputModes=空集合(服务缺省)、inputMode=null、
// inversedBackwardMoving=false、invertShaftVerticalAxis=false、
// mouse=Q0(50, true)、gamepad=t2(true, 13, 13)、touchscreen=n2(50, true, false, true, true, true, true)。
// 根委派:`_2().ess(t, n.zpw_1)`。
// reducer:
// _C(ResetSettingsFromStorage)→ 从存储 + 输入服务重建整套设置
// u2(UpdateSettings) → 整体替换设置对象(并写回存储 / 输入服务)
//
// 嵌套模型按源码构造参数收口:
// Mouse(cameraSensitivity, enableAcceleration)
// Gamepad(enabled, leftStickDeadZone, rightStickDeadZone)
// Touchscreen(cameraSensitivity, rotateJoystick, movableJoystick, doubleTapFire,
// autoFire, showMovementIndicator, autoCenterTurret)
// storage / 输入服务写回属 side effect,纯 reducer 不做 IO(`_C` 保留现有状态)。
package App.State.Reducers
/* ------------------------------------------------------------------ 动作 */
/** 原 `u2`(原名 `UpdateSettings`,与其它设置切片同名):整体替换操作设置。 */
data class ApplyControlSettings(val settings: ControlSettingsStateSlice)
/* ------------------------------------------------------------------ 嵌套模型 */
data class ControlMouseSettings(
val CameraSensitivity: Double = 50.0,
val EnableAcceleration: Boolean = true,
)
data class ControlGamepadSettings(
val Enabled: Boolean = true,
val LeftStickDeadZone: Int = 13,
val RightStickDeadZone: Int = 13,
)
data class ControlTouchscreenSettings(
val CameraSensitivity: Double = 50.0,
val RotateJoystick: Boolean = true,
val MovableJoystick: Boolean = false,
val DoubleTapFire: Boolean = true,
val AutoFire: Boolean = true,
val ShowMovementIndicator: Boolean = true,
val AutoCenterTurret: Boolean = true,
)
/* ------------------------------------------------------------------ 状态 */
data class ControlSettingsStateSlice(
val AvailableInputModes: Set<Any?> = emptySet(),
val InputMode: Any? = null,
val InversedBackwardMoving: Boolean = false,
val InvertShaftVerticalAxis: Boolean = false,
val Mouse: ControlMouseSettings = ControlMouseSettings(),
val Gamepad: ControlGamepadSettings = ControlGamepadSettings(),
val Touchscreen: ControlTouchscreenSettings = ControlTouchscreenSettings(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceControlSettings(
State0: ControlSettingsStateSlice,
Action: Any,
): ControlSettingsStateSlice = when (Action) {
is ApplyControlSettings -> Action.settings
is ResetSettingsFromStorage -> State0
else -> State0
}
@@ -0,0 +1,51 @@
// GraphicsSettingsReducer.kt —— 画质设置切片(对应 module_10032 的 L2 状态 + U2.isu)
//
// 证据链:
// 状态类 L2:17 个字段,toString 名 "GraphicsSettings(…)"。
// 默认:qualityDetectByCardCompleted=null、extended=false、fpsLimit=0(服务缺省)、
// showFPSAndPing/showSkybox=true、dynamicShadows/dynamicLighting/softParticles=false、
// fog=true、antialiasing=false、vsync=true、decalsProfile=null、dustEnabled=false、
// anisotropicFilteringLevel=0、treesQuality/effectsQuality/resolutionScale=null。
// 根委派:`lc.isu(t, n.ypw_1)`。
// reducer:`t instanceof B2 ? t.hsu_1 : n` —— 整体替换设置对象。
//
// 各类画质枚举(decals / trees / effects / resolution)来自画质服务,按 `Any?` 收口;
// 原 reducer 在 vsync 变化时还会派发重启提示(副作用),留给中间件层。
package App.State.Reducers
/* ------------------------------------------------------------------ 动作 */
/** 原 `B2`(原名 `UpdateSettings`,与其它设置切片同名):整体替换画质设置。 */
data class ApplyGraphicsSettings(val settings: GraphicsSettingsStateSlice)
/* ------------------------------------------------------------------ 状态 */
data class GraphicsSettingsStateSlice(
val QualityDetectByCardCompleted: Boolean? = null,
val Extended: Boolean = false,
val FpsLimit: Int = 0,
val ShowFPSAndPing: Boolean = true,
val ShowSkybox: Boolean = true,
val DynamicShadows: Boolean = false,
val DynamicLighting: Boolean = false,
val SoftParticles: Boolean = false,
val Fog: Boolean = true,
val Antialiasing: Boolean = false,
val Vsync: Boolean = true,
val DecalsProfile: Any? = null,
val DustEnabled: Boolean = false,
val AnisotropicFilteringLevel: Int = 0,
val TreesQuality: Any? = null,
val EffectsQuality: Any? = null,
val ResolutionScale: Any? = null,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceGraphicsSettings(
State0: GraphicsSettingsStateSlice,
Action: Any,
): GraphicsSettingsStateSlice = when (Action) {
is ApplyGraphicsSettings -> Action.settings
else -> State0
}
@@ -1,7 +1,7 @@
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
//
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
// 这里采用同一形状。已迁移的 70 个切片里,69 个真正调用 reducer,launchParams 只做类型收口:
// 这里采用同一形状。已迁移的 73 个切片里,72 个真正调用 reducer,launchParams 只做类型收口:
//
// inviteState → ReduceInvite(aD / pD.er5)
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
@@ -72,9 +72,12 @@
// 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)`)
// graphicsSettings → ReduceGraphicsSettings(L2 / lc.isu,根委派 `lc.isu(t, n.ypw_1)`)
// controlSettings → ReduceControlSettings(i2 / _2().ess,根委派 `_2().ess(t, n.zpw_1)`)
// battleSettings → ReduceBattleSettings(W0 / X0().esq,根委派 `X0().esq(t, n.xpw_1)`)
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
//
// 其余 19 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 16 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
@@ -85,6 +88,7 @@ import App.State.Reducers.ReduceAntiAddiction
import App.State.Reducers.ReduceBattleResult
import App.State.Reducers.ReduceBattlePause
import App.State.Reducers.ReduceBattleSearch
import App.State.Reducers.ReduceBattleSettings
import App.State.Reducers.ReduceBattleStatistics
import App.State.Reducers.ReduceBattleTutorial
import App.State.Reducers.ReduceBattleView
@@ -93,6 +97,7 @@ import App.State.Reducers.ReduceCaptcha
import App.State.Reducers.ReduceClanUserInfo
import App.State.Reducers.ReduceClanChat
import App.State.Reducers.ReduceContextTutorial
import App.State.Reducers.ReduceControlSettings
import App.State.Reducers.ReduceCreatorTag
import App.State.Reducers.ReduceDroppableGold
import App.State.Reducers.ReduceEntrance
@@ -101,6 +106,7 @@ import App.State.Reducers.ReduceGarage
import App.State.Reducers.ReduceGaragePreview
import App.State.Reducers.ReduceFriends
import App.State.Reducers.ReduceGameSettings
import App.State.Reducers.ReduceGraphicsSettings
import App.State.Reducers.ReduceHome
import App.State.Reducers.ReduceIcons
import App.State.Reducers.ReduceInvite
@@ -231,6 +237,9 @@ object RootReducer {
GameSettings = ReduceGameSettings(State0.GameSettings, Action),
KeyboardSettings = ReduceKeyboardSettings(State0.KeyboardSettings, Action),
TouchscreenHudSettings = ReduceTouchscreenHudSettings(State0.TouchscreenHudSettings, Action),
GraphicsSettings = ReduceGraphicsSettings(State0.GraphicsSettings, Action),
ControlSettings = ReduceControlSettings(State0.ControlSettings, Action),
BattleSettings = ReduceBattleSettings(State0.BattleSettings, Action),
)
}
@@ -12,6 +12,7 @@ import App.State.Reducers.BattleInviteStateSlice
import App.State.Reducers.BattlePauseStateSlice
import App.State.Reducers.BattleResultStateSlice
import App.State.Reducers.BattleSearchStateSlice
import App.State.Reducers.BattleSettingsStateSlice
import App.State.Reducers.BattleStatisticsStateSlice
import App.State.Reducers.BattleTutorialStateSlice
import App.State.Reducers.BattleViewStateSlice
@@ -19,6 +20,7 @@ import App.State.Reducers.CaptchaStateSlice
import App.State.Reducers.ClanChatStateSlice
import App.State.Reducers.ClanUserInfoStateSlice
import App.State.Reducers.ContextTutorialStateSlice
import App.State.Reducers.ControlSettingsStateSlice
import App.State.Reducers.CreatorTagStateSlice
import App.State.Reducers.DroppableGoldStateSlice
import App.State.Reducers.EntranceStateSlice
@@ -27,6 +29,7 @@ import App.State.Reducers.FriendsStateSlice
import App.State.Reducers.GameSettingsStateSlice
import App.State.Reducers.GarageStateSlice
import App.State.Reducers.GaragePreviewStateSlice
import App.State.Reducers.GraphicsSettingsStateSlice
import App.State.Reducers.HomeStateSlice
import App.State.Reducers.IconsStateSlice
import App.State.Reducers.InviteStateSlice
@@ -180,12 +183,12 @@ data class RootState(
val SoundSettings: SoundSettingsStateSlice = SoundSettingsStateSlice(),
/** 原 gameSettings(c2 / g2().zss)。已迁移。 */
val GameSettings: GameSettingsStateSlice = GameSettingsStateSlice(),
/** 原 battleSettings(W0 / X0().esq)。未迁移:原样透传。 */
val BattleSettings: Any? = null,
/** 原 graphicsSettings(? / lc.isu)。未迁移:原样透传。 */
val GraphicsSettings: Any? = null,
/** 原 controlSettings(i2 / _2().ess)。未迁移:原样透传。 */
val ControlSettings: Any? = null,
/** 原 battleSettings(W0 / X0().esq)。已迁移。 */
val BattleSettings: BattleSettingsStateSlice = BattleSettingsStateSlice(),
/** 原 graphicsSettings(L2 / lc.isu)。已迁移。 */
val GraphicsSettings: GraphicsSettingsStateSlice = GraphicsSettingsStateSlice(),
/** 原 controlSettings(i2 / _2().ess)。已迁移。 */
val ControlSettings: ControlSettingsStateSlice = ControlSettingsStateSlice(),
/** 原 securitySettings(V2 / pc.qsv)。已迁移。 */
val SecuritySettings: SecuritySettingsStateSlice = SecuritySettingsStateSlice(),
/** 原 purchaseDialogState(CO / je.ur2)。已迁移。 */
@@ -65,9 +65,9 @@ object RootWiring {
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("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("ResetSettingsFromStorage", "UpdateSettings")),
SliceWiring("battleSettings", "xpw_1", "W0", "X0().esq", "X0().esq(t, n.xpw_1)", listOf("ApplyBattleSettings", "ResetSettingsFromStorage")),
SliceWiring("graphicsSettings", "ypw_1", "L2", "lc.isu", "lc.isu(t, n.ypw_1)", listOf("ApplyGraphicsSettings")),
SliceWiring("controlSettings", "zpw_1", "i2", "_2().ess", "_2().ess(t, n.zpw_1)", listOf("ApplyControlSettings", "ResetSettingsFromStorage")),
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")),
@@ -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")
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")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
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;
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings;
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
@@ -686,6 +686,18 @@ import App.State.Reducers.ResetTouchscreenHudMapping
import App.State.Reducers.SetEntranceSoundSettings
import App.State.Reducers.SoundSettingsStateSlice
import App.State.Reducers.TouchscreenHudSettingsStateSlice
import App.State.Reducers.ApplyBattleSettings
import App.State.Reducers.ApplyControlSettings
import App.State.Reducers.ApplyGraphicsSettings
import App.State.Reducers.BattleSettingsStateSlice
import App.State.Reducers.ControlGamepadSettings
import App.State.Reducers.ControlMouseSettings
import App.State.Reducers.ControlSettingsStateSlice
import App.State.Reducers.ControlTouchscreenSettings
import App.State.Reducers.GraphicsSettingsStateSlice
import App.State.Reducers.ReduceBattleSettings
import App.State.Reducers.ReduceControlSettings
import App.State.Reducers.ReduceGraphicsSettings
import App.State.Reducers.UserRankBounds
import App.State.Reducers.UserStateSlice
import App.State.Reducers.ClearHullData
@@ -1113,7 +1125,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 70 个已迁移切片(69 归约 + launchParams 透传)
// ⑭ 根 reducer:89 个槽位的委派表 + 73 个已迁移切片(72 归约 + launchParams 透传)
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -1177,7 +1189,7 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 70 个已迁移字段,其余 19 个待迁",
"根:迁移表 = 73 个已迁移字段,其余 16 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
@@ -1199,8 +1211,9 @@ object StateCheck {
"battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial",
"clanChatState", "battleSearchState", "battleStatistics",
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
"graphicsSettings", "controlSettings", "battleSettings",
) &&
RootWiring.Unmigrated.size == 19
RootWiring.Unmigrated.size == 16
)
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
@@ -1256,7 +1269,7 @@ object StateCheck {
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 70/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 73/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -5967,6 +5980,142 @@ object StateCheck {
)
}
// 81 切片 reducer:GraphicsSettings(L2 / lc.isu,17 字段;1 个动作)
run {
val S0 = GraphicsSettingsStateSlice()
Check(
"切片 GraphicsSettings:默认值完整",
S0.QualityDetectByCardCompleted == null && !S0.Extended && S0.FpsLimit == 0 &&
S0.ShowFPSAndPing && S0.ShowSkybox && !S0.DynamicShadows &&
!S0.DynamicLighting && !S0.SoftParticles && S0.Fog && !S0.Antialiasing &&
S0.Vsync && S0.DecalsProfile == null && !S0.DustEnabled &&
S0.AnisotropicFilteringLevel == 0 && S0.TreesQuality == null &&
S0.EffectsQuality == null && S0.ResolutionScale == null
)
val Custom = GraphicsSettingsStateSlice(
QualityDetectByCardCompleted = true, Extended = true, FpsLimit = 144,
ShowFPSAndPing = false, ShowSkybox = false, DynamicShadows = true,
DynamicLighting = true, SoftParticles = true, Fog = false,
Antialiasing = true, Vsync = false, DecalsProfile = "high", DustEnabled = true,
AnisotropicFilteringLevel = 8, TreesQuality = "high",
EffectsQuality = "high", ResolutionScale = "1.0",
)
Check(
"切片 GraphicsSettings:ApplyGraphicsSettings 整体替换",
ReduceGraphicsSettings(S0, ApplyGraphicsSettings(Custom)) == Custom
)
Check(
"切片 GraphicsSettings:无关动作不改状态",
ReduceGraphicsSettings(Custom, AddFrags(1)) == Custom
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ApplyGraphicsSettings(Custom))
Check(
"根:ApplyGraphicsSettings 只动 graphicsSettings",
Root1.GraphicsSettings == Custom &&
Root1.ControlSettings == Root0.ControlSettings
)
val W = RootWiring.ByField("graphicsSettings")!!
Check(
"根:graphicsSettings 委派到 L2 / lc.isu",
W.SliceClass == "L2" && W.Reducer == "lc.isu" &&
W.Actions == listOf("ApplyGraphicsSettings")
)
}
// 82 切片 reducer:ControlSettings(i2 / _2().ess,7 字段;2 个动作)
run {
val S0 = ControlSettingsStateSlice()
Check(
"切片 ControlSettings:默认值完整",
S0.AvailableInputModes.isEmpty() && S0.InputMode == null &&
!S0.InversedBackwardMoving && !S0.InvertShaftVerticalAxis &&
S0.Mouse == ControlMouseSettings(50.0, true) &&
S0.Gamepad == ControlGamepadSettings(true, 13, 13) &&
S0.Touchscreen ==
ControlTouchscreenSettings(50.0, true, false, true, true, true, true)
)
val Custom = ControlSettingsStateSlice(
AvailableInputModes = setOf<Any?>("mouse", "touch"),
InputMode = "touch",
InversedBackwardMoving = true,
InvertShaftVerticalAxis = true,
Mouse = ControlMouseSettings(30.0, false),
Gamepad = ControlGamepadSettings(false, 20, 25),
Touchscreen = ControlTouchscreenSettings(70.0, false, true, false, false, false, false),
)
Check(
"切片 ControlSettings:ApplyControlSettings 整体替换",
ReduceControlSettings(S0, ApplyControlSettings(Custom)) == Custom
)
Check(
"切片 ControlSettings:ResetSettingsFromStorage 保留状态",
ReduceControlSettings(Custom, ResetSettingsFromStorage) == Custom
)
Check(
"切片 ControlSettings:无关动作不改状态",
ReduceControlSettings(Custom, AddFrags(1)) == Custom
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ApplyControlSettings(Custom))
Check(
"根:ApplyControlSettings 只动 controlSettings",
Root1.ControlSettings == Custom && Root1.BattleSettings == Root0.BattleSettings
)
val W = RootWiring.ByField("controlSettings")!!
Check(
"根:controlSettings 委派到 i2 / _2().ess 且 2 个动作",
W.SliceClass == "i2" && W.Reducer == "_2().ess" &&
W.Actions.toSet() == setOf("ApplyControlSettings", "ResetSettingsFromStorage")
)
}
// 83 切片 reducer:BattleSettings(W0 / X0().esq,11 字段;2 个动作)
run {
val S0 = BattleSettingsStateSlice()
Check(
"切片 BattleSettings:默认值完整",
S0.ShowAimHelperDot && S0.ShowDamage && S0.ShowTankPointers &&
S0.ShowButtonLabels && S0.AutoSuicide && S0.ShowStatusName &&
!S0.IsCenteredLayout && S0.BattleUISize == 50.0 &&
S0.AimhelperDotOpacity == 50.0 && S0.AimhelperDotScale == 1.0 &&
S0.AutoSupply
)
val Custom = BattleSettingsStateSlice(
ShowAimHelperDot = false, ShowDamage = false, ShowTankPointers = false,
ShowButtonLabels = false, AutoSuicide = false, ShowStatusName = false,
IsCenteredLayout = true, BattleUISize = 80.0, AimhelperDotOpacity = 20.0,
AimhelperDotScale = 2.0, AutoSupply = false,
)
Check(
"切片 BattleSettings:ApplyBattleSettings 整体替换",
ReduceBattleSettings(S0, ApplyBattleSettings(Custom)) == Custom
)
Check(
"切片 BattleSettings:ResetSettingsFromStorage 保留状态",
ReduceBattleSettings(Custom, ResetSettingsFromStorage) == Custom
)
Check(
"切片 BattleSettings:无关动作不改状态",
ReduceBattleSettings(Custom, AddFrags(1)) == Custom
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ApplyBattleSettings(Custom))
Check(
"根:ApplyBattleSettings 只动 battleSettings",
Root1.BattleSettings == Custom && Root1.GraphicsSettings == Root0.GraphicsSettings
)
val W = RootWiring.ByField("battleSettings")!!
Check(
"根:battleSettings 委派到 W0 / X0().esq 且 2 个动作",
W.SliceClass == "W0" && W.Reducer == "X0().esq" &&
W.Actions.toSet() == setOf("ApplyBattleSettings", "ResetSettingsFromStorage")
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)