Skip to content

Commit d9a33ee

Browse files
Upd: removed imwrite+ own file
1 parent 0048c51 commit d9a33ee

6 files changed

Lines changed: 219 additions & 167 deletions

File tree

assets/en/retire/DOCK_AMOUNT.png

964 Bytes
Loading
1.64 KB
Loading

module/combat/auto_search_combat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from module.handler.assets import AUTO_SEARCH_MAP_OPTION_ON
77
from module.logger import logger
88
from module.map.map_operation import MapOperation
9-
from module.statistics.campaign_bonus import CampaignBonusStatistics
9+
from module.statistics.autosearch_reward import *
1010

1111

12-
class AutoSearchCombat(MapOperation, Combat, CampaignStatus, CampaignBonusStatistics):
12+
class AutoSearchCombat(MapOperation, Combat, CampaignStatus, AutosearchReward):
1313
_auto_search_in_stage_timer = Timer(3, count=6)
1414
_auto_search_status_confirm = False
1515
auto_search_oil_limit_triggered = False

module/retire/assets.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
COMMON_SHIP_FILTER_DISABLE = Button(area={'cn': (714, 13, 757, 41), 'en': (706, 15, 758, 41), 'jp': (711, 13, 759, 42), 'tw': (712, 12, 759, 42)}, color={'cn': (127, 143, 183), 'en': (86, 99, 138), 'jp': (119, 131, 170), 'tw': (117, 132, 173)}, button={'cn': (714, 13, 757, 41), 'en': (706, 15, 758, 41), 'jp': (711, 13, 759, 42), 'tw': (712, 12, 759, 42)}, file={'cn': './assets/cn/retire/COMMON_SHIP_FILTER_DISABLE.png', 'en': './assets/en/retire/COMMON_SHIP_FILTER_DISABLE.png', 'jp': './assets/jp/retire/COMMON_SHIP_FILTER_DISABLE.png', 'tw': './assets/tw/retire/COMMON_SHIP_FILTER_DISABLE.png'})
88
COMMON_SHIP_FILTER_ENABLE = Button(area={'cn': (713, 12, 758, 41), 'en': (707, 16, 758, 40), 'jp': (712, 13, 758, 42), 'tw': (712, 11, 759, 42)}, color={'cn': (214, 174, 130), 'en': (193, 149, 106), 'jp': (212, 171, 129), 'tw': (211, 170, 127)}, button={'cn': (713, 12, 758, 41), 'en': (707, 16, 758, 40), 'jp': (712, 13, 758, 42), 'tw': (712, 11, 759, 42)}, file={'cn': './assets/cn/retire/COMMON_SHIP_FILTER_ENABLE.png', 'en': './assets/en/retire/COMMON_SHIP_FILTER_ENABLE.png', 'jp': './assets/jp/retire/COMMON_SHIP_FILTER_ENABLE.png', 'tw': './assets/tw/retire/COMMON_SHIP_FILTER_ENABLE.png'})
9-
DOCK_AMOUNT = Button(area={'cn': (473, 17, 611, 41), 'en': (513, 15, 588, 42), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, color={'cn': (71, 74, 78), 'en': (44, 49, 54), 'jp': (46, 53, 61), 'tw': (71, 74, 78)}, button={'cn': (473, 17, 611, 41), 'en': (513, 15, 588, 42), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, file={'cn': './assets/cn/retire/DOCK_AMOUNT.png', 'en': './assets/en/retire/DOCK_AMOUNT.png', 'jp': './assets/jp/retire/DOCK_AMOUNT.png', 'tw': './assets/tw/retire/DOCK_AMOUNT.png'})
9+
DOCK_AMOUNT = Button(area={'cn': (473, 17, 611, 41), 'en': (476, 9, 611, 44), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, color={'cn': (71, 74, 78), 'en': (50, 54, 58), 'jp': (46, 53, 61), 'tw': (71, 74, 78)}, button={'cn': (473, 17, 611, 41), 'en': (476, 9, 611, 44), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, file={'cn': './assets/cn/retire/DOCK_AMOUNT.png', 'en': './assets/en/retire/DOCK_AMOUNT.png', 'jp': './assets/jp/retire/DOCK_AMOUNT.png', 'tw': './assets/tw/retire/DOCK_AMOUNT.png'})
1010
DOCK_CHECK = Button(area={'cn': (121, 14, 175, 39), 'en': (121, 17, 189, 39), 'jp': (232, 12, 308, 41), 'tw': (121, 13, 176, 39)}, color={'cn': (156, 171, 207), 'en': (112, 125, 162), 'jp': (88, 96, 128), 'tw': (149, 164, 200)}, button={'cn': (121, 14, 175, 39), 'en': (121, 17, 189, 39), 'jp': (232, 12, 308, 41), 'tw': (121, 13, 176, 39)}, file={'cn': './assets/cn/retire/DOCK_CHECK.png', 'en': './assets/en/retire/DOCK_CHECK.png', 'jp': './assets/jp/retire/DOCK_CHECK.png', 'tw': './assets/tw/retire/DOCK_CHECK.png'})
1111
DOCK_EMPTY = Button(area={'cn': (95, 347, 388, 378), 'en': (95, 318, 264, 339), 'jp': (96, 347, 252, 376), 'tw': (94, 347, 390, 379)}, color={'cn': (160, 154, 159), 'en': (106, 99, 106), 'jp': (159, 152, 156), 'tw': (163, 157, 162)}, button={'cn': (95, 347, 388, 378), 'en': (95, 318, 264, 339), 'jp': (96, 347, 252, 376), 'tw': (94, 347, 390, 379)}, file={'cn': './assets/cn/retire/DOCK_EMPTY.png', 'en': './assets/en/retire/DOCK_EMPTY.png', 'jp': './assets/jp/retire/DOCK_EMPTY.png', 'tw': './assets/tw/retire/DOCK_EMPTY.png'})
1212
DOCK_FILTER = Button(area={'cn': (1099, 5, 1193, 48), 'en': (1098, 4, 1194, 49), 'jp': (1101, 6, 1192, 46), 'tw': (1099, 6, 1193, 47)}, color={'cn': (70, 87, 127), 'en': (73, 90, 128), 'jp': (67, 84, 125), 'tw': (78, 96, 137)}, button={'cn': (1099, 5, 1193, 48), 'en': (1098, 4, 1194, 49), 'jp': (1101, 6, 1192, 46), 'tw': (1099, 6, 1193, 47)}, file={'cn': './assets/cn/retire/DOCK_FILTER.png', 'en': './assets/en/retire/DOCK_FILTER.png', 'jp': './assets/jp/retire/DOCK_FILTER.png', 'tw': './assets/tw/retire/DOCK_FILTER.png'})
1313
DOCK_FILTER_CONFIRM = Button(area={'cn': (714, 613, 886, 671), 'en': (718, 618, 883, 666), 'jp': (717, 618, 885, 668), 'tw': (715, 630, 884, 680)}, color={'cn': (86, 133, 192), 'en': (108, 148, 201), 'jp': (83, 128, 188), 'tw': (83, 130, 190)}, button={'cn': (714, 613, 886, 671), 'en': (718, 618, 883, 666), 'jp': (717, 618, 885, 668), 'tw': (715, 630, 884, 680)}, file={'cn': './assets/cn/retire/DOCK_FILTER_CONFIRM.png', 'en': './assets/en/retire/DOCK_FILTER_CONFIRM.png', 'jp': './assets/jp/retire/DOCK_FILTER_CONFIRM.png', 'tw': './assets/tw/retire/DOCK_FILTER_CONFIRM.png'})
1414
DOCK_FIRST_NPC = Button(area={'cn': (96, 111, 123, 123), 'en': (96, 111, 123, 123), 'jp': (96, 111, 123, 123), 'tw': (96, 111, 123, 123)}, color={'cn': (184, 150, 150), 'en': (184, 150, 150), 'jp': (184, 150, 150), 'tw': (184, 150, 150)}, button={'cn': (96, 111, 123, 123), 'en': (96, 111, 123, 123), 'jp': (96, 111, 123, 123), 'tw': (96, 111, 123, 123)}, file={'cn': './assets/cn/retire/DOCK_FIRST_NPC.png', 'en': './assets/cn/retire/DOCK_FIRST_NPC.png', 'jp': './assets/cn/retire/DOCK_FIRST_NPC.png', 'tw': './assets/cn/retire/DOCK_FIRST_NPC.png'})
1515
DOCK_SCROLL = Button(area={'cn': (1239, 76, 1248, 641), 'en': (1239, 76, 1248, 641), 'jp': (1237, 78, 1250, 628), 'tw': (1239, 76, 1248, 641)}, color={'cn': (47, 46, 37), 'en': (47, 46, 37), 'jp': (180, 156, 66), 'tw': (47, 46, 37)}, button={'cn': (1239, 76, 1248, 641), 'en': (1239, 76, 1248, 641), 'jp': (1237, 78, 1250, 628), 'tw': (1239, 76, 1248, 641)}, file={'cn': './assets/cn/retire/DOCK_SCROLL.png', 'en': './assets/en/retire/DOCK_SCROLL.png', 'jp': './assets/jp/retire/DOCK_SCROLL.png', 'tw': './assets/tw/retire/DOCK_SCROLL.png'})
1616
DOCK_SELECTED = Button(area={'cn': (582, 662, 647, 685), 'en': (597, 658, 655, 691), 'jp': (603, 662, 655, 685), 'tw': (582, 662, 647, 685)}, color={'cn': (75, 75, 83), 'en': (64, 62, 73), 'jp': (84, 83, 92), 'tw': (75, 75, 83)}, button={'cn': (582, 662, 647, 685), 'en': (597, 658, 655, 691), 'jp': (603, 662, 655, 685), 'tw': (582, 662, 647, 685)}, file={'cn': './assets/cn/retire/DOCK_SELECTED.png', 'en': './assets/en/retire/DOCK_SELECTED.png', 'jp': './assets/jp/retire/DOCK_SELECTED.png', 'tw': './assets/tw/retire/DOCK_SELECTED.png'})
17-
EMPTY_ENHANCE_SLOT_PLUS = Button(area={'cn': (737, 402, 773, 437), 'en': (747, 402, 782, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, color={'cn': (46, 46, 46), 'en': (38, 38, 38), 'jp': (46, 46, 46), 'tw': (46, 46, 46)}, button={'cn': (737, 402, 773, 437), 'en': (747, 402, 782, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, file={'cn': './assets/cn/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'en': './assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'jp': './assets/jp/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'tw': './assets/tw/retire/EMPTY_ENHANCE_SLOT_PLUS.png'})
17+
EMPTY_ENHANCE_SLOT_PLUS = Button(area={'cn': (737, 402, 773, 437), 'en': (737, 402, 773, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, color={'cn': (46, 46, 46), 'en': (46, 46, 46), 'jp': (46, 46, 46), 'tw': (46, 46, 46)}, button={'cn': (737, 402, 773, 437), 'en': (737, 402, 773, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, file={'cn': './assets/cn/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'en': './assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'jp': './assets/jp/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'tw': './assets/tw/retire/EMPTY_ENHANCE_SLOT_PLUS.png'})
1818
ENHANCE_CONFIRM = Button(area={'cn': (1126, 602, 1256, 645), 'en': (1226, 605, 1262, 641), 'jp': (1126, 601, 1257, 646), 'tw': (1215, 606, 1253, 641)}, color={'cn': (203, 149, 81), 'en': (212, 167, 100), 'jp': (189, 139, 78), 'tw': (216, 169, 99)}, button={'cn': (1126, 602, 1256, 645), 'en': (1226, 605, 1262, 641), 'jp': (1126, 601, 1257, 646), 'tw': (1215, 606, 1253, 641)}, file={'cn': './assets/cn/retire/ENHANCE_CONFIRM.png', 'en': './assets/en/retire/ENHANCE_CONFIRM.png', 'jp': './assets/jp/retire/ENHANCE_CONFIRM.png', 'tw': './assets/tw/retire/ENHANCE_CONFIRM.png'})
1919
ENHANCE_FILLED = Button(area={'cn': (728, 440, 781, 454), 'en': (728, 440, 781, 454), 'jp': (722, 387, 789, 400), 'tw': (728, 440, 781, 454)}, color={'cn': (156, 138, 127), 'en': (156, 138, 127), 'jp': (146, 153, 211), 'tw': (156, 138, 127)}, button={'cn': (728, 440, 781, 454), 'en': (728, 440, 781, 454), 'jp': (722, 387, 789, 400), 'tw': (728, 440, 781, 454)}, file={'cn': './assets/cn/retire/ENHANCE_FILLED.png', 'en': './assets/en/retire/ENHANCE_FILLED.png', 'jp': './assets/jp/retire/ENHANCE_FILLED.png', 'tw': './assets/tw/retire/ENHANCE_FILLED.png'})
2020
ENHANCE_RECOMMEND = Button(area={'cn': (959, 602, 1089, 645), 'en': (1057, 606, 1091, 640), 'jp': (965, 605, 1085, 641), 'tw': (1050, 607, 1082, 639)}, color={'cn': (87, 134, 194), 'en': (106, 151, 215), 'jp': (86, 136, 201), 'tw': (106, 151, 212)}, button={'cn': (959, 602, 1089, 645), 'en': (1057, 606, 1091, 640), 'jp': (965, 605, 1085, 641), 'tw': (1050, 607, 1082, 639)}, file={'cn': './assets/cn/retire/ENHANCE_RECOMMEND.png', 'en': './assets/en/retire/ENHANCE_RECOMMEND.png', 'jp': './assets/jp/retire/ENHANCE_RECOMMEND.png', 'tw': './assets/tw/retire/ENHANCE_RECOMMEND.png'})
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import time
2+
import cv2
3+
import numpy as np
4+
from module.base.button import ButtonGrid
5+
from module.base.timer import Timer
6+
from module.logger import logger
7+
from module.base.utils import crop
8+
from module.handler.assets import AUTO_SEARCH_MENU_EXIT, AUTO_SEARCH_MENU_CONTINUE
9+
from module.statistics.item import AmountOcr
10+
from module.statistics.azurstats import AzurStats
11+
12+
class AutosearchReward(ButtonGrid, Timer, AzurStats):
13+
def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, similarity_threshold=0.999):
14+
"""
15+
checks if all rewards have appeared, via first doing a predicted grid, 3 types to account for the Ui
16+
change due to Meta xp, then verifies via amount ocr, after that waits till the reward area has no changes
17+
e.g all drops appeared
18+
"""
19+
logger.info('wait until area stable')
20+
21+
try:
22+
# 1. Image handling separation, ocr uses non modified, area stable check uses modified
23+
self.device.screenshot()
24+
original_bgr = self.device.image.copy()
25+
gray_image = cv2.cvtColor(original_bgr, cv2.COLOR_BGR2GRAY)
26+
27+
# 2. Debugging/Reference visualization (BGR)
28+
exit_btn = AUTO_SEARCH_MENU_EXIT._button
29+
continue_btn = AUTO_SEARCH_MENU_CONTINUE._button
30+
debug_img = original_bgr.copy()
31+
cv2.rectangle(debug_img, (exit_btn[0], exit_btn[1]), (exit_btn[2], exit_btn[3]), (255,0,0), 2)
32+
cv2.rectangle(debug_img, (continue_btn[0], continue_btn[1]), (continue_btn[2], continue_btn[3]), (0,0,255), 2)
33+
self._save(debug_img, 'debug', 'reference_points.png')
34+
35+
# 3. Grid setup with single row, tested with EN, CN is fine due to same button,
36+
#continue button shift less of an issue, but alot of the measures depend on exit
37+
#TW might have grid too low and ocr area might miss the numbers area
38+
#compared to EN/CN: exit width -42, height -9, continue width -3, height -5
39+
#JP gonna have issues due to not using a default exit asset, ui moved due to meta xp sidebar
40+
#compared to EN/CN: exit width -1, height -1, but further on the left by 68px
41+
#continue width -1, height the same, but further on the right side by 72px
42+
GRID_PARAMS = {
43+
'button_shape': (64, 64),
44+
'grid_shape': (7, 1), # need to only check first row
45+
'delta': (72 + 2/3, 0),
46+
'name': 'REWARD_GRID'
47+
}
48+
exit_x, exit_y = exit_btn[0], exit_btn[1]
49+
GRID_ORIGINS = [
50+
(exit_x + 22, exit_y - 417), # Grid 0 - Primary
51+
(exit_x + 22 - 72, exit_y - 417), # Grid 1 - Fallback 1 (one button left)
52+
(exit_x + 22 - 129, exit_y - 417) # Grid 2 - Fallback 2
53+
]
54+
55+
# 4. Enhanced grid verification logic
56+
validated_grids = []
57+
58+
for i, (grid_x, grid_y) in enumerate(GRID_ORIGINS):
59+
grid = ButtonGrid(origin=(grid_x, grid_y), **GRID_PARAMS)
60+
matched = self._verify_grid_via_ocr(grid, i, original_bgr)
61+
62+
if matched:
63+
logger.info(f"Grid {i} validated")
64+
validated_grids.append((i, grid_x, grid_y))
65+
66+
# Special case: If Grid 0 validates, also check Grid 1
67+
if i == 0 and len(GRID_ORIGINS) > 1:
68+
logger.info("Grid 0 validated, checking Grid 1 for potentially more rewards...")
69+
grid1_x, grid1_y = GRID_ORIGINS[1]
70+
grid1 = ButtonGrid(origin=(grid1_x, grid1_y), **GRID_PARAMS)
71+
grid1_matched = self._verify_grid_via_ocr(grid1, 1, original_bgr)
72+
73+
if grid1_matched:
74+
logger.info("Grid 1 also validated - using Grid 1 for wider coverage")
75+
validated_grids.append((1, grid1_x, grid1_y))
76+
else:
77+
logger.info("Grid 1 failed validation - sticking with Grid 0")
78+
79+
break # Found at least one valid grid, proceed
80+
81+
# 5. Select the best grid (prefer Grid 1 over Grid 0 if both validate)
82+
if not validated_grids:
83+
logger.warning("All grid positions failed")
84+
return False
85+
86+
# Choose Grid 1 if available (better coverage), otherwise use the first validated grid
87+
best_grid = None
88+
for grid_info in validated_grids:
89+
grid_idx, grid_x, grid_y = grid_info
90+
if grid_idx == 1: # Prefer Grid 1
91+
best_grid = grid_info
92+
break
93+
94+
if best_grid is None:
95+
best_grid = validated_grids[0] # Use first validated grid
96+
97+
selected_idx, selected_x, selected_y = best_grid
98+
logger.info(f"Using Grid {selected_idx} for monitoring (x={selected_x}, y={selected_y})")
99+
100+
# 6. Set up monitoring area with selected grid
101+
continue_btn = AUTO_SEARCH_MENU_CONTINUE._button
102+
monitor_area = (
103+
int(selected_x),
104+
int(selected_y),
105+
int(selected_x + (7 * 72.67)),
106+
int(continue_btn[1])
107+
)
108+
109+
logger.info(f"DEBUG - Selected Grid {selected_idx}: x={selected_x}, y={selected_y}")
110+
logger.info(f"DEBUG - Monitor area: {monitor_area}")
111+
112+
self.device.image = gray_image
113+
return self.reward_area_stable_check(monitor_area, timeout, required_consecutive_matches, similarity_threshold)
114+
115+
except Exception as e:
116+
logger.error(f'Stabilization failure: {str(e)}')
117+
return False
118+
119+
def _verify_grid_via_ocr(self, grid, grid_idx, bgr_image):
120+
"""
121+
Double checks if grid correct via the item amount on the bottom right of grid buttons
122+
Enhanced with better logging for grid comparison
123+
"""
124+
amount_ocr = AmountOcr([], threshold=96, name='Amount_ocr')
125+
detected_amounts = []
126+
127+
for btn_idx, btn in enumerate(grid.buttons):
128+
try:
129+
# 1. Get button image (keep as BGR)
130+
button_img = crop(bgr_image, btn.area)
131+
if button_img is None or button_img.size == 0:
132+
continue
133+
134+
# 2. Extract amount area (bottom-right portion)
135+
h, w = button_img.shape[:2]
136+
amount_area = (int(w*0.6), int(h*0.8), w, h)
137+
amount_img = crop(button_img, amount_area)
138+
139+
# 3. Ensure BGR format for OCR
140+
if len(amount_img.shape) == 2:
141+
amount_img = cv2.cvtColor(amount_img, cv2.COLOR_GRAY2BGR)
142+
143+
# 4. OCR detection
144+
amount = amount_ocr.ocr([amount_img], direct_ocr=True)[0]
145+
if amount > 0:
146+
detected_amounts.append(amount)
147+
logger.info(f"Grid {grid_idx}, Button {btn_idx}: amount={amount}")
148+
149+
except Exception as e:
150+
logger.warning(f"Grid {grid_idx}, Button {btn_idx} check failed: {str(e)}")
151+
continue
152+
153+
# Log summary for this grid
154+
total_detected = len(detected_amounts)
155+
if total_detected > 0:
156+
logger.info(f"Grid {grid_idx} summary: {total_detected} buttons with amounts detected")
157+
return True
158+
else:
159+
logger.info(f"Grid {grid_idx} summary: No amounts detected")
160+
return False
161+
162+
def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold):
163+
"""
164+
checks if the previously assigned area has any changes
165+
returns True for being stable after 3 same consecutive matches, while similarity was met or beaten
166+
"""
167+
start_time = time.time()
168+
last_check = start_time
169+
stabilization_start = None
170+
consecutive_matches = 0
171+
check_count = 0
172+
173+
last_luma = crop(self.device.image, monitor_area)
174+
self._save(last_luma, 'debug/dropstats', '0_initial.png')
175+
176+
while True:
177+
current_time = time.time()
178+
elapsed = current_time - start_time
179+
180+
if elapsed >= timeout:
181+
logger.warning(f'Timeout after {elapsed:.2f}s (never stabilized)')
182+
return False
183+
184+
if current_time - last_check >= 0.2:
185+
check_count += 1
186+
last_check = current_time
187+
188+
self.device.screenshot()
189+
current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY)
190+
current_luma = crop(current_luma, monitor_area)
191+
self._save(current_luma, 'debug/dropstats', f'{check_count}_check.png')
192+
193+
res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED)
194+
similarity = cv2.minMaxLoc(res)[1]
195+
196+
if similarity >= similarity_threshold:
197+
consecutive_matches += 1
198+
if stabilization_start is None:
199+
stabilization_start = time.time()
200+
201+
if consecutive_matches >= required_matches:
202+
stabilization_time = time.time() - stabilization_start
203+
total_time = time.time() - start_time
204+
logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)')
205+
self._save(current_luma, 'debug/dropstats', 'final_stable.png')
206+
return True
207+
else:
208+
consecutive_matches = 0
209+
stabilization_start = None
210+
211+
last_luma = current_luma

0 commit comments

Comments
 (0)