{"record":{"id":"3784f3917ba3b451","repo":"babalae/better-genshin-impact","slug":"misplaced-slot","errorCode":null,"errorMessage":"切换角色：未找到 {misplaced.Slot} 号位的换下按钮","messagePattern":"切换角色：未找到 (.+?) 号位的换下按钮","errorType":"exception","errorClass":"PartySetupFailedException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs","lineNumber":701,"sourceCode":"            return StateHandlerResult.Success;\n        }\n\n        var misplaced = _currentTeamSlots.FirstOrDefault(slot => slot.Slot != _currentRole.Slot && _currentRole.Matches(slot.Name));\n        if (misplaced != null)\n        {\n            if (_currentTeamSlots.Count == 1)\n            {\n                _logger.LogInformation(\"切换角色：队伍仅剩一个角色，保留该角色并从 1 号位开始通过更换重建队伍\");\n                StartSuffixRebuild(1);\n                _currentRole = null;\n                return StateHandlerResult.Wait;\n            }\n\n            ClickFixedTeamSlot(misplaced.Slot);\n            await Delay(500, _ct);\n            if (!TryClickText(page, \"换下\", Rect1080(382, 994, 87, 51)))\n            {\n                throw new PartySetupFailedException($\"切换角色：未找到 {misplaced.Slot} 号位的换下按钮\");\n            }\n\n            await WaitForRoleRemoved(misplaced.Slot, _ct);\n            _expectedTeamCount--;\n            _teamSnapshotDirty = true;\n            _prepareSuffixRebuildAfterRemoval = true;\n            _currentRole = null;\n            return StateHandlerResult.Wait;\n        }\n\n        EnsureSlotIsOperable(_currentRole.Slot);\n        ClickFixedTeamSlot(_currentRole.Slot);\n        _workflowState = SwitchCharacterState.OpenFilterPanel;\n        return StateHandlerResult.Success;\n    }\n\n    /// <summary>\n    /// 打开筛选面板。","sourceCodeStart":683,"sourceCodeEnd":719,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/Common/Job/SwitchCharacterStateMachineTask.cs#L683-L719","documentation":"Thrown by HandlePrepareNextRole when a character currently occupies the wrong slot (a 'misplaced' slot) and the OCR-driven click of the '换下' (remove) button fails. TryClickText searches the fixed ROI Rect1080(382,994,87,51) for the literal text; a false return means the remove button could not be located, so the party teardown cannot proceed safely.","triggerScenarios":"Inside HandlePrepareNextRole, _currentTeamSlots finds a slot whose name matches _currentRole but sits at a different slot; the team has >1 member; ClickFixedTeamSlot(misplaced.Slot) then Delay(500) runs, but TryClickText(page,\"换下\",...) returns false on the very first attempt (no retry within this handler — only the surrounding RetryTimeout applies on a different code path).","commonSituations":"Game resolution/capture scale differs from 1080p assumptions so the fixed ROI misses the button; the 500ms Delay was too short and a slot-click animation is still playing; a popup or toast covers the bottom button bar; OCR misreads '换下' due to font/color changes after a game version update; capture frame was a transitional blur.","solutions":["Confirm the game is running at the 1080p-based resolution the ROIs assume (assetScale must be correct); fix the TaskContext.SystemInfo.AssetScale.","Re-capture manually and inspect Rect1080(382,994,87,51): if the '换下' button moved, update the ROI constant to match the current game layout.","Increase the Delay(500) before TryClickText or loop TryClickText a few times instead of throwing on the first miss, mirroring the Retry pattern used elsewhere.","Verify no overlay/dialog is covering the button bar at teardown time."],"exampleFix":"// before\nClickFixedTeamSlot(misplaced.Slot);\nawait Delay(500, _ct);\nif (!TryClickText(page, \"换下\", Rect1080(382, 994, 87, 51)))\n{\n    throw new PartySetupFailedException($\"切换角色：未找到 {misplaced.Slot} 号位的换下按钮\");\n}\n\n// after\nClickFixedTeamSlot(misplaced.Slot);\nawait Delay(500, _ct);\nif (!await NewRetry.WaitForAction(() =>\n        {\n            using var c = CaptureToRectArea();\n            return TryClickText(page, \"换下\", Rect1080(382, 994, 87, 51));\n        }, _ct, 3, 400))\n{\n    throw new PartySetupFailedException($\"切换角色：未找到 {misplaced.Slot} 号位的换下按钮\");\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await switchTask.Start(slot1, slot2, slot3, slot4, usePhysicalSlots, ct); }\ncatch (PartySetupFailedException ex) when (ex.Message.Contains(\"换下按钮\"))\n{\n    _logger.LogWarning(ex, \"换下按钮识别失败，建议稍后重试或检查分辨率\");\n    // optionally retry once after re-capturing\n}","preventionTips":["Run the game at the 1080p-derived resolution so all Rect1080 ROIs line up.","Keep ROIs and OCR text constants in sync with the current Genshin version.","Wrap SwitchCharacterStateMachineTask.Start in try-catch(PartySetupFailedException) and treat remove-button failures as retryable."],"tags":["party-setup","ui-automation","ocr","fixed-roi","switch-character"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}