Skip to content

Commit 2e5e618

Browse files
committed
Add: island technology scanner (without main line)
1 parent f84c77f commit 2e5e618

9 files changed

Lines changed: 177 additions & 0 deletions

File tree

22.6 KB
Loading
10.4 KB
Loading
11.9 KB
Loading
9.62 KB
Loading
18.2 KB
Loading
10.5 KB
Loading
9.55 KB
Loading

module/island_handler/assets.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
ISLAND_DOCK_SORTING_CLICK = Button(area={'cn': (115, 608, 266, 641), 'en': (115, 608, 266, 641), 'jp': (115, 608, 266, 641), 'tw': (115, 608, 266, 641)}, color={'cn': (104, 104, 103), 'en': (104, 104, 103), 'jp': (104, 104, 103), 'tw': (104, 104, 103)}, button={'cn': (115, 608, 266, 641), 'en': (115, 608, 266, 641), 'jp': (115, 608, 266, 641), 'tw': (115, 608, 266, 641)}, file={'cn': './assets/cn/island_handler/ISLAND_DOCK_SORTING_CLICK.png', 'en': './assets/cn/island_handler/ISLAND_DOCK_SORTING_CLICK.png', 'jp': './assets/cn/island_handler/ISLAND_DOCK_SORTING_CLICK.png', 'tw': './assets/cn/island_handler/ISLAND_DOCK_SORTING_CLICK.png'})
1010
ISLAND_DOCK_SORT_ASC = Button(area={'cn': (151, 619, 154, 622), 'en': (151, 619, 154, 622), 'jp': (151, 619, 154, 622), 'tw': (151, 619, 154, 622)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (151, 619, 154, 622), 'en': (151, 619, 154, 622), 'jp': (151, 619, 154, 622), 'tw': (151, 619, 154, 622)}, file={'cn': './assets/cn/island_handler/ISLAND_DOCK_SORT_ASC.png', 'en': './assets/cn/island_handler/ISLAND_DOCK_SORT_ASC.png', 'jp': './assets/cn/island_handler/ISLAND_DOCK_SORT_ASC.png', 'tw': './assets/cn/island_handler/ISLAND_DOCK_SORT_ASC.png'})
1111
ISLAND_DOCK_SORT_DESC = Button(area={'cn': (151, 627, 154, 630), 'en': (151, 627, 154, 630), 'jp': (151, 627, 154, 630), 'tw': (151, 627, 154, 630)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (151, 627, 154, 630), 'en': (151, 627, 154, 630), 'jp': (151, 627, 154, 630), 'tw': (151, 627, 154, 630)}, file={'cn': './assets/cn/island_handler/ISLAND_DOCK_SORT_DESC.png', 'en': './assets/cn/island_handler/ISLAND_DOCK_SORT_DESC.png', 'jp': './assets/cn/island_handler/ISLAND_DOCK_SORT_DESC.png', 'tw': './assets/cn/island_handler/ISLAND_DOCK_SORT_DESC.png'})
12+
ISLAND_TECHNOLOGY_TAB1 = Button(area={'cn': (20, 575, 109, 644), 'en': (20, 575, 109, 644), 'jp': (20, 575, 109, 644), 'tw': (20, 575, 109, 644)}, color={'cn': (76, 109, 130), 'en': (76, 109, 130), 'jp': (76, 109, 130), 'tw': (76, 109, 130)}, button={'cn': (20, 575, 109, 644), 'en': (20, 575, 109, 644), 'jp': (20, 575, 109, 644), 'tw': (20, 575, 109, 644)}, file={'cn': './assets/cn/island_handler/ISLAND_TECHNOLOGY_TAB1.png', 'en': './assets/cn/island_handler/ISLAND_TECHNOLOGY_TAB1.png', 'jp': './assets/cn/island_handler/ISLAND_TECHNOLOGY_TAB1.png', 'tw': './assets/cn/island_handler/ISLAND_TECHNOLOGY_TAB1.png'})
1213
TEMPLATE_ISLAND_DOCK_OCCUPIED = Template(file={'cn': './assets/cn/island_handler/TEMPLATE_ISLAND_DOCK_OCCUPIED.png', 'en': './assets/cn/island_handler/TEMPLATE_ISLAND_DOCK_OCCUPIED.png', 'jp': './assets/jp/island_handler/TEMPLATE_ISLAND_DOCK_OCCUPIED.png', 'tw': './assets/cn/island_handler/TEMPLATE_ISLAND_DOCK_OCCUPIED.png'})
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import cv2
2+
import numpy as np
3+
from yaml import safe_dump
4+
5+
from module.base.button import ButtonGrid
6+
from module.base.decorator import cached_property
7+
from module.base.mask import Mask
8+
from module.base.utils import color_similarity_2d, rgb2luma, load_image, random_rectangle_vector, area_offset, crop
9+
from module.island_handler.assets import *
10+
from module.island.data import DIC_ISLAND_TECHNOLOGY
11+
from module.island.ui import IslandUI
12+
from module.ui.navbar import Navbar
13+
from module.ui.page import page_island_technology
14+
15+
DELTA_X = 136 + 2/3
16+
DELTA_Y = 60
17+
ORIGIN_X = -5/3
18+
ORIGIN_Y = 46
19+
LEFT_STRIP = 167
20+
MASK_ISLAND_TECHNOLOGY = Mask('./assets/mask/MASK_ISLAND_TECHNOLOGY.png')
21+
TECHNOLOGY_LENGTH = {
22+
'2': 3139 - 1280 + LEFT_STRIP,
23+
'3': 4231 - 1280 + LEFT_STRIP,
24+
'4': 3003 - 1280 + LEFT_STRIP,
25+
'5': 5462 - 1280 + LEFT_STRIP,
26+
'6': 4233 - 1280 + LEFT_STRIP,
27+
}
28+
DETECTION_AREA = (167, 54, 1280, 720)
29+
DETECTION_AREA_MASK = (1098, 646, 1280, 720)
30+
BUTTON_AREA = (-110, -26, 110, 26)
31+
32+
33+
def extract_flowchart(image):
34+
brightness = rgb2luma(image)
35+
black = color_similarity_2d(image, (7, 10, 17))
36+
brightness_mask = cv2.inRange(brightness, 160, 255)
37+
black_mask = cv2.inRange(black, 245, 255)
38+
mask = cv2.bitwise_or(brightness_mask, black_mask)
39+
contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
40+
filled_mask = np.zeros_like(mask)
41+
cv2.drawContours(filled_mask, contours, -1, 255, thickness=cv2.FILLED)
42+
filled_mask = MASK_ISLAND_TECHNOLOGY.apply(filled_mask)
43+
filled_mask = filled_mask[:, LEFT_STRIP:]
44+
return filled_mask
45+
46+
def get_technology_tab_and_position(index):
47+
tech_info = DIC_ISLAND_TECHNOLOGY[index]
48+
tab = tech_info['tech_belong']
49+
axis_x, axis_y = tech_info['axis']
50+
position_x = ORIGIN_X + DELTA_X * axis_x
51+
position_y = ORIGIN_Y + DELTA_Y * axis_y
52+
return tab, (position_x, position_y)
53+
54+
55+
class IslandTechnologyScanner(IslandUI):
56+
"""
57+
Currently only supports checking tab 2,3,4,5,6.
58+
"""
59+
@cached_property
60+
def _island_technology_side_navbar(self):
61+
island_technology_side_navbar = ButtonGrid(
62+
origin=(13, 107), delta=(0, 196/3),
63+
button_shape=(128, 43), grid_shape=(1, 5)
64+
)
65+
return Navbar(grids=island_technology_side_navbar,
66+
active_color=(30, 143, 255),
67+
inactive_color=(50, 52, 55),
68+
active_count=500,
69+
inactive_count=500)
70+
71+
def _island_technology_side_navbar_get_active(self):
72+
active, _, _ = self._island_technology_side_navbar.get_info(main=self)
73+
if active is None:
74+
return 1
75+
return active + 2
76+
77+
def island_technology_side_navbar_ensure(self, tab=1, skip_first_screenshot=True):
78+
"""
79+
Tab 2, 3, 4, 5, 6 corresponds to _island_technology_side_navbar 1, 2, 3, 4, 5
80+
Tab 1 is a special situation where the botton icon is chosen,
81+
and all the navbar icons are inactive.
82+
"""
83+
for _ in self.loop(skip_first=skip_first_screenshot):
84+
active = self._island_technology_side_navbar_get_active()
85+
if active == tab:
86+
return True
87+
if tab == 1:
88+
self.device.click(ISLAND_TECHNOLOGY_TAB1)
89+
continue
90+
else:
91+
if active == 1:
92+
self.device.click(self._island_technology_side_navbar.grids.buttons[tab-2])
93+
continue
94+
else:
95+
self._island_technology_side_navbar.set(self, upper=tab-1)
96+
return True
97+
return False
98+
99+
def get_technology_view_position(self, tab):
100+
globe_view = load_image(f'./assets/island/technology/technology_chart_{tab}.png')
101+
extracted_flowchart = extract_flowchart(self.device.image)
102+
result = cv2.matchTemplate(globe_view, extracted_flowchart, cv2.TM_CCOEFF_NORMED)
103+
_, similarity, _, loca = cv2.minMaxLoc(result)
104+
# print(similarity)
105+
return loca[0]
106+
107+
def _island_technology_swipe(self, forward=True):
108+
detection_area = DETECTION_AREA
109+
direction_vector = (-600, 0) if forward else (600, 0)
110+
p1, p2 = random_rectangle_vector(
111+
direction_vector, box=detection_area, random_range=(-50, -50, 50, 50), padding=20
112+
)
113+
self.device.drag(p1, p2, segments=2, shake=(0, 25), point_random=(0, 0, 0, 0), shake_random=(0, -5, 0, 5))
114+
115+
def technology_reset_view(self, skip_first_screenshot=True):
116+
active = self._island_technology_side_navbar_get_active()
117+
for _ in range(10): # tab 5 has 4400 length, so 5 swipes are not enough
118+
if skip_first_screenshot:
119+
skip_first_screenshot = False
120+
else:
121+
self.device.screenshot()
122+
position_x = self.get_technology_view_position(tab=active)
123+
if position_x < 3:
124+
break
125+
self._island_technology_swipe(forward=False)
126+
self.device.click_record_remove('DRAG')
127+
128+
def scan_all(self):
129+
all_technology = {}
130+
for index in DIC_ISLAND_TECHNOLOGY.keys():
131+
if DIC_ISLAND_TECHNOLOGY[index]['tech_belong'] not in [2, 3, 4, 5, 6]:
132+
continue
133+
tab, position = get_technology_tab_and_position(index)
134+
all_technology[index] = {
135+
'tab': tab,
136+
'position': position,
137+
'active': False,
138+
}
139+
technology_by_tab = [{} for _ in range(5)]
140+
for index, info in all_technology.items():
141+
technology_by_tab[info['tab'] - 2][index] = info['position']
142+
for tab in [2, 3, 4, 5, 6]:
143+
self.island_technology_side_navbar_ensure(tab=tab)
144+
self.technology_reset_view()
145+
position_x_old = None
146+
for _ in self.loop():
147+
position_x = self.get_technology_view_position(tab=tab)
148+
if position_x_old is not None:
149+
if position_x - position_x_old < 5:
150+
break
151+
position_x_old = position_x
152+
for index, (tech_pos_x, tech_pos_y) in technology_by_tab[tab - 2].items():
153+
tech_pos_x_in_view = tech_pos_x - position_x
154+
if (DETECTION_AREA[0] - BUTTON_AREA[0] <= LEFT_STRIP + tech_pos_x_in_view <= DETECTION_AREA[2] - BUTTON_AREA[2]
155+
and not (
156+
tech_pos_y > DETECTION_AREA_MASK[1] + BUTTON_AREA[1]
157+
and LEFT_STRIP + tech_pos_x_in_view >= DETECTION_AREA_MASK[0]
158+
)):
159+
tech_button = crop(self.device.image, area=area_offset(BUTTON_AREA, (LEFT_STRIP + tech_pos_x_in_view, tech_pos_y)))
160+
luma = rgb2luma(tech_button)
161+
color = np.mean(luma.flatten())
162+
if color > 160:
163+
all_technology[index]['active'] = True
164+
self._island_technology_swipe(forward=True)
165+
self.device.click_record_remove('DRAG')
166+
return {index: info['active'] for index, info in all_technology.items()}
167+
168+
def get_technology_status(self, dump_key=None):
169+
self.ui_ensure(page_island_technology)
170+
result = self.scan_all()
171+
if dump_key is not None:
172+
value = safe_dump(result)
173+
self.config.cross_set(keys=dump_key, value=value)
174+
return result
175+
176+

0 commit comments

Comments
 (0)