- Added `find_template` and `template_visible` functions for template matching in screenshots. - Introduced `read_int_white_on_dark` for OCR of bright text on dark backgrounds. - Ported arena functionality from reference, including ticket management, opponent selection, and reward collection. - Implemented safety checks for modal visibility and result confirmation to prevent unintended ticket spends. - Live-tested the arena task, confirming functionality across multiple tickets with real fight outcomes. - Updated mapping documentation to reflect new arena task implementation and its unique navigation requirements.
11 KiB
11 KiB
Reference mapping
Maps each local feature to the corresponding ~/repo/baas-reference/module/... implementation, driver/backend replacements, and current status. Update this before/while implementing or migrating a feature — see CLAUDE.md → "Reference mapping notes" and plan.md → "Reference mapping table".
| Local feature | Reference file | Reference functions/classes | Local file | Backend replacements | Status |
|---|---|---|---|---|---|
| Mailbox | module/mail.py |
to_mail, implement |
ba_auto/tasks/mailbox.py |
tap/click via xdotool, screenshot via scrot, color.rgb_in_range → driver.color_at pixel-probe check |
Migrated: real Python, state-verified via color probe (no legacy bridge) |
| Cafe | module/cafe_reward.py |
to_cafe (its relationship_rank_up popup-handling now also ported, see below), interaction_for_cafe_solve_method3, collect |
ba_auto/tasks/cafe.py |
picture.co_detect/color.rgb_in_range → driver.color_at pixel-probe checks; sparkle template match ported in-process into ba_auto/detector.py (find_cafe_sparkle, now multi-scale) |
Migrated: real Python, state-verified via color probes (no legacy bridge). Pat loop now polls for the full attempt budget instead of stopping on the first miss (see plan.md Phase 6 follow-up) — not yet confirmed against a live sparkle since none was available during testing. _dismiss_rank_up_if_shown reuses navigation.is_on_subscreen to detect and clear the full-screen bond-rank-up cutscene after a pat (see plan.md Phase 6 follow-up: rank-up popups) — not yet live-confirmed against a real trigger |
| Stamina/AP | module/collect_daily_free_power.py, module/collect_daily_task_power.py |
to_tasks/implement (task-power, ported); to_purchase_pyroxenes_menu (free-power, not ported) |
ba_auto/tasks/stamina.py |
color.rgb_in_range → driver.color_at; reference's per-tab claim loop → live UI's single "一括受取" bulk-claim button + Enter |
Partially migrated: Mission-panel claim done (see plan.md Phase 8). Daily Free Power (real-money purchase menu) deliberately not automated |
| Normal/Hard story AP sweep | module/explore_tasks/sweep_task.py, module/explore_tasks/task_utils.py |
to_region (ported: OCR region-number readout + delta-click), a scoped-down swipe_search_target_str (ported: OCR stage-row label matching), start_sweep's named-outcome contract (ported via navigation.wait_for_state, this project's scoped co_detect port) |
ba_auto/tasks/story_sweep.py |
OCR region/stage-name matching, ported for real (Phase 10) — replaces Phase 9's "next-region arrow stops advancing, then random stage" heuristic; MAX click verified via SWEEP_MINUS_BUTTON_PROBE color check (reused, still correct), modal closed via its own X button (Escape doesn't close it; X-button position re-calibrated per stage-layout variant, see Phase 10) |
Done (see plan.md Phase 10, supersedes Phase 9). Config-driven exact (region, stage, count) targets (config.STORY_SWEEP_TARGETS), not latest-region/random-stage. Opt-in only, not in default flow |
| Group/Club AP | module/group.py |
Need to inspect | ba_auto/tasks/group.py |
fixed click + state check via local driver | Not started |
| Bounty | module/rewarded_task.py |
Need to inspect | ba_auto/tasks/bounty.py |
sweep/color/OCR adaptation | Not started |
| Commissions | module/clear_special_task_power.py |
Need to inspect | ba_auto/tasks/commission.py |
sweep/color adaptation | Not started |
| Arena | module/arena.py |
implement (main flow), to_tactical_challenge (nav from main page), get_tickets (ticket-count OCR), choose_enemy (self/opponent level OCR + bounded refresh-reroll loop), check_skip_button (skip-toggle color probe), fight (click fight, wait for win/lose), collect_tactical_challenge_reward (two reward-slot color probes) |
ba_auto/tasks/arena.py |
Ticket count/self level/opponent level/rank are plain digit OCR (detector.read_int/read_text, plus a new read_int_white_on_dark for the profile card's bright-on-dark level text). Skip-toggle state and the two reward-slot claimed-vs-claimable colors are plain pixel-color probes (driver.color_at/_color_in_range). The post-fight WIN/LOSE result modal and an unrelated list-refresh-expired notice are NOT detected by precisely locating a button (a color-region search proved unreliable — see Status); they're dismissed via a bounded blind-Enter-press loop matching lesson.py's own _run_one_schedule pattern, gated by a hard safety check against the one modal where Enter is dangerous (opponent-info's own attack-formation button, checked via its fixed-position gold button). choose_enemy's refresh-reroll loop is direct Python control flow, bounded by maxArenaRefreshTimes. Config knobs carried over from reference defaults: ArenaComponentNumber=1, ArenaLevelDiff=0, maxArenaRefreshTimes=10, ArenaStopFightWhenRank1=False |
Done. Live-tested for real across all 5 of the account's daily tickets (2 WIN, 1 LOSE, 2 spent debugging the result-modal detection — see plan.md Phase 13 for the full writeup). Real navigation differences confirmed live: Tactical Challenge is a Work-hub card, not a bottom-nav icon; the reference's separate opponent-info and formation-edit screens are merged into one modal here with a live ticket-preview; navigation.is_modal_open's shared probe reads inverted on this screen (own _is_modal_open via config.ARENA_MODAL_PROBE). Three real bugs fixed: a level-OCR crop too small for tesseract despite looking legible (fixed by widening the crop, not the pipeline); level text being bright-on-dark unlike every other OCR read in this project (fixed via read_int_white_on_dark); and the result-modal detection cycling through two failed color-based designs (fixed by switching to bounded blind-Enter dismissal with a hard safety gate — a real near-miss of the same "mistimed keypress" hazard class CLAUDE.md already documents from story_sweep). Deliberately opt-in only, never in DEFAULT_ORDER — unlike every other opt-in task so far (which spend a known-safe resource on a config-driven target list), this one fights a real ranked PvP battle that can win or lose and moves the account's actual arena rank. Per explicit user decision: fights exactly one battle per invocation, matching the reference's own per-call pacing (its next_time = 55 background-thread rescheduling has no equivalent in this project's one-shot CLI). Not yet exercised live: the "no ticket" mid-flow race, an actual reroll click (every opponent offered was already an acceptable level), and ArenaStopFightWhenRank1's rank-1 stop condition — all implemented per the reference's logic, just not yet hit by real game state. detector.find_template/template_visible (generalized named-template matcher, built during scaffolding) ended up unused — state detection stayed OCR/color-probe-driven throughout, like every other task in this project. |
| Common Shop | module/shop/common_shop.py, module/shop/shop_utils.py |
implement, to_common_shop, get_item_position/ensure_choose/buy (shared, see Tactical Shop row) |
ba_auto/tasks/shop_common.py, ba_auto/tasks/shop_utils.py |
get_item_position's color+template item-state scan → fixed grid-position targets (config.COMMON_SHOP_TARGETS) + price-digit OCR verify, since the reference's own item-identification here indexes an external static price table (self.static_config.common_shop_price_list, fetched from a remote resource) this repo doesn't have — not per-item OCR, so this isn't an OCR-avoidance shortcut. Purchase-confirm dialog + reward-acquired banner handled via a single overlay-darkness probe (config.SHOP_OVERLAY_PROBE) instead of tracking each dialog's own layout |
Done. Live-tested with real purchases (all 8 configured targets bought, cost matched exactly). Discovered live: these items have a per-refresh-cycle purchase cap not shown as a visible counter (unlike the 青輝石 tab's "あと1回購入可能" labels) — confirmed by re-running the task after purchase and observing it correctly detect the now-unselectable items (checkbox + individual 購入 button both unresponsive) and safely decline rather than guess. A fresh, everything-available run hasn't been re-verified since the account had already exhausted this cycle's purchases via that same test |
| Tactical Shop | module/shop/tactical_challenge_shop.py, module/shop/shop_utils.py |
implement, goto_shop_by_name, shared get_item_position/ensure_choose/buy |
ba_auto/tasks/shop_tactical.py, ba_auto/tasks/shop_utils.py |
goto_shop_by_name's OCR swipe-search over the shop-tab list → fixed click (config.SHOP_TAB_TACTICAL): this account's tab list is only 7 entries and fits on screen with no scroll needed, confirmed live, so there's nothing to search for — not an OCR-avoidance shortcut. Same grid-position + price-OCR-verify + overlay-probe design as Common Shop, sharing shop_utils.run_shop_tab |
Done. Live-tested with real purchases (both configured AP-recovery drinks bought; AP and tactical-coin balance changes matched exactly) |
| Lesson/Schedule | module/lesson.py |
implement, to_lesson_location_select/to_select_location/to_all_locations (nav state machine), get_lesson_region_num/switch_lesson_region_page/to_lesson_region (paged region nav), get_lesson_each_region_status+check_region_availability (per-cell status via isometric-parallelogram pixel scan), get_lesson_relationship_counts (per-cell affection pip count via color count), choose_lesson (selection policy), execute_lesson/to_location_info/start_lesson (click cell -> info panel -> start -> result) |
ba_auto/tasks/lesson.py |
picture.co_detect -> navigation.wait_for_state-style bounded Enter-press loop (see below); the reference's paged-arrow region nav (needing OCR to know current position) -> this client renders the 12 regions as a scrollable list instead, which only ever settles at two scroll positions (config.LESSON_REGION_ROW_Y), so navigation is direct index-based clicking with nothing to OCR-locate; the reference's isometric Parallelogram/Triangle per-cell scan (tuned to the reference's own screen layout) -> reading each portrait's heart-shaped affection badge via a dedicated OCR path (detector.read_int_on_heart_badge) needs no isometric geometry at all |
Done. Config-driven scope only in the sense of the policy (affection-first selection, sweep every unlocked region until tickets/lessons run out, no ticket purchasing, no favor-student targeting) -- unlike shop, no user-specific target list was needed since the reference's own lesson_region_name.JP (embedded directly in its default_config.py, not externally fetched) already names all 12 regions, used here only for logging. Live-tested for real: 5 real tickets spent across 3 regions with correct outcomes (ticket count, cleanup navigation, home-screen return all verified). Two real bugs were found and fixed from that run -- see below and plan.md's Lesson phase |
Do not implement a feature without filling at least the relevant row.