feat(state): 迁移 battleStatistics
This commit is contained in:
1 parent
4aec83f684
commit
86979eb168
8 files changed
+466
-34
No files matched your search
@@ -143,7 +143,7 @@
|
||||
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
||||
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
||||
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
||||
- 已迁移切片 **65/89**:
|
||||
- 已迁移切片 **66/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)
|
||||
@@ -347,12 +347,15 @@
|
||||
- `battleSearchState` → `Itt` / `Htt().vtg`(`BattleSearchStateReducer.kt`):5 字段,9 个分支。
|
||||
`Fight` 只发副作用、状态不变;`RemoveBattle` 仅当选中 id 匹配时清空;
|
||||
`selectedFormat` 默认值来自服务定位器,按 `null` 收口。
|
||||
- `battleStatistics` → `jR` / `rr.fqr`(`BattleStatisticsReducer.kt`):27 字段,22 个分支。
|
||||
`FR`(14 字段的 Init)与通知切片的同名类冲突,改名为 `InitBattle`(contractState 之后复用);
|
||||
其余为战斗元信息 setter + 回合 / 统计面板 / 菜单 / 旗主 / 加载位;`SetRemainingTime` 用 `$m` 把时长转秒。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 64 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **24 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 65/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **741 项全绿**,含 89 槽位完整性、64 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 65 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **23 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 66/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **755 项全绿**,含 89 槽位完整性、65 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
@@ -373,6 +376,7 @@
|
||||
userRenameState 的 5 个也已登记(其中 `mtt` 因同名类冲突改名为 `AddInvitedUser`);
|
||||
battlePauseState 的 2 个、battleViewState 的 12 个、tankActionsLogState 的 1 个同样按效果定名。
|
||||
clanChatState 的 3 个(`$nt`/`xnt`/`Tnt`)、battleSearchState 的 1 个也已登记。
|
||||
battleStatistics 的 12 个(含改名的 `FR`→`InitBattle`)同样按效果定名。
|
||||
|
||||
### S4 引擎核心
|
||||
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T08:38:53.378Z",
|
||||
"generatedAt": "2026-10-24T08:50:07.796Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -71,7 +71,8 @@
|
||||
"tankActionsLogState",
|
||||
"suppliesTutorial",
|
||||
"clanChatState",
|
||||
"battleSearchState"
|
||||
"battleSearchState",
|
||||
"battleStatistics"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -1208,6 +1209,78 @@
|
||||
"Name": "ClearSelectedBattle",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "selectedBattleId=null(`n.vtn(cd, cd, cd, cd, null)`)"
|
||||
},
|
||||
{
|
||||
"Local": "FR",
|
||||
"Name": "InitBattle",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "14 字段战斗初始化(原名 Init,与通知切片的同名类冲突,改名 InitBattle)"
|
||||
},
|
||||
{
|
||||
"Local": "PR",
|
||||
"Name": "ResetRoundState",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "roundStarted=false、menuVisible=false(`n.cqq(…, !1, cd, !1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "KR",
|
||||
"Name": "ShowStat",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "statVisible=true;menuVisible=!(menu && !stat) && menu(`n.cqq(…, !0, cd, cd, …)`)"
|
||||
},
|
||||
{
|
||||
"Local": "YR",
|
||||
"Name": "HideStat",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "statVisible=false(`n.cqq(…, !1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "XR",
|
||||
"Name": "BeginRound",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "isContinueBattle=false、score=emptyMap()、flagOrBallOwner=null、statVisible=false、roundStarted=true、menuVisible=false"
|
||||
},
|
||||
{
|
||||
"Local": "UR",
|
||||
"Name": "ContinueBattle",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "isContinueBattle=true(`n.cqq(…, !0)`)"
|
||||
},
|
||||
{
|
||||
"Local": "QR",
|
||||
"Name": "CloseStatMenu",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "menuVisible=false(`n.cqq(…, !1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "ZR",
|
||||
"Name": "ToggleStatMenu",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "menuVisible=!menuVisible(`n.cqq(…, !n.bq2_1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "DR",
|
||||
"Name": "ClearFlagOrBallOwner",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "flagOrBallOwner=null(`n.cqq(…, null)`)"
|
||||
},
|
||||
{
|
||||
"Local": "tP",
|
||||
"Name": "MarkBattleLoaded",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "battleLoaded=true(`n.cqq(…, !0)`)"
|
||||
},
|
||||
{
|
||||
"Local": "nP",
|
||||
"Name": "MarkBattleUnloaded",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "battleLoaded=false(`n.cqq(…, !1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "HR",
|
||||
"Name": "ResetBattleStatistics",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "new jR() 整片复位"
|
||||
}
|
||||
],
|
||||
"Markers": [
|
||||
@@ -1337,19 +1410,17 @@
|
||||
"Reducer": "rr.fqr",
|
||||
"Delegate": "rr.fqr(t, n.bpv_1)",
|
||||
"Actions": [
|
||||
"?DR",
|
||||
"?HR",
|
||||
"?KR",
|
||||
"?PR",
|
||||
"?QR",
|
||||
"?UR",
|
||||
"?XR",
|
||||
"?YR",
|
||||
"?ZR",
|
||||
"?nP",
|
||||
"?tP",
|
||||
"Init",
|
||||
"BeginRound",
|
||||
"ClearFlagOrBallOwner",
|
||||
"CloseStatMenu",
|
||||
"ContinueBattle",
|
||||
"HideStat",
|
||||
"InitBattle",
|
||||
"InitBattleModeWithLevels",
|
||||
"MarkBattleLoaded",
|
||||
"MarkBattleUnloaded",
|
||||
"ResetBattleStatistics",
|
||||
"ResetRoundState",
|
||||
"SetBattleMode",
|
||||
"SetDisplayClientInfo",
|
||||
"SetFlagOrBallOwner",
|
||||
@@ -1357,11 +1428,13 @@
|
||||
"SetReArmor",
|
||||
"SetRemainingTime",
|
||||
"SetRoundRestartTime",
|
||||
"ShowStat",
|
||||
"ToggleStatMenu",
|
||||
"UpdateFund",
|
||||
"UpdateScore"
|
||||
],
|
||||
"Default": "new jR()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "battleTutorial",
|
||||
@@ -2910,7 +2983,7 @@
|
||||
"Actions": [
|
||||
"?Hnt",
|
||||
"GetReward",
|
||||
"Init",
|
||||
"InitBattle",
|
||||
"SetActiveContract",
|
||||
"SetBattleProgress",
|
||||
"SetContract",
|
||||
|
||||
@@ -281,6 +281,19 @@ const AnonymousActionNames = new Map([
|
||||
['Tnt', 'ResetClanChat'],
|
||||
// battleSearchState 切片(Itt / Gtt.vtg):Dtt 没有 toString。
|
||||
['Dtt', 'ClearSelectedBattle'],
|
||||
// battleStatistics 切片(jR / rP.fqr):11 个类没有 toString;FR 与通知切片的 Init 同名,改名。
|
||||
['FR', 'InitBattle'],
|
||||
['PR', 'ResetRoundState'],
|
||||
['KR', 'ShowStat'],
|
||||
['YR', 'HideStat'],
|
||||
['XR', 'BeginRound'],
|
||||
['UR', 'ContinueBattle'],
|
||||
['QR', 'CloseStatMenu'],
|
||||
['ZR', 'ToggleStatMenu'],
|
||||
['DR', 'ClearFlagOrBallOwner'],
|
||||
['tP', 'MarkBattleLoaded'],
|
||||
['nP', 'MarkBattleUnloaded'],
|
||||
['HR', 'ResetBattleStatistics'],
|
||||
]);
|
||||
|
||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||
@@ -739,6 +752,7 @@ const Migrated = {
|
||||
},
|
||||
clanChatState: { Type: 'ClanChatStateSlice', Reduce: 'ReduceClanChat' },
|
||||
battleSearchState: { Type: 'BattleSearchStateSlice', Reduce: 'ReduceBattleSearch' },
|
||||
battleStatistics: { Type: 'BattleStatisticsStateSlice', Reduce: 'ReduceBattleStatistics' },
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
@@ -1113,6 +1127,22 @@ const BattleSearchAnonymousEffects = {
|
||||
Dtt: 'selectedBattleId=null(`n.vtn(cd, cd, cd, cd, null)`)',
|
||||
};
|
||||
|
||||
// battleStatistics 切片(jR / rP.fqr):11 个类没有 toString;FR 与通知切片 Init 同名。
|
||||
const BattleStatisticsAnonymousEffects = {
|
||||
FR: '14 字段战斗初始化(原名 Init,与通知切片的同名类冲突,改名 InitBattle)',
|
||||
PR: 'roundStarted=false、menuVisible=false(`n.cqq(…, !1, cd, !1)`)',
|
||||
KR: 'statVisible=true;menuVisible=!(menu && !stat) && menu(`n.cqq(…, !0, cd, cd, …)`)',
|
||||
YR: 'statVisible=false(`n.cqq(…, !1)`)',
|
||||
XR: 'isContinueBattle=false、score=emptyMap()、flagOrBallOwner=null、statVisible=false、roundStarted=true、menuVisible=false',
|
||||
UR: 'isContinueBattle=true(`n.cqq(…, !0)`)',
|
||||
QR: 'menuVisible=false(`n.cqq(…, !1)`)',
|
||||
ZR: 'menuVisible=!menuVisible(`n.cqq(…, !n.bq2_1)`)',
|
||||
DR: 'flagOrBallOwner=null(`n.cqq(…, null)`)',
|
||||
tP: 'battleLoaded=true(`n.cqq(…, !0)`)',
|
||||
nP: 'battleLoaded=false(`n.cqq(…, !1)`)',
|
||||
HR: 'new jR() 整片复位',
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(P, 'names', 'root_wiring.json'),
|
||||
JSON.stringify(
|
||||
@@ -1369,6 +1399,12 @@ fs.writeFileSync(
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
...Object.entries(BattleStatisticsAnonymousEffects).map(([Local, Effect]) => ({
|
||||
Local,
|
||||
Name: AnonymousActionNames.get(Local),
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
],
|
||||
Markers: [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
// BattleStatisticsReducer.kt —— 战斗统计切片(对应 module_10032 的 jR 状态 + rP.fqr)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 jR:27 个字段,toString 名 "BattleStatistics(…)";copy 是 cqq(27 字段 + 回调)。
|
||||
// 根委派:`rr.fqr(t, n.bpv_1)`。
|
||||
// reducer(22 个实例臂):
|
||||
// FR(Init) → 用 14 字段的重置动作灌入战斗元信息
|
||||
// GR(SetDisplayClientInfo) → DisplayClientInfo
|
||||
// IR(SetBattleMode) → Mode
|
||||
// AR(SetReArmor) → IsReArmorEnabled
|
||||
// RR(SetRankRange) → RankRange
|
||||
// ER(UpdateScore) → Score 写键值
|
||||
// TR(UpdateFund) → Fund
|
||||
// PR / KR / YR / XR / UR / QR / ZR(无 toString)
|
||||
// → 回合开始/结束、统计面板与菜单的显示切换
|
||||
// NR(SetRoundRestartTime) → RoundRestartTime
|
||||
// MR(SetRemainingTime) → RemainingTimeInSec(时长 → 秒)
|
||||
// OR(SetFlagOrBallOwner) → FlagOrBallOwner
|
||||
// DR / tP / nP(无 toString) → 清旗主、BattleLoaded 开关
|
||||
// HR(无 toString) → `new jR()` 整片复位
|
||||
// JR(InitBattleModeWithLevels)→ GameModeLevelsCount
|
||||
//
|
||||
// `FR` 与 contractState 共用同一个 14 字段动作类,生成物里的 `Init` 撞到了通知切片的
|
||||
// 同名类,因此这里改名为 `InitBattle`(两个切片都直接读这 14 个字段)。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.InitBattleModeWithLevels
|
||||
import App.State.Range
|
||||
import App.State.SetBattleMode
|
||||
import App.State.SetDisplayClientInfo
|
||||
import App.State.SetFlagOrBallOwner
|
||||
import App.State.SetRankRange
|
||||
import App.State.SetReArmor
|
||||
import App.State.SetRemainingTime
|
||||
import App.State.SetRoundRestartTime
|
||||
import App.State.UpdateFund
|
||||
import App.State.UpdateScore
|
||||
|
||||
/* ------------------------------------------------------------------ 动作 */
|
||||
|
||||
/** 原 `FR`(原名 `Init`,与通知切片的同名类冲突):14 字段的战斗初始化动作。 */
|
||||
data class InitBattle(
|
||||
val battleId: Any? = null,
|
||||
val remainingTime: Any? = null,
|
||||
val battleName: String = "",
|
||||
val matchBattle: Boolean = true,
|
||||
val isParkourMode: Boolean = false,
|
||||
val isTutorial: Boolean = false,
|
||||
val valuableBattle: Boolean = true,
|
||||
val scoreLimit: Int = 0,
|
||||
val fund: Int = 0,
|
||||
val options: List<Any?> = emptyList(),
|
||||
val customBattleName: String? = null,
|
||||
val battleFormat: Any? = null,
|
||||
val isMatchmakingMark: Boolean = false,
|
||||
val mapNameWithoutMode: String = "",
|
||||
)
|
||||
|
||||
/** 原 `PR`:回合结束,复位开始位与菜单。 */
|
||||
object ResetRoundState
|
||||
|
||||
/** 原 `KR`:显示统计面板(菜单保持原状,除非原本「菜单开且统计关」)。 */
|
||||
object ShowStat
|
||||
|
||||
/** 原 `YR`:隐藏统计面板。 */
|
||||
object HideStat
|
||||
|
||||
/** 原 `XR`:新回合开始,清分数 / 旗主并打开回合位。 */
|
||||
object BeginRound
|
||||
|
||||
/** 原 `UR`:标记为连续战斗。 */
|
||||
object ContinueBattle
|
||||
|
||||
/** 原 `QR`:关闭统计菜单。 */
|
||||
object CloseStatMenu
|
||||
|
||||
/** 原 `ZR`:切换统计菜单。 */
|
||||
object ToggleStatMenu
|
||||
|
||||
/** 原 `DR`:清空旗主 / 球主。 */
|
||||
object ClearFlagOrBallOwner
|
||||
|
||||
/** 原 `tP`:标记战斗资源已加载。 */
|
||||
object MarkBattleLoaded
|
||||
|
||||
/** 原 `nP`:标记战斗资源未加载。 */
|
||||
object MarkBattleUnloaded
|
||||
|
||||
/** 原 `HR`:整片复位(`new jR()`)。 */
|
||||
object ResetBattleStatistics
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class BattleStatisticsStateSlice(
|
||||
val BattleId: Any? = null,
|
||||
val MapName: String = "",
|
||||
val Mode: String? = null,
|
||||
val IsMMBattle: Boolean = true,
|
||||
val IsParkourMode: Boolean = false,
|
||||
val IsTutorial: Boolean = false,
|
||||
val ValuableBattle: Boolean = true,
|
||||
val IsReArmorEnabled: Boolean = true,
|
||||
val RankRange: Range? = null,
|
||||
val IsContinueBattle: Boolean = false,
|
||||
val RoundRestartTime: Any? = null,
|
||||
val Score: Map<Any?, Any?> = emptyMap(),
|
||||
val Fund: Int = 0,
|
||||
val GameModeLevelsCount: Int = 0,
|
||||
val FlagOrBallOwner: Any? = null,
|
||||
val StatVisible: Boolean = false,
|
||||
val RoundStarted: Boolean = false,
|
||||
val ScoreLimit: Int = 0,
|
||||
val MenuVisible: Boolean = false,
|
||||
val RemainingTimeInSec: Int = -1,
|
||||
val BattleLoaded: Boolean = false,
|
||||
val Options: List<Any?> = emptyList(),
|
||||
val CustomBattleName: String? = null,
|
||||
val BattleFormat: Any? = null,
|
||||
val IsMatchmakingMark: Boolean = false,
|
||||
val MapNameWithoutMode: String = "",
|
||||
val DisplayClientInfo: Boolean = true,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
/** 原 `$m`:时长 → 秒。 */
|
||||
private fun AsSeconds(Value: Any?): Int = (Value as? Number)?.toInt() ?: 0
|
||||
|
||||
fun ReduceBattleStatistics(
|
||||
State0: BattleStatisticsStateSlice,
|
||||
Action: Any,
|
||||
): BattleStatisticsStateSlice = when (Action) {
|
||||
is InitBattle -> State0.copy(
|
||||
MapName = Action.battleName,
|
||||
IsMMBattle = Action.matchBattle,
|
||||
IsParkourMode = Action.isParkourMode,
|
||||
IsTutorial = Action.isTutorial,
|
||||
ValuableBattle = Action.valuableBattle,
|
||||
IsContinueBattle = false,
|
||||
Score = emptyMap(),
|
||||
Fund = Action.fund,
|
||||
ScoreLimit = Action.scoreLimit,
|
||||
MenuVisible = false,
|
||||
RemainingTimeInSec = AsSeconds(Action.remainingTime),
|
||||
Options = Action.options,
|
||||
CustomBattleName = Action.customBattleName,
|
||||
BattleFormat = Action.battleFormat,
|
||||
IsMatchmakingMark = Action.isMatchmakingMark,
|
||||
MapNameWithoutMode = Action.mapNameWithoutMode,
|
||||
)
|
||||
is SetDisplayClientInfo -> State0.copy(DisplayClientInfo = Action.visible)
|
||||
is SetBattleMode -> State0.copy(Mode = Action.mode)
|
||||
is SetReArmor -> State0.copy(IsReArmorEnabled = Action.enabled)
|
||||
is SetRankRange -> State0.copy(RankRange = Action.rankRange)
|
||||
is UpdateScore -> State0.copy(Score = State0.Score + (Action.team to Action.score))
|
||||
is UpdateFund -> State0.copy(Fund = Action.fund)
|
||||
is ResetRoundState -> State0.copy(RoundStarted = false, MenuVisible = false)
|
||||
is ShowStat -> State0.copy(
|
||||
StatVisible = true,
|
||||
MenuVisible = !(State0.MenuVisible && !State0.StatVisible) && State0.MenuVisible,
|
||||
)
|
||||
is HideStat -> State0.copy(StatVisible = false)
|
||||
is BeginRound -> State0.copy(
|
||||
IsContinueBattle = false,
|
||||
Score = emptyMap(),
|
||||
FlagOrBallOwner = null,
|
||||
StatVisible = false,
|
||||
RoundStarted = true,
|
||||
MenuVisible = false,
|
||||
)
|
||||
is ContinueBattle -> State0.copy(IsContinueBattle = true)
|
||||
is CloseStatMenu -> State0.copy(MenuVisible = false)
|
||||
is ToggleStatMenu -> State0.copy(MenuVisible = !State0.MenuVisible)
|
||||
is SetRoundRestartTime -> State0.copy(RoundRestartTime = Action.time)
|
||||
is SetRemainingTime -> State0.copy(RemainingTimeInSec = AsSeconds(Action.time))
|
||||
is SetFlagOrBallOwner -> State0.copy(FlagOrBallOwner = Action.ownerUserId)
|
||||
is ClearFlagOrBallOwner -> State0.copy(FlagOrBallOwner = null)
|
||||
is MarkBattleLoaded -> State0.copy(BattleLoaded = true)
|
||||
is MarkBattleUnloaded -> State0.copy(BattleLoaded = false)
|
||||
is ResetBattleStatistics -> BattleStatisticsStateSlice()
|
||||
is InitBattleModeWithLevels -> State0.copy(GameModeLevelsCount = Action.levelsCount as? Int ?: 0)
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 65 个切片里,64 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 66 个切片里,65 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -67,9 +67,10 @@
|
||||
// suppliesTutorial → ReduceSuppliesTutorial(ust / Uw.uth,根委派 `Uw.uth(t, n.zpx_1)`)
|
||||
// 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)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 24 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 23 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -80,6 +81,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.ReduceBattleStatistics
|
||||
import App.State.Reducers.ReduceBattleTutorial
|
||||
import App.State.Reducers.ReduceBattleView
|
||||
import App.State.Reducers.ReduceBattleInvite
|
||||
@@ -216,6 +218,7 @@ object RootReducer {
|
||||
SuppliesTutorial = ReduceSuppliesTutorial(State0.SuppliesTutorial, Action),
|
||||
ClanChatState = ReduceClanChat(State0.ClanChatState, Action),
|
||||
BattleSearchState = ReduceBattleSearch(State0.BattleSearchState, Action),
|
||||
BattleStatistics = ReduceBattleStatistics(State0.BattleStatistics, 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.BattleStatisticsStateSlice
|
||||
import App.State.Reducers.BattleTutorialStateSlice
|
||||
import App.State.Reducers.BattleViewStateSlice
|
||||
import App.State.Reducers.CaptchaStateSlice
|
||||
@@ -79,8 +80,8 @@ data class RootState(
|
||||
val Screen: ScreenStateSlice = ScreenStateSlice(),
|
||||
/** 原 modalScreen(nV / Fs.js1)。已迁移。 */
|
||||
val ModalScreen: ModalScreenStateSlice = ModalScreenStateSlice(),
|
||||
/** 原 battleStatistics(jR / rr.fqr)。未迁移:原样透传。 */
|
||||
val BattleStatistics: Any? = null,
|
||||
/** 原 battleStatistics(jR / rr.fqr)。已迁移。 */
|
||||
val BattleStatistics: BattleStatisticsStateSlice = BattleStatisticsStateSlice(),
|
||||
/** 原 battleTutorial(rE / Q.sq8)。已迁移。 */
|
||||
val BattleTutorial: BattleTutorialStateSlice = BattleTutorialStateSlice(),
|
||||
/** 原 garage(UB / hF)。已迁移。 */
|
||||
|
||||
@@ -17,7 +17,7 @@ object RootWiring {
|
||||
SliceWiring("user", "ypu_1", "t8", "M8().qtd", "M8().qtd(t, n.ypu_1)", listOf("AddCoins", "AddRubies", "ClearInDebt", "DisableLobbyTutorial", "SetBatteryLevel", "SetClanInfo", "SetCoins", "SetDisabledWithoutPremiumLocations", "SetHasTemporaryCash", "SetInDebt", "SetPremiumExpirationTime", "SetRankNames", "SetRubies", "SetUserId", "SetUserPropertiesAction", "SpendCoins", "SpendCrystals", "SpendRubies", "UpdateClanTag", "UpdateCrystals", "UpdateEventPass", "UpdateGearScore", "UpdatePremium", "UpdateRank", "UpdateScore", "UpdateUid", "UpdateUserRating")),
|
||||
SliceWiring("screen", "zpu_1", "WK", "dY().ks5", "dY().ks5(t, n.zpu_1)", listOf("ClearScreenStack", "DelayNextGoTo", "DisableBack", "DisableForeground", "DisplayMainEntranceScreen", "DisplayReauthorizationMessage", "EnableBack", "EnableForeground", "FilterScreenStack", "GoTo", "PopScreen", "PressedAnyButton", "ResetScreenStackOnNextGoTo")),
|
||||
SliceWiring("modalScreen", "apv_1", "nV", "Fs.js1", "Fs.js1(t, n.apv_1)", listOf("AddModal", "CloseCurrentModal", "RemoveCurrentByName", "RemoveModal")),
|
||||
SliceWiring("battleStatistics", "bpv_1", "jR", "rr.fqr", "rr.fqr(t, n.bpv_1)", listOf("?DR", "?HR", "?KR", "?PR", "?QR", "?UR", "?XR", "?YR", "?ZR", "?nP", "?tP", "Init", "InitBattleModeWithLevels", "SetBattleMode", "SetDisplayClientInfo", "SetFlagOrBallOwner", "SetRankRange", "SetReArmor", "SetRemainingTime", "SetRoundRestartTime", "UpdateFund", "UpdateScore")),
|
||||
SliceWiring("battleStatistics", "bpv_1", "jR", "rr.fqr", "rr.fqr(t, n.bpv_1)", listOf("BeginRound", "ClearFlagOrBallOwner", "CloseStatMenu", "ContinueBattle", "HideStat", "InitBattle", "InitBattleModeWithLevels", "MarkBattleLoaded", "MarkBattleUnloaded", "ResetBattleStatistics", "ResetRoundState", "SetBattleMode", "SetDisplayClientInfo", "SetFlagOrBallOwner", "SetRankRange", "SetReArmor", "SetRemainingTime", "SetRoundRestartTime", "ShowStat", "ToggleStatMenu", "UpdateFund", "UpdateScore")),
|
||||
SliceWiring("battleTutorial", "cpv_1", "rE", "Q.sq8", "Q.sq8(t, n.cpv_1)", listOf("MarkMovementStarted", "ResetBattleTutorial", "SetFragsCount", "ShowMessage")),
|
||||
SliceWiring("garage", "dpv_1", "UB", "hF", "hF(t, n)", listOf("AddGarageItemCount", "AddNewItem", "AddOverdriveDescription", "AddRelatedBazooka", "AddRelatedResistanceModule", "AddToFavoriteItem", "BuyProtectionSlot", "ClearWaitServerItemMount", "GarageAddItemToDepot", "GarageApplyResistanceMount", "GarageBuyDevice", "GarageDeviceTrySetInfinityLifetime", "GarageDevicesLoadedAction", "GarageLoaded", "GarageMountItem", "GarageMountItems", "GarageRemoveDevice", "GarageRemoveDiscountForDevice", "GarageRemoveItemFromDepot", "GarageRemoveItemFromStore", "GarageResistanceUnMount", "GarageSelectAlteration", "GarageSelectItem", "GarageSelectShotSkin", "GarageSelectSkin", "GarageUnmountItem", "GarageUpdateItemCount", "GarageUpgradeItemAction", "GarageUpgradeItemConfirm", "GarageUpgradeItemCoolDownFinished", "GarageUpgradeItemCoolDownStarted", "GoTo", "HideVideoInDescription", "InitState", "RemoveFromFavoriteItem", "RemoveFromSale", "RemoveNewItem", "RemoveWaitServerItemMount", "ResetGarage", "ResetMountDelay", "SelectGarageCategory", "SetCooldownReduceEndTime", "SetGarageNotLoaded", "SetGarageObject", "SetGarageSkinsAction", "SetItemRarity", "SetItemsOnDepot", "SetItemsOnMarket", "SetMountedItems", "SetMountedShotSkinForItems", "SetMountedSkinForItems", "SetNewItems", "SetResistanceReadyToMount", "SetRestrictionMount", "ShowGarageDiscountNotification", "StartCooldownReduce", "SwitchingRowsScroll", "UpdateDiscount")),
|
||||
SliceWiring("garagePreview", "epv_1", "IG", "fs.eru", "fs.eru(t, n.epv_1)", listOf("AddRequestedItem", "CaptureScreenshot", "CaptureVideo", "ClearDroneData", "ClearGrenadeData", "ClearHasGrenades", "ClearHullData", "ClearWeaponData", "InitGarageResources", "ResetItem3dModelActionCache", "SetCaptureActions", "SetDrone", "SetExpectedItemId", "SetGrenadeData", "SetHasGrenades", "SetHullData", "SetMountedItemsProperties", "SetPaint", "SetWeaponData", "ShowRequest", "UpdatePreviewSettings", "UpdateViewStyle")),
|
||||
@@ -99,14 +99,14 @@ object RootWiring {
|
||||
SliceWiring("availabilityByRank", "cpy_1", "tet", "\$p.xth", "\$p.xth(t, n.cpy_1)", listOf("SetLoadedData")),
|
||||
SliceWiring("userReportsState", "dpy_1", "Zst", "qot.zth", "(null == sd && new qot(), sd).zth(t, n.dpy_1)", listOf("InitUserReports", "ReportBattleChatMessage", "ReportLobbyChatMessage", "ReportProBattleName", "SetAvailableGameReports", "SetAvailableReports")),
|
||||
SliceWiring("tankBuilderState", "epy_1", "brt", "wp.ati", "wp.ati(t, n.epy_1)", listOf("?Trt", "SetKode", "SetParts", "SetPartsInfo")),
|
||||
SliceWiring("contractState", "fpy_1", "Ont", "xv.bti", "xv.bti(t, n.fpy_1)", listOf("?Hnt", "GetReward", "Init", "SetActiveContract", "SetBattleProgress", "SetContract", "UpdateProgress")),
|
||||
SliceWiring("contractState", "fpy_1", "Ont", "xv.bti", "xv.bti(t, n.fpy_1)", listOf("?Hnt", "GetReward", "InitBattle", "SetActiveContract", "SetBattleProgress", "SetContract", "UpdateProgress")),
|
||||
SliceWiring("promoCode", "gpy_1", "cZ", "Q_.ysg", "Q_.ysg(t, n.gpy_1)", listOf("ActivatePromoCode", "SetCodeValidateStatus")),
|
||||
SliceWiring("referralEvent", "hpy_1", "pQ", "lh.osk", "lh.osk(t, n.hpy_1)", listOf("SetPromoCode", "SetReferralEventData", "SetReferrals", "SetReferrer")),
|
||||
SliceWiring("supportUrl", "ipy_1", "I4", "Yl.ct7", "Yl.ct7(t, n.ipy_1)", listOf("ClearSupportUrl", "SetUrl")),
|
||||
)
|
||||
|
||||
/** 已迁移(强类型;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")
|
||||
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 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;
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -643,6 +643,30 @@ import App.State.SetSuspiciousFilter
|
||||
import App.State.Reducers.BattleSearchStateSlice
|
||||
import App.State.Reducers.ClearSelectedBattle
|
||||
import App.State.Reducers.ReduceBattleSearch
|
||||
import App.State.InitBattleModeWithLevels
|
||||
import App.State.Range
|
||||
import App.State.SetBattleMode
|
||||
import App.State.SetDisplayClientInfo
|
||||
import App.State.SetFlagOrBallOwner
|
||||
import App.State.SetRankRange
|
||||
import App.State.SetReArmor
|
||||
import App.State.SetRemainingTime
|
||||
import App.State.SetRoundRestartTime
|
||||
import App.State.UpdateFund
|
||||
import App.State.Reducers.BattleStatisticsStateSlice
|
||||
import App.State.Reducers.BeginRound
|
||||
import App.State.Reducers.ClearFlagOrBallOwner
|
||||
import App.State.Reducers.CloseStatMenu
|
||||
import App.State.Reducers.ContinueBattle
|
||||
import App.State.Reducers.HideStat
|
||||
import App.State.Reducers.InitBattle
|
||||
import App.State.Reducers.MarkBattleLoaded
|
||||
import App.State.Reducers.MarkBattleUnloaded
|
||||
import App.State.Reducers.ReduceBattleStatistics
|
||||
import App.State.Reducers.ResetBattleStatistics
|
||||
import App.State.Reducers.ResetRoundState
|
||||
import App.State.Reducers.ShowStat
|
||||
import App.State.Reducers.ToggleStatMenu
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -1070,7 +1094,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 65 个已迁移切片(64 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 66 个已迁移切片(65 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -1134,7 +1158,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 65 个已迁移字段,其余 24 个待迁",
|
||||
"根:迁移表 = 66 个已迁移字段,其余 23 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -1154,9 +1178,9 @@ object StateCheck {
|
||||
"friends", "suppliesState", "clanUserInfo", "battleInviteState",
|
||||
"partner", "userRenameState",
|
||||
"battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial",
|
||||
"clanChatState", "battleSearchState",
|
||||
"clanChatState", "battleSearchState", "battleStatistics",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 24
|
||||
RootWiring.Unmigrated.size == 23
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -1212,7 +1236,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 65/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 66/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -5610,6 +5634,114 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// 76 切片 reducer:BattleStatistics(jR / rr.fqr,27 字段;22 个动作)
|
||||
run {
|
||||
val S0 = BattleStatisticsStateSlice()
|
||||
Check(
|
||||
"切片 BattleStatistics:默认值完整",
|
||||
S0.BattleId == null && S0.MapName == "" && S0.Mode == null &&
|
||||
S0.IsMMBattle && !S0.IsParkourMode && !S0.IsTutorial &&
|
||||
S0.ValuableBattle && S0.IsReArmorEnabled && S0.RankRange == null &&
|
||||
!S0.IsContinueBattle && S0.RoundRestartTime == null && S0.Score.isEmpty() &&
|
||||
S0.Fund == 0 && S0.GameModeLevelsCount == 0 && S0.FlagOrBallOwner == null &&
|
||||
!S0.StatVisible && !S0.RoundStarted && S0.ScoreLimit == 0 &&
|
||||
!S0.MenuVisible && S0.RemainingTimeInSec == -1 && !S0.BattleLoaded &&
|
||||
S0.Options.isEmpty() && S0.CustomBattleName == null &&
|
||||
S0.BattleFormat == null && !S0.IsMatchmakingMark &&
|
||||
S0.MapNameWithoutMode == "" && S0.DisplayClientInfo
|
||||
)
|
||||
val S1 = ReduceBattleStatistics(
|
||||
S0,
|
||||
InitBattle(
|
||||
battleId = "b1", remainingTime = 180, battleName = "Desert",
|
||||
matchBattle = true, isParkourMode = false, isTutorial = false,
|
||||
valuableBattle = true, scoreLimit = 100, fund = 50,
|
||||
options = listOf("o"), customBattleName = "custom",
|
||||
battleFormat = "fmt", isMatchmakingMark = true, mapNameWithoutMode = "Desert",
|
||||
),
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:InitBattle 灌入元信息并清分数 / 菜单",
|
||||
S1.MapName == "Desert" && S1.IsMMBattle && S1.Fund == 50 &&
|
||||
S1.ScoreLimit == 100 && S1.RemainingTimeInSec == 180 &&
|
||||
S1.CustomBattleName == "custom" && S1.IsMatchmakingMark &&
|
||||
S1.MapNameWithoutMode == "Desert" && S1.Options == listOf<Any?>("o") &&
|
||||
S1.Score.isEmpty() && !S1.MenuVisible && !S1.IsContinueBattle
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:模式 / 护甲 / 段位 / 客户端信息",
|
||||
ReduceBattleStatistics(S1, SetBattleMode("DM")).Mode == "DM" &&
|
||||
!ReduceBattleStatistics(S1, SetReArmor(false)).IsReArmorEnabled &&
|
||||
ReduceBattleStatistics(S1, SetRankRange(Range(10, 1))).RankRange ==
|
||||
Range(10, 1) &&
|
||||
!ReduceBattleStatistics(S1, SetDisplayClientInfo(false)).DisplayClientInfo
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:UpdateScore / UpdateFund 写表与资金",
|
||||
ReduceBattleStatistics(S1, UpdateScore("A", 3)).Score ==
|
||||
mapOf<Any?, Any?>("A" to 3) &&
|
||||
ReduceBattleStatistics(S1, UpdateFund(0L, 9)).Fund == 9
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:SetRemainingTime 时长转秒",
|
||||
ReduceBattleStatistics(S1, SetRemainingTime(90)).RemainingTimeInSec == 90
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:旗主设置 / 清空",
|
||||
ReduceBattleStatistics(S1, SetFlagOrBallOwner(7L)).FlagOrBallOwner == 7L &&
|
||||
ReduceBattleStatistics(
|
||||
ReduceBattleStatistics(S1, SetFlagOrBallOwner(7L)),
|
||||
ClearFlagOrBallOwner,
|
||||
).FlagOrBallOwner == null
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:ShowStat / HideStat / 菜单两态",
|
||||
ReduceBattleStatistics(S1, ShowStat).StatVisible &&
|
||||
!ReduceBattleStatistics(S1, HideStat).StatVisible &&
|
||||
!ReduceBattleStatistics(S1, CloseStatMenu).MenuVisible &&
|
||||
ReduceBattleStatistics(S1, ToggleStatMenu).MenuVisible
|
||||
)
|
||||
val S2 = ReduceBattleStatistics(S1, BeginRound)
|
||||
Check(
|
||||
"切片 BattleStatistics:BeginRound 开回合并清分数",
|
||||
S2.RoundStarted && !S2.StatVisible && !S2.MenuVisible &&
|
||||
S2.Score.isEmpty() && S2.FlagOrBallOwner == null
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:ContinueBattle / ResetRoundState",
|
||||
ReduceBattleStatistics(S2, ContinueBattle).IsContinueBattle &&
|
||||
!ReduceBattleStatistics(S2, ResetRoundState).RoundStarted
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:BattleLoaded 两态与 levelsCount",
|
||||
ReduceBattleStatistics(S2, MarkBattleLoaded).BattleLoaded &&
|
||||
!ReduceBattleStatistics(S2, MarkBattleUnloaded).BattleLoaded &&
|
||||
ReduceBattleStatistics(S2, InitBattleModeWithLevels(4))
|
||||
.GameModeLevelsCount == 4
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:ResetBattleStatistics 整片复位",
|
||||
ReduceBattleStatistics(S2, ResetBattleStatistics) == BattleStatisticsStateSlice()
|
||||
)
|
||||
Check(
|
||||
"切片 BattleStatistics:无关动作不改状态",
|
||||
ReduceBattleStatistics(S2, AddFrags(1)) == S2
|
||||
)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, MarkBattleLoaded)
|
||||
Check(
|
||||
"根:MarkBattleLoaded 只动 battleStatistics",
|
||||
Root1.BattleStatistics.BattleLoaded &&
|
||||
Root1.BattleSearchState == Root0.BattleSearchState
|
||||
)
|
||||
val W = RootWiring.ByField("battleStatistics")!!
|
||||
Check(
|
||||
"根:battleStatistics 委派到 jR / rr.fqr 且 22 个动作",
|
||||
W.SliceClass == "jR" && W.Reducer == "rr.fqr" && W.Actions.size == 22
|
||||
)
|
||||
}
|
||||
|
||||
val Failed = Results.filter { !it.second }
|
||||
println("TankiOffline 状态层自检:${Results.size} 项")
|
||||
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
||||
|
||||
Reference in new issue
Block a user