Nik Afiq a04ef5d0c5 Refactor scratchpad directory references and update Phase 6 follow-up report
- Changed references from './scratchpad' to '.scratchpad/' in graph.json and plan.md for consistency.
- Expanded Phase 6 follow-up section in plan.md to clarify changes made to the pat detection logic:
  - Updated `find_cafe_sparkle()` to utilize multiple template scales for improved detection.
  - Modified `_pat_room` to allow polling for maximum clicks instead of breaking on the first miss.
  - Added mouse movement after each pat to prevent cursor occlusion of sparkles.
- Verified that room entry and modal state checks function correctly, but end-to-end pat success remains untested due to lack of available interactions.
2026-07-05 17:57:26 +09:00

106 lines
3.4 KiB
Python

"""Cafe daily task. Ported from baas-reference module/cafe_reward.py's state-probe pattern."""
from ba_auto import detector, navigation
ROOM_OPEN_RETRIES = 3
# "受取" (claim) renders as flat grey when there is nothing to collect yet.
CLAIM_PROBE = (960, 850)
CLAIM_DISABLED_RGB = (218, 218, 218)
CLAIM_DISABLED_TOLERANCE = 15
def _claim_disabled(driver):
r, g, b = driver.color_at(*CLAIM_PROBE)
tr, tg, tb = CLAIM_DISABLED_RGB
return (
abs(r - tr) <= CLAIM_DISABLED_TOLERANCE
and abs(g - tg) <= CLAIM_DISABLED_TOLERANCE
and abs(b - tb) <= CLAIM_DISABLED_TOLERANCE
)
def _enter_room(driver, coords):
for attempt in range(1, ROOM_OPEN_RETRIES + 1):
driver.click(*coords)
driver.wait(3)
if navigation.is_on_subscreen(driver):
# dismiss the "visited student list" notice shown on room entry
driver.keypress("Return")
driver.wait(1)
return True
print(f"[cafe] room not detected after click (attempt {attempt}/{ROOM_OPEN_RETRIES})")
return False
def _pat_room(driver, config):
# Sparkles appear on a per-student cooldown, so most single checks find
# nothing -- the old Bash loop (and an earlier version of this one) gave
# up on the very first miss, which meant it essentially never farmed.
# Keep polling for the full budget instead of bailing early.
patted = 0
for _ in range(config.CAFE_MAX_CLICKS_PER_ROOM):
match = detector.find_cafe_sparkle()
if match is None:
driver.wait(1)
continue
x, y, score = match
driver.click(x, y)
driver.wait(1)
driver.keypress("Return")
# park the cursor away from the sparkle area so it can't occlude the
# next detection screenshot (see screenshots/cafe/sparkle/02_*_cursor_on_head.png)
driver.move_mouse(10, 1190)
patted += 1
print(f"[cafe] patted sparkle at ({x}, {y}), score={score:.3f}")
print(f"[cafe] patted {patted} sparkle(s)" if patted else "[cafe] no sparkle found")
def _claim_income(driver, config):
driver.click(*config.CAFE_INCOME)
driver.wait(2)
if not navigation.is_modal_open(driver):
print("[cafe] income panel not detected, skipping claim")
return
if _claim_disabled(driver):
print("[cafe] nothing to claim")
else:
print("[cafe] claiming income")
driver.keypress("Return")
driver.wait(2)
driver.keypress("Return")
driver.wait(2)
if navigation.is_modal_open(driver):
driver.keypress("Escape")
driver.wait(1.5)
def run(driver, config):
driver.focus_game()
if not _enter_room(driver, config.CAFE_ICON):
print("[cafe] could not confirm cafe is open, aborting without pressing further keys")
return
print("[cafe] room 1: farming affection")
_pat_room(driver, config)
if not _enter_room(driver, config.CAFE_ROOM_SWITCH):
print("[cafe] could not confirm room switch, stopping before income claim")
if navigation.is_on_subscreen(driver):
driver.keypress("Escape")
driver.wait(1.5)
return
print("[cafe] room 2: farming affection")
_pat_room(driver, config)
_claim_income(driver, config)
if navigation.is_on_subscreen(driver):
driver.keypress("Escape")
driver.wait(1.5)
print("[cafe] Done.")