189 lines
10 KiB
Python
189 lines
10 KiB
Python
"""Central configuration for ba-auto-daily, ported from the old ba_dailies.sh."""
|
|
import os
|
|
|
|
DISPLAY = ":0"
|
|
XAUTHORITY = "/run/user/1000/gdm/Xauthority"
|
|
ENV = {**os.environ, "DISPLAY": DISPLAY, "XAUTHORITY": XAUTHORITY}
|
|
|
|
WINDOW_NAME = "BlueArchive"
|
|
ASSET_DIR = os.path.expanduser("~/ba_assets")
|
|
|
|
# Runtime working files (probe/detection screenshots); never /tmp, per CLAUDE.md.
|
|
SCRATCHPAD_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scratchpad")
|
|
os.makedirs(SCRATCHPAD_DIR, exist_ok=True)
|
|
|
|
# Refined from (1726, 60): that coordinate sat on the edge of the icon's
|
|
# hitbox and intermittently missed during live testing.
|
|
MAILBOX_ICON = (1732, 50)
|
|
CLAIM_ALL = (1691, 1128)
|
|
|
|
MISSION_ICON = (75, 350)
|
|
# Background pixel inside the "一括受取" (claim all) button on the Mission
|
|
# panel: bright yellow (r~250, r-b~180+) when something is claimable, flat
|
|
# grey (r-b<20) when not. Calibrated live at 1920x1200.
|
|
MISSION_CLAIM_PROBE = (1600, 1100)
|
|
|
|
CAFE_ICON = (165, 1100)
|
|
CAFE_ROOM_SWITCH = (190, 160)
|
|
CAFE_INCOME = (1780, 1105)
|
|
# Max sparkle-detection attempts per room (hits and misses both count --
|
|
# sparkles are on a per-student cooldown, so most checks legitimately find
|
|
# nothing and the loop keeps polling rather than giving up after one miss).
|
|
CAFE_MAX_CLICKS_PER_ROOM = 15
|
|
CAFE_SPARKLE_TEMPLATE = os.path.join(ASSET_DIR, "cafe_sparkle.png")
|
|
# A pat that crosses an affection-rank threshold shows a full-screen "絆ラン
|
|
# クアップ!" (Bond Rank Up!) cutscene with no cafe header visible at all --
|
|
# confirmed against screenshots/cafe/student/01-02: navigation.is_on_subscreen's
|
|
# header probe reads (183,220,240) there (r<200, fails) vs (248,249,250) on
|
|
# the normal cafe screen (r>200, passes), so the existing header-brightness
|
|
# check already tells the two apart. Bounds how many Enter presses
|
|
# _dismiss_rank_up_if_shown will try before giving up.
|
|
CAFE_RANK_UP_DISMISS_RETRIES = 5
|
|
|
|
# Home -> お仕事 (Work hub) -> 任務 (Task) card -> Normal/Hard story region browser.
|
|
WORK_ICON = (1793, 1138)
|
|
# Moved up from the original (1370, 450): that point sat close enough to the
|
|
# 任務 card's bottom edge that a live Phase 10 run missed and landed on the
|
|
# "総力戦" (Total War) card in the row below instead -- confirmed live via
|
|
# screenshot, not just a hunch. (1250, 380) sits solidly mid-card, on the
|
|
# "任務" title text itself, well clear of every edge.
|
|
TASK_CARD = (1250, 380)
|
|
REGION_RIGHT_ARROW = (1862, 598)
|
|
# Pixel-scanline-scanned (not visually estimated -- see plan.md Phase 8's
|
|
# lesson) from scratchpad/stage_info.png: the "<" chevron's navy-blue pixel
|
|
# centroid was (66, 597), mirroring REGION_RIGHT_ARROW. Used for the
|
|
# OCR-driven to_region port (ba_auto/tasks/story_sweep.py) to step backward
|
|
# when the current region is past the target.
|
|
REGION_LEFT_ARROW = (66, 598)
|
|
# Bounds the "read region, click delta, re-check" loop in
|
|
# ba_auto/tasks/story_sweep.py's _go_to_region. A correct read normally
|
|
# converges in one round; this just guards against a stuck OCR misread.
|
|
REGION_NAV_MAX_ATTEMPTS = 8
|
|
|
|
# Region-number readout on the region browser's left panel (the "Area 30"
|
|
# card's big digits, below the smaller "Area" label). Rect pixel-scanned from
|
|
# scratchpad/stage_info.png: the "Area" label occupies roughly y 295-325, the
|
|
# number itself y 330-372 -- this rect isolates just the digits. Replaces
|
|
# _go_to_latest_region's "spam the arrow and hope" (see plan.md Phase 9's
|
|
# retrospective) with task_utils.py::to_region's actual OCR-read-and-click-
|
|
# the-exact-delta approach.
|
|
REGION_NUMBER_OCR_RECT = (175, 325, 250, 380)
|
|
|
|
# Stage list panel (right side of the region browser). Scrolling to either
|
|
# extreme always shows exactly 4 full stage rows, since every region has at
|
|
# least 5 stages -- scrolling past either end is a harmless no-op (verified
|
|
# live), so a generous bounded click count is safe. Each stage row's "入場"
|
|
# (enter) button sits at STAGE_ENTER_X across both scroll extremes; the two
|
|
# row-position sets below were measured at each extreme.
|
|
STAGE_LIST_SCROLL_POINT = (1400, 700)
|
|
# 10 was the original (Phase 9) calibration, but live re-testing during
|
|
# Phase 10 found it insufficient to reach the opposite extreme when the list
|
|
# was already scrolled near the other end (it undershot, landing between the
|
|
# two calibrated row-position sets and producing garbled OCR reads) -- 20
|
|
# reliably reached either extreme regardless of starting position. Scrolling
|
|
# past either end remains a harmless no-op (verified live both phases).
|
|
STAGE_LIST_SCROLL_CLICKS = 20
|
|
STAGE_ENTER_X = 1683
|
|
STAGE_ROWS_AT_TOP_Y = (424, 570, 718, 866)
|
|
STAGE_ROWS_AT_BOTTOM_Y = (483, 630, 778, 926)
|
|
|
|
# Stage-label OCR rect (e.g. "30-1", "30-A"), offset from a row's known
|
|
# center y above. Pixel-scanned across all eight row positions (both
|
|
# STAGE_ROWS_AT_TOP_Y and _BOTTOM_Y) in live captures -- x[1030,1150],
|
|
# y[row_y-40, row_y-4] consistently isolates just the label text line above
|
|
# the row's star-rating icons. The symmetric row_y+/-40 crop tried first
|
|
# during calibration included the stars below and reliably broke OCR (empty
|
|
# or garbled reads) even with a character whitelist -- see plan.md Phase 10.
|
|
# Replaces _pick_random_stage_row's "scroll to an extreme, grab a random one
|
|
# of the 4 rows" with a scoped-down port of the reference's
|
|
# swipe_search_target_str: since this client's stage list only ever has 2
|
|
# relevant scroll positions (top/bottom extremes, both already known-good),
|
|
# full swipe-and-retry generality isn't needed -- just OCR each of the 4
|
|
# visible rows at each extreme and match by label text.
|
|
STAGE_LABEL_OCR_X = (1030, 1150)
|
|
STAGE_LABEL_OCR_Y_PAD = (40, 4)
|
|
|
|
# 任務情報 (stage info) modal's sweep sub-panel. This modal is wide enough
|
|
# that navigation.MODAL_DIM_PROBE (960, 200) lands on the modal's own white
|
|
# card instead of the dimmed backdrop -- use a corner point that's outside
|
|
# the card in either scroll/region state instead.
|
|
STAGE_MODAL_PROBE = (1870, 600)
|
|
# Regular numbered stages (30-1..30-5) render an extra "集中指揮"/"簡易攻略"
|
|
# tab row and a manual "任務開始" panel below the sweep panel that the
|
|
# bonus "-A" stage Phase 9 originally calibrated against does not have --
|
|
# discovered live during Phase 10 when Phase 9's coordinates (calibrated
|
|
# only against 30-A) missed the MAX button on 30-3 by ~43px vertically.
|
|
# These values are pixel-scanned against 30-3's tabbed layout and are what
|
|
# config.STORY_SWEEP_TARGETS will hit in the common case (sweeping a
|
|
# regular numbered stage, not the "-A" bonus stage). If a target's stage is
|
|
# "A", these may be off by the same ~43px the old (untabbed) calibration
|
|
# used -- not yet re-confirmed against an actual "-A" stage since this fix;
|
|
# see plan.md Phase 10.
|
|
SWEEP_MAX_BUTTON = (1620, 550)
|
|
SWEEP_START_BUTTON = (1400, 710)
|
|
# The "-" stepper button next to the sweep count: flat grey (240,240,239)
|
|
# while count is still at its default of 1, vivid orange (255,111,0) once
|
|
# MAX (or any +) has raised it. Used to verify the MAX click actually landed
|
|
# instead of trusting a single click blindly, since this gates real AP spend.
|
|
SWEEP_MINUS_BUTTON_PROBE = (1285, 555)
|
|
# The modal's own "X" close icon (top-right corner of the white card).
|
|
# Escape does NOT close this modal (confirmed live: two Escape presses left
|
|
# it open with focus on the live "任務開始"/start-mission button) -- must
|
|
# click this explicitly. Pinned via pixel-scanline scan of the glyph's
|
|
# crossing point, not visual estimation.
|
|
#
|
|
# The whole card (not just the sweep sub-panel) is vertically centered on
|
|
# its own content height rather than anchored at a fixed absolute position
|
|
# -- confirmed live during Phase 10: the tabbed regular-stage layout (see
|
|
# SWEEP_MAX_BUTTON above) is taller than the "-A" bonus-stage layout this
|
|
# was originally calibrated against, and its X button sits ~46px higher
|
|
# on screen (225 vs the old 271) as a result. This value is re-measured
|
|
# against the taller, tabbed layout.
|
|
STAGE_MODAL_CLOSE_BUTTON = (1691, 225)
|
|
# "+" stepper button, for configured exact (non-"max") sweep counts. Pinned
|
|
# via color-scan (bright cyan glyph centroid) against 30-3's tabbed layout
|
|
# (see SWEEP_MAX_BUTTON above) -- unlike SWEEP_MAX_BUTTON/
|
|
# SWEEP_MINUS_BUTTON_PROBE this specific button has NOT been live-clicked
|
|
# yet; confirm it before relying on a non-"max" configured count (see
|
|
# plan.md Phase 10).
|
|
SWEEP_PLUS_BUTTON = (1520, 550)
|
|
|
|
# Clicking 掃討開始 (start sweep) always raises an "AP<N>使用して、掃討を
|
|
# <M>回行いますか?" usage-confirmation dialog before the sweep actually
|
|
# runs -- discovered live during Phase 10; the previous design had no
|
|
# handling for this dialog at all, which is what a crude early placeholder
|
|
# probe was misreading as "inadequate_ap" on every sweep, successful or not.
|
|
#
|
|
# If AP is too low for even one sweep (confirmed live by deliberately
|
|
# emptying the count via MAX/"+" at low AP), a dialog that looks the same
|
|
# and sits at the *same* OK-button position appears instead, but titled
|
|
# "AP購入" (real-currency AP purchase) with a gold/yellow OK button instead
|
|
# of cyan. The two are told apart by that color, not by position.
|
|
SWEEP_CONFIRM_BUTTON = (1150, 810)
|
|
SWEEP_CONFIRM_CANCEL_BUTTON = (770, 810)
|
|
SWEEP_CONFIRM_CYAN = ((90, 190, 230), (200, 240, 256))
|
|
SWEEP_CONFIRM_GOLD = ((200, 200, 50), (256, 256, 150))
|
|
|
|
# Region spanning every button this task clicks through after 掃討開始: the
|
|
# AP-usage-confirm OK above, and the "掃討完了" (sweep complete) results
|
|
# screen's "SKIP" (first, skips the reward-reveal animation) and final "OK"
|
|
# (after full reward totals appear) buttons. SKIP and the final OK share
|
|
# SWEEP_CONFIRM_CYAN's color but sit ~120px apart vertically, so this task
|
|
# finds whichever one is showing by color within this region instead of
|
|
# hardcoding each dialog's exact Y position.
|
|
SWEEP_RESULT_BUTTON_REGION = (700, 700, 1300, 1050)
|
|
|
|
# (region, stage, count) targets to sweep, mirroring the reference's
|
|
# unfinished_normal_tasks shape (module/explore_tasks/sweep_task.py). `stage`
|
|
# is 1-5, or the string "A" for the bonus stage that only exists when
|
|
# region % 3 == 0. `count` is a positive int (uses the "+" stepper) or the
|
|
# literal string "max" (uses the in-game MAX button).
|
|
#
|
|
# PLACEHOLDER -- region 1 stage 1 is unlikely to be what you actually want
|
|
# swept (it may not even be 3-starred/cleared on this account yet). Edit
|
|
# this list with your own already-cleared stage(s) before running
|
|
# story_sweep for real.
|
|
STORY_SWEEP_TARGETS = [
|
|
(1, 1, "max"),
|
|
]
|