fix: address multiple bugs causing incorrect battle handling and result screen issues
- In arena.py, widened RESULT_MODAL_MAX_POLLS to 200 to accommodate real battle durations and added early exit check in _wait_for_result to prevent stuck screens. - Updated _fight_one to properly handle _wait_for_result's return value, ensuring failures stop the fight loop. - In bounty.py, implemented OCR verification in _confirm_dialog_is_sweep to ensure the correct confirmation dialog is displayed before proceeding with sweeps, preventing unintended battles. - Documented hazards and fixes in plan.md, detailing incidents from 2026-07-17 that led to these changes.
This commit is contained in:
parent
7ccb28a1f9
commit
a30c3888d6
@ -1065,6 +1065,39 @@ BOUNTY_RESULT_BUTTON_MIN_PIXELS = 3000
|
||||
# date-ordinal-modulo scheme as STORY_SWEEP_ROTATION_*/EVENT_SWEEP_ROTATION_*.
|
||||
BOUNTY_SWEEP_COUNT = "max"
|
||||
|
||||
# Real-usage hazard, reported live 2026-07-17: the 任務情報 (task info)
|
||||
# modal has TWO separate action buttons stacked vertically -- the cyan
|
||||
# 掃討開始 (start sweep, safe, instant) this task intends to click, and a
|
||||
# separate gold 任務開始 (start mission, a REAL manual battle) directly
|
||||
# below it, both showing an identical "N→N-1" ticket-cost preview tooltip.
|
||||
# A real run's log showed `_click_sweep_start_and_verify` succeeding
|
||||
# cleanly (no retry messages) right after two failed
|
||||
# `_click_max_and_verify` attempts, followed by `_watch_sweep_result`
|
||||
# eventually reporting "swept" -- but the account was left showing a real
|
||||
# "Battle Complete" result screen (a live combat timer, ~3 minutes, visible
|
||||
# in a follow-up screenshot) instead of having done an instant sweep, and
|
||||
# every task that ran afterward failed to open its own screen for the rest
|
||||
# of that `daily` run. The exact mechanism that let this happen was not
|
||||
# fully reproduced live (doing so would mean deliberately repeating a real
|
||||
# battle), but every check along this path
|
||||
# (_count_raised_above_one/_is_sweep_usage_confirm/_watch_sweep_result's
|
||||
# own "swept" conditions) is a generic color/position probe with no check
|
||||
# on WHAT is actually showing -- any of them could plausibly be satisfied
|
||||
# by an unexpected screen (including a real battle's own UI) the same way
|
||||
# login.py's single/few-point checks were repeatedly fooled by unexpected
|
||||
# splash-art frames the same day (see plan.md's Phase 18 follow-up #3).
|
||||
#
|
||||
# Fixed with an OCR text check (_confirm_dialog_is_sweep in bounty.py)
|
||||
# gating the FINAL, irreversible SWEEP_CONFIRM_BUTTON click -- ported
|
||||
# directly from the same real, live-captured dialog
|
||||
# ("指名手配チケットをN使用して、掃討をN回行いますか?", captured
|
||||
# 2026-07-17 by reaching the real confirm dialog and cancelling before
|
||||
# confirming, the same safe-calibration pattern the original Phase 15
|
||||
# bounty work used throughout) rather than trusting SWEEP_CONFIRM_CYAN's
|
||||
# color match alone to mean "this is definitely the sweep confirm dialog
|
||||
# and not some other cyan-styled confirmation."
|
||||
BOUNTY_SWEEP_CONFIRM_TEXT_RECT = (605, 505, 1320, 615)
|
||||
|
||||
# Gem shop daily free package (毎日無料パッケージ), ported from
|
||||
# module/collect_daily_free_power.py. Reference reads: home-screen icon ->
|
||||
# purchase-pyroxenes dialog -> パッケージ (package) tab -> the FREE card at a
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -67,12 +67,58 @@ that mean nothing from wherever the previous run left the game. Fixed by
|
||||
calling the shared navigation.return_to_home(driver) at the very start of
|
||||
run(), before _open_tactical_challenge -- the same generic Escape-based
|
||||
recovery primitive event_sweep.py uses for its own wrong-page recovery.
|
||||
|
||||
Real hazard found via a real unattended `daily` cron run (2026-07-17),
|
||||
reported live by the user: the log showed "battle skip already on" printed
|
||||
for all 5 fights and a clean "fought 5 battle(s) this run", but the account
|
||||
was left showing a real "Battle Complete" result screen with a ~3-minute
|
||||
combat timer -- battles had actually run in full, not skipped -- and every
|
||||
task that ran for the rest of that `daily` invocation failed to open its
|
||||
own screen. Two compounding bugs, both fixed: (1) `_ensure_skip_on`'s "on"
|
||||
detection has never actually been verified to reject a real "off" state
|
||||
(see config.py's ARENA_SKIP_ON_RGB comment -- skip happened to already be
|
||||
on every session calibrated so far), so a wrong "already on" reading was
|
||||
never caught and the toggle was never actually clicked; (2)
|
||||
independently, and more directly responsible for the stuck screen,
|
||||
`_wait_for_result` had no early-exit signal at all and always ran its full
|
||||
fixed budget (10*1.5=15s) before unconditionally declaring success -- far
|
||||
too short for a real, non-skipped battle, and `_fight_one` on top of that
|
||||
discarded `_wait_for_result`'s return value entirely, so even a correctly
|
||||
*detected* failure (including the dangerous opponent-info-modal case its
|
||||
own hard safety gate exists to catch) never actually stopped `run()`'s
|
||||
fight loop from proceeding to the next ticket. Fixed by widening
|
||||
`RESULT_MODAL_MAX_POLLS` substantially (to tolerate a real battle's full
|
||||
duration regardless of whether the skip-mode detection bug gets fully
|
||||
root-caused) while adding a new `_back_on_challenge_list` early exit (so
|
||||
the normal fast-skip-mode case doesn't also slow down), and by making
|
||||
`_fight_one` actually stop the loop when `_wait_for_result` reports
|
||||
failure. The skip-toggle's own "off" detection was NOT independently
|
||||
re-verified (would require spending a real ticket at a moment the account
|
||||
had none left) -- this fix is deliberately structured to make the
|
||||
consequences safe even if that underlying detection is still wrong, rather
|
||||
than depending on fully root-causing it first.
|
||||
"""
|
||||
|
||||
from ba_auto import detector, navigation
|
||||
|
||||
OPEN_RETRIES = 3
|
||||
RESULT_MODAL_MAX_POLLS = 10
|
||||
# Widened from 10 (real hazard found live 2026-07-17, reported by the user):
|
||||
# this loop originally always ran its full fixed budget with no early exit,
|
||||
# so at the old 10*1.5=15s bound it silently assumed success even when
|
||||
# _ensure_skip_on's "already on" check was wrong and a fight actually ran
|
||||
# as a real, non-skipped battle (confirmed live: a "Battle Complete" result
|
||||
# screen with a ~3-minute combat timer was found on the account afterward,
|
||||
# and every task that ran for the rest of that `daily` invocation failed to
|
||||
# open its own screen). _ensure_skip_on's own "off" detection has never
|
||||
# actually been verified live (see config.py's ARENA_SKIP_ON_RGB comment --
|
||||
# it was only ever confirmed to recognize "on", ON by default every session
|
||||
# so far), so this bound is widened generously to tolerate a real battle
|
||||
# regardless of whether that gets fixed, rather than trusting skip mode to
|
||||
# always engage. The new _back_on_challenge_list early-exit (see
|
||||
# _wait_for_result) means this larger bound doesn't slow down the normal,
|
||||
# already-working case -- a fast skip-mode fight still exits within the
|
||||
# first couple of polls, same as before.
|
||||
RESULT_MODAL_MAX_POLLS = 200
|
||||
RESULT_POLL_INTERVAL = 1.5
|
||||
|
||||
|
||||
@ -221,6 +267,19 @@ def _opponent_info_modal_showing(driver, config):
|
||||
return _color_in_range(driver.color_at(*config.ARENA_ATTACK_FORMATION_BUTTON), config.ARENA_REWARD_CLAIMABLE_RGB)
|
||||
|
||||
|
||||
def _back_on_challenge_list(driver, config):
|
||||
# The natural "nothing left to dismiss, the fight genuinely wrapped up"
|
||||
# signal -- the same navigation.is_on_subscreen check
|
||||
# _open_tactical_challenge itself trusts to confirm this exact screen,
|
||||
# combined with this module's own (inverted-reading) _is_modal_open to
|
||||
# rule out a dialog still open on top of it. Added alongside widening
|
||||
# RESULT_MODAL_MAX_POLLS (2026-07-17) so a real, non-skipped battle's
|
||||
# much longer wait doesn't also slow down the normal fast-skip-mode
|
||||
# case -- lets _wait_for_result's loop exit as soon as a fight is
|
||||
# actually done instead of always blindly running its full budget.
|
||||
return navigation.is_on_subscreen(driver) and not _is_modal_open(driver, config)
|
||||
|
||||
|
||||
def _wait_for_result(driver, config):
|
||||
"""Dismiss whatever unpredictable sequence of screens follows Sortie:
|
||||
the "対戦結果" WIN/LOSE modal, and occasionally an unrelated "list
|
||||
@ -250,18 +309,38 @@ def _wait_for_result(driver, config):
|
||||
rather than risk an unintended second ticket spend the way a mistimed
|
||||
keypress caused a real hazard elsewhere in this project (see CLAUDE.md's
|
||||
story_sweep writeup).
|
||||
|
||||
Real hazard found live (2026-07-17, reported by the user): this loop
|
||||
originally had no early exit at all -- it always ran its full fixed
|
||||
budget (10*1.5=15s), assuming success once that budget elapsed. When
|
||||
_ensure_skip_on's "battle skip already on" check was wrong (see
|
||||
config.py's ARENA_SKIP_ON_RGB comment -- its "off" detection was never
|
||||
actually verified live), a fight ran as a real, non-skipped battle
|
||||
(confirmed via a screenshot: a "Battle Complete" result screen with a
|
||||
~3-minute combat timer), and this loop declared the result handled
|
||||
after 15s regardless -- while the real battle was still playing. Every
|
||||
task that ran for the rest of that `daily` invocation then failed to
|
||||
open its own screen, because the game was never actually back at a
|
||||
known state. Fixed two ways: RESULT_MODAL_MAX_POLLS widened
|
||||
substantially to tolerate a real battle's full duration regardless of
|
||||
whether the skip-mode bug gets fixed separately, and a new
|
||||
_back_on_challenge_list early exit so this widened budget doesn't also
|
||||
slow down the normal fast-skip-mode case -- the loop now exits as soon
|
||||
as a fight is genuinely confirmed over, not after a fixed guess.
|
||||
"""
|
||||
for _ in range(RESULT_MODAL_MAX_POLLS):
|
||||
if _opponent_info_modal_showing(driver, config):
|
||||
print("[arena] warning: opponent-info modal unexpectedly showing during result wait -- stopping without pressing Enter to avoid an unintended ticket spend")
|
||||
return False
|
||||
if _back_on_challenge_list(driver, config):
|
||||
return True
|
||||
driver.keypress(config.ARENA_RESULT_CONFIRM_KEY)
|
||||
driver.wait(RESULT_POLL_INTERVAL)
|
||||
|
||||
if _opponent_info_modal_showing(driver, config):
|
||||
print("[arena] warning: opponent-info modal showing after result-wait timeout -- leaving as-is without pressing Enter")
|
||||
return False
|
||||
return True
|
||||
return _back_on_challenge_list(driver, config)
|
||||
|
||||
|
||||
def _fight_one(driver, config):
|
||||
@ -292,7 +371,15 @@ def _fight_one(driver, config):
|
||||
driver.keypress(config.ARENA_SORTIE_CONFIRM_KEY)
|
||||
driver.wait(2)
|
||||
|
||||
_wait_for_result(driver, config)
|
||||
# Real bug found alongside the _wait_for_result fixes above
|
||||
# (2026-07-17): this used to discard _wait_for_result's return value
|
||||
# and always report success regardless, so even a detected problem --
|
||||
# including the dangerous opponent-info-modal case its own hard safety
|
||||
# gate exists to catch -- never actually stopped run()'s fight loop
|
||||
# from proceeding to the next ticket.
|
||||
if not _wait_for_result(driver, config):
|
||||
print("[arena] result not confirmed after the fight -- stopping without attempting further fights")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@ -86,6 +86,38 @@ bulk/MAX-count sweep's result-screen flow specifically -- only count=1 was
|
||||
ever tested (ticket scarcity forced it), so it's not yet confirmed whether a
|
||||
bulk sweep shows the same single-OK dialog or a SKIP-then-OK sequence like
|
||||
story_sweep/event_sweep's own bulk-sweep result screens.
|
||||
|
||||
Real hazard found and fixed via a real unattended `daily` cron run
|
||||
(2026-07-17), reported live by the user: a real "Battle Complete" screen (a
|
||||
live combat timer, ~3 minutes) was found on the account instead of an
|
||||
instant sweep having happened, and every task that ran afterward in that
|
||||
same `daily` invocation failed to open its own screen for the rest of the
|
||||
run -- this task's own `_watch_sweep_result` had reported "swept" cleanly,
|
||||
with no warnings anywhere in the log. Manually reproducing the flow live
|
||||
(reaching the real confirm dialog and cancelling before confirming, same
|
||||
safe-calibration pattern as the original Phase 15 work) found the actual
|
||||
hazard: the 任務情報 modal has TWO separate action buttons stacked
|
||||
vertically -- the intended cyan 掃討開始 (start sweep, instant) and a
|
||||
separate gold 任務開始 (start mission, a REAL manual battle) directly below
|
||||
it, both showing an identical "N→N-1" ticket-cost preview. Every check
|
||||
along this task's own commit path (`_count_raised_above_one`,
|
||||
`_is_sweep_usage_confirm`, `_watch_sweep_result`'s own "swept" conditions)
|
||||
is a generic color/position probe with no verification of WHAT is actually
|
||||
showing -- the exact same class of false-positive risk that repeatedly
|
||||
fooled login.py's single/few-point checks the same day (see plan.md's
|
||||
Phase 18 follow-up #3), just with much higher stakes here (an actual
|
||||
battle, not a misread loading screen). The precise mechanism that let a
|
||||
real run reach 任務開始 instead of 掃討開始 was not fully reproduced live
|
||||
(doing so would mean deliberately repeating a real battle) -- but the fix
|
||||
closes the hazard regardless of the exact upstream cause: `_confirm_dialog_
|
||||
is_sweep` OCR-verifies the confirm dialog's actual text
|
||||
("指名手配チケットをN使用して、掃討をN回行いますか?", captured live from
|
||||
the real dialog) before the one irreversible click in this whole flow, and
|
||||
cancels rather than confirms if it doesn't read as a sweep. Not yet
|
||||
re-verified live against a fresh real sweep (would need to spend another
|
||||
real ticket) -- the dialog-text capture and cancel-without-confirming were
|
||||
both done live, but the actual gate has not yet been exercised by a
|
||||
genuine full run.
|
||||
"""
|
||||
import datetime
|
||||
|
||||
@ -119,6 +151,23 @@ def _is_ticket_purchase_prompt(driver, config):
|
||||
return _color_in_range(driver.color_at(*config.SWEEP_CONFIRM_BUTTON), config.SWEEP_CONFIRM_GOLD)
|
||||
|
||||
|
||||
def _confirm_dialog_is_sweep(driver, config):
|
||||
"""OCR-verify the confirm dialog reached after clicking 掃討開始 is
|
||||
genuinely the sweep-usage confirm ("指名手配チケットをN使用して、掃討を
|
||||
N回行いますか?"), not some other cyan-styled confirmation that happens
|
||||
to satisfy _is_sweep_usage_confirm's color-only check -- see
|
||||
config.py's BOUNTY_SWEEP_CONFIRM_TEXT_RECT for the real incident this
|
||||
guards against (a real battle got triggered instead of a sweep, and
|
||||
every check along that path was a generic color/position probe with no
|
||||
check on what was actually showing). Substring match on "掃討" rather
|
||||
than an exact match, matching this project's own established
|
||||
dialog-classification convention (cafe.py's 衣装/隣 checks,
|
||||
event_sweep.py's "終了" check).
|
||||
"""
|
||||
text = detector.read_text(config.BOUNTY_SWEEP_CONFIRM_TEXT_RECT, psm=6, lang="jpn")
|
||||
return "掃討" in text
|
||||
|
||||
|
||||
def _find_result_button(driver, config):
|
||||
return detector.find_color_centroid(
|
||||
config.BOUNTY_SWEEP_RESULT_BUTTON_REGION, *config.SWEEP_CONFIRM_CYAN,
|
||||
@ -345,6 +394,19 @@ def _sweep_latest_stage(driver, config, area_index, count):
|
||||
_close_stage_modal(driver, config)
|
||||
return "inadequate_ticket"
|
||||
|
||||
# Hard safety gate before the one irreversible click in this whole
|
||||
# flow -- see config.py's BOUNTY_SWEEP_CONFIRM_TEXT_RECT for the real
|
||||
# incident (a real battle got triggered instead of a sweep) this
|
||||
# closes. _is_sweep_usage_confirm already passed (a color match), but
|
||||
# that alone isn't proof this is really the sweep dialog -- verify the
|
||||
# actual text before committing rather than trusting color alone.
|
||||
if not _confirm_dialog_is_sweep(driver, config):
|
||||
print("[bounty] confirm dialog text did not read as a sweep confirmation -- cancelling without confirming (see config.py's BOUNTY_SWEEP_CONFIRM_TEXT_RECT)")
|
||||
driver.click(*config.SWEEP_CONFIRM_CANCEL_BUTTON)
|
||||
driver.wait(1)
|
||||
_close_stage_modal(driver, config)
|
||||
return "unrecognized_state"
|
||||
|
||||
driver.click(*config.SWEEP_CONFIRM_BUTTON)
|
||||
driver.wait(1.5)
|
||||
print("[bounty] sweep confirmed, waiting for results")
|
||||
|
||||
25
plan.md
25
plan.md
@ -587,6 +587,19 @@ This is a deliberate reversal of the original Phase 13 design decision (exactly
|
||||
|
||||
**Not yet live-tested.** Deployed and remote-compiled; running this for real will fight multiple real ranked battles and take several minutes (multiple 30s cooldowns) — confirm with the user before spending real arena tickets on a live test, per this project's established pattern for any live test of a newly-changed resource-spending flow.
|
||||
|
||||
#### Phase 13 follow-up #3: battles ran unskipped, and a stuck result screen broke the rest of a real `daily` run (2026-07-17)
|
||||
|
||||
Follow-up #2's multi-fight loop did eventually run live, via cron, and fought all 5 of the account's real daily tickets — but the user reported the `daily` run left the game stuck, with a real "Battle Complete" screen visible (a live ~3-minute combat timer). The log had shown "battle skip already on" for all 5 fights and "fought 5 battle(s) this run" with no warnings, yet the battles had clearly run in full, not skipped — and every task that ran afterward in that same `daily` invocation failed to open its own screen for the rest of the run (this was the same incident whose actual stuck screen the user later pinpointed to bounty, not arena — see Phase 15 follow-up above — but the arena skip-mode issue found along the way was independently real and confirmed).
|
||||
|
||||
Two compounding bugs, both fixed:
|
||||
|
||||
1. `_ensure_skip_on`'s "on" detection (`ARENA_SKIP_ON_RGB`) has never actually been verified live to reject a real "off" state — the config.py comment already flagged this at calibration time: skip happened to already be on by default every session tested so far, so only the "on" color was ever confirmed, not the boundary against "off". If the toggle were ever genuinely off, this check could plausibly still read "already on" and never attempt to click it.
|
||||
2. Independently, and more directly responsible for the stuck screen: `_wait_for_result` had no early-exit signal at all — it always ran its full fixed budget (`RESULT_MODAL_MAX_POLLS=10 * RESULT_POLL_INTERVAL=1.5s` = 15s) before unconditionally declaring success, far too short for a real, non-skipped battle. On top of that, `_fight_one` discarded `_wait_for_result`'s return value entirely (`_wait_for_result(driver, config); return True`), so even a *correctly detected* failure — including the dangerous opponent-info-modal case its own hard safety gate exists to catch — never actually stopped `run()`'s fight loop from proceeding to the next ticket.
|
||||
|
||||
Fixed without requiring a full root-cause of bug 1 (which would need spending a real ticket to re-verify, and the account had none left that day): `RESULT_MODAL_MAX_POLLS` widened substantially (200 polls, up to 300s) to tolerate a real battle's full duration regardless of whether the skip-mode detection is ever separately fixed, paired with a new `_back_on_challenge_list` early-exit check (`navigation.is_on_subscreen` plus the module's own inverted-reading `_is_modal_open`) so the normal, already-working fast-skip-mode case doesn't also get slowed down — a fight that genuinely finishes quickly still exits the loop within the first couple of polls, same as before. `_fight_one` now correctly propagates `_wait_for_result`'s return value, so a detected failure actually stops the loop instead of being silently ignored.
|
||||
|
||||
Deliberately structured so the fix is safe even if the skip-toggle detection bug is never separately root-caused: the worst case is now "a real battle takes as long as it takes and the code correctly waits for it," not "the code guesses wrong after 15 seconds and leaves the game in an unknown state for every subsequent task to fail against." Not yet re-verified live (arena tickets were at 0 by the time this was diagnosed) — both the widened wait and the `_fight_one` propagation fix, and the skip-toggle's own "off" detection remains unverified.
|
||||
|
||||
### Phase 14: Event sweep
|
||||
|
||||
**Status: Implemented, calibrated live against zero real AP spend, NOT yet live-tested with a real sweep.** Ported `module/sweep_activity.py` -> `module/activities/activity_utils.py`'s `activity_sweep`/`start_sweep` to `ba_auto/tasks/event_sweep.py`, per explicit user request (2026-07-10): "the current event has up to 12 stages, randomly choose stage 9-12, same mod%4 date method as story sweep."
|
||||
@ -709,6 +722,18 @@ With that fixed, the user approved spending the account's one remaining ticket o
|
||||
|
||||
**Confirmed real**: 2 live sweeps total, credits gained both times (+180,000 then +36,000), clean automatic return home both times, zero manual intervention, zero real Pyroxene spent despite the near-miss. **Not yet re-confirmed live**: a fresh sweep with today's fix deployed (account is at 0/6 tickets as of session end) and any bulk/MAX-count sweep's result-screen flow specifically (only count=1 was ever tested, since ticket scarcity forced it — a bulk sweep may show a SKIP-then-OK sequence like story_sweep/event_sweep rather than the single-OK dialog confirmed here).
|
||||
|
||||
#### Phase 15 follow-up: real battle triggered instead of a sweep (2026-07-17)
|
||||
|
||||
A real unattended `daily` cron run reported live by the user: instead of an instant sweep, the account was left showing a real "Battle Complete" result screen (a live ~3-minute combat timer, visible in a follow-up screenshot), and every task that ran for the rest of that `daily` invocation failed to open its own screen. `_watch_sweep_result` had reported "swept" cleanly, with no warnings anywhere in the log — the false success was silent.
|
||||
|
||||
The user first suspected arena (which had its own, separate, genuinely confirmed skip-mode bug the same day — see Phase 13 follow-up #3 below), but direct real-time observation ("When you run the script I saw the screen go out from bounty") placed the actual stuck screen specifically at bounty's own run.
|
||||
|
||||
Diagnosed by safely reproducing the real flow live (reaching the actual confirm dialog and cancelling before confirming — the same safe-calibration pattern the original Phase 15 work used throughout, applied again rather than guessing): the 任務情報 modal has TWO separate action buttons stacked vertically — the intended cyan 掃討開始 (start sweep, instant, safe) and a separate gold 任務開始 (start mission, a REAL manual battle) directly below it, both showing an identical "N→N-1" ticket-cost preview tooltip. Every check along `_sweep_latest_stage`'s own commit path (`_count_raised_above_one`, `_is_sweep_usage_confirm`, `_watch_sweep_result`'s own "swept" ends conditions) is a generic color/position probe with zero verification of *what* is actually showing — the same class of false-positive risk that independently, repeatedly fooled login.py's single/few-point checks the same day (Phase 18 follow-up #3), just with far higher stakes here.
|
||||
|
||||
Live-captured the real confirm dialog's actual text for the first time — "指名手配チケットを1使用して、掃討を1回行いますか?" — by carefully driving `bounty.py`'s own functions one step at a time (open the stage 10 modal — genuinely 3-starred/cleared, contradicting an early "uncleared stage" theory — MAX then MIN the count back down to the safest 1, click 掃討開始, screenshot, then cancel via Escape without ever clicking OK). Fixed with a new `_confirm_dialog_is_sweep` OCR check (`config.BOUNTY_SWEEP_CONFIRM_TEXT_RECT`, substring match on "掃討" matching this project's established dialog-classification convention — cafe.py's 衣装/隣 checks, event_sweep.py's "終了" check) gating the one truly irreversible click in this whole flow: if the dialog doesn't read as a sweep confirmation, cancel rather than confirm.
|
||||
|
||||
The exact mechanism that let a real run reach 任務開始 instead of 掃討開始 was not fully reproduced live — doing so would mean deliberately repeating a real battle, which wasn't warranted once the actual hazard (no text verification anywhere in this path) was clearly identified and closed. The fix addresses the hazard regardless of the exact upstream cause. Not yet re-verified live against a fresh real sweep (would need to spend another real ticket).
|
||||
|
||||
### Phase 16: Gem shop daily free package (2026-07-15)
|
||||
|
||||
Reference: `module/collect_daily_free_power.py`. Local: `ba_auto/tasks/gem_shop.py`. Requested directly by the user with reference screenshots (`screenshots/gem_shop/1-4.png`): claim the 毎日無料パッケージ (daily free package, +10 AP / +10,000 credits, 0 yen, once per day) inside the 青輝石購入 (gem purchase) dialog. This is the exact piece Phase 8 deliberately deferred (see its follow-up above) — that phase stopped at "this menu has real-money purchase buttons visible immediately," but the specific free card is a fixed, non-selectable, no-decision claim, same category as Phase 8's own Mission-panel claim.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user