- 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.
51 lines
1.9 KiB
Python
51 lines
1.9 KiB
Python
"""Image/color matching helpers (OpenCV-based). Ported from scripts/detect_and_click.py."""
|
|
import os
|
|
|
|
import cv2
|
|
import numpy as np
|
|
|
|
from ba_auto import config, driver
|
|
|
|
SPARKLE_SHOT_PATH = os.path.join(config.SCRATCHPAD_DIR, "cafe_live.png")
|
|
SPARKLE_CLICK_OFFSET = (75, 47)
|
|
SPARKLE_THRESHOLD = 0.97
|
|
|
|
# The cafe camera's zoom level isn't reset before farming, and the sparkle
|
|
# icon's on-screen size scales with it (see screenshots/cafe/sparkle/
|
|
# 01_sparke_zoomed_centered.png vs 03_sparkle_zoomed_out.png) -- a single
|
|
# fixed-scale template match misses whenever the camera isn't at the exact
|
|
# zoom the template was captured at. Try a spread of scales instead.
|
|
SPARKLE_SCALES = (0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2)
|
|
|
|
|
|
def _masked_template(template):
|
|
b, g, r = cv2.split(template.astype(np.int16))
|
|
yellow_white = ((r > 180) & (g > 140) & (r - b > 60)) | ((r > 200) & (g > 200) & (b > 200))
|
|
mask_plane = (yellow_white.astype(np.uint8)) * 255
|
|
return cv2.merge([mask_plane, mask_plane, mask_plane])
|
|
|
|
|
|
def find_cafe_sparkle():
|
|
driver.screenshot(SPARKLE_SHOT_PATH)
|
|
template_full = cv2.imread(config.CAFE_SPARKLE_TEMPLATE)
|
|
img = cv2.imread(SPARKLE_SHOT_PATH)
|
|
th0, tw0 = template_full.shape[:2]
|
|
|
|
best = None
|
|
for scale in SPARKLE_SCALES:
|
|
tw, th = max(1, round(tw0 * scale)), max(1, round(th0 * scale))
|
|
template = cv2.resize(template_full, (tw, th))
|
|
mask = _masked_template(template)
|
|
|
|
result = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED, mask=mask)
|
|
_, max_val, _, max_loc = cv2.minMaxLoc(result)
|
|
if max_val >= SPARKLE_THRESHOLD and (best is None or max_val > best[0]):
|
|
best = (max_val, max_loc[0], max_loc[1], tw, th, scale)
|
|
|
|
if best is None:
|
|
return None
|
|
|
|
score, x, y, tw, th, scale = best
|
|
ox, oy = SPARKLE_CLICK_OFFSET
|
|
return (x + tw // 2 + round(ox * scale), y + th // 2 + round(oy * scale), score)
|