Add: Get obtain from synthesize page
BIN
assets/share/item/synthesize/ENTRY_ITEM_FROM.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/share/item/synthesize/ENTRY_ITEM_TO.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
assets/share/item/synthesize/ITEM_NAME.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/share/item/synthesize/SWITCH_RARITY.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/share/item/synthesize/SYNTHESIZE_AMOUNT.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/share/item/synthesize/SYNTHESIZE_MINUS.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/share/item/synthesize/SYNTHESIZE_PLUS.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
@ -30,10 +30,11 @@ class CombatObtain(PlannerMixin):
|
||||
# True to exit and reenter to get obtained items
|
||||
obtain_frequent_check = False
|
||||
|
||||
def _obtain_enter(self, entry, skip_first_screenshot=True):
|
||||
def _obtain_enter(self, entry, appear_button, skip_first_screenshot=True):
|
||||
"""
|
||||
Args:
|
||||
entry: Item entry
|
||||
appear_button:
|
||||
skip_first_screenshot:
|
||||
|
||||
Pages:
|
||||
@ -41,7 +42,7 @@ class CombatObtain(PlannerMixin):
|
||||
out: ITEM_CLOSE
|
||||
"""
|
||||
logger.info(f'Obtain enter {entry}')
|
||||
self.interval_clear(COMBAT_PREPARE)
|
||||
self.interval_clear(appear_button)
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
@ -50,9 +51,9 @@ class CombatObtain(PlannerMixin):
|
||||
|
||||
if self.appear(ITEM_CLOSE):
|
||||
break
|
||||
if self.appear(COMBAT_PREPARE, interval=2):
|
||||
if self.appear(appear_button, interval=2):
|
||||
self.device.click(entry)
|
||||
self.interval_reset(COMBAT_PREPARE)
|
||||
self.interval_reset(appear_button)
|
||||
continue
|
||||
|
||||
# Wait animation
|
||||
@ -70,9 +71,10 @@ class CombatObtain(PlannerMixin):
|
||||
logger.warning('Wait obtain item timeout')
|
||||
break
|
||||
|
||||
def _obtain_close(self, skip_first_screenshot=True):
|
||||
def _obtain_close(self, check_button, skip_first_screenshot=True):
|
||||
"""
|
||||
Args:
|
||||
check_button:
|
||||
skip_first_screenshot:
|
||||
|
||||
Pages:
|
||||
@ -87,8 +89,13 @@ class CombatObtain(PlannerMixin):
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
if not self.appear(ITEM_CLOSE) and self.appear(COMBAT_PREPARE) and self.appear(MAY_OBTAIN):
|
||||
break
|
||||
if not self.appear(ITEM_CLOSE):
|
||||
if callable(check_button):
|
||||
if check_button():
|
||||
break
|
||||
else:
|
||||
if self.appear(check_button):
|
||||
break
|
||||
if self.appear_then_click(ITEM_CLOSE, interval=2):
|
||||
continue
|
||||
|
||||
@ -219,7 +226,7 @@ class CombatObtain(PlannerMixin):
|
||||
logger.error(f'No obtain entry for {entry_index}')
|
||||
break
|
||||
|
||||
self._obtain_enter(entry)
|
||||
self._obtain_enter(entry, appear_button=COMBAT_PREPARE)
|
||||
item = self._obtain_parse()
|
||||
if item.item == KEYWORDS_ITEM_CURRENCY.Trailblaze_EXP:
|
||||
logger.warning('Trailblaze_EXP is in obtain list, OBTAIN_TRAILBLAZE_EXP may need to verify')
|
||||
@ -229,7 +236,7 @@ class CombatObtain(PlannerMixin):
|
||||
items.append(item)
|
||||
index += 1
|
||||
prev = item
|
||||
self._obtain_close()
|
||||
self._obtain_close(check_button=MAY_OBTAIN)
|
||||
|
||||
logger.hr('Obtained Result')
|
||||
for item in items:
|
||||
|
75
tasks/item/assets/assets_item_synthesize.py
Normal file
@ -0,0 +1,75 @@
|
||||
from module.base.button import Button, ButtonWrapper
|
||||
|
||||
# This file was auto-generated, do not modify it manually. To generate:
|
||||
# ``` python -m dev_tools.button_extract ```
|
||||
|
||||
ENTRY_ITEM_FROM = ButtonWrapper(
|
||||
name='ENTRY_ITEM_FROM',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/ENTRY_ITEM_FROM.png',
|
||||
area=(825, 408, 905, 488),
|
||||
search=(805, 388, 925, 508),
|
||||
color=(70, 105, 114),
|
||||
button=(825, 408, 905, 488),
|
||||
),
|
||||
)
|
||||
ENTRY_ITEM_TO = ButtonWrapper(
|
||||
name='ENTRY_ITEM_TO',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/ENTRY_ITEM_TO.png',
|
||||
area=(815, 193, 915, 293),
|
||||
search=(795, 173, 935, 313),
|
||||
color=(157, 143, 120),
|
||||
button=(815, 193, 915, 293),
|
||||
),
|
||||
)
|
||||
ITEM_NAME = ButtonWrapper(
|
||||
name='ITEM_NAME',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/ITEM_NAME.png',
|
||||
area=(657, 95, 1057, 118),
|
||||
search=(637, 75, 1077, 138),
|
||||
color=(92, 100, 111),
|
||||
button=(657, 95, 1057, 118),
|
||||
),
|
||||
)
|
||||
SWITCH_RARITY = ButtonWrapper(
|
||||
name='SWITCH_RARITY',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/SWITCH_RARITY.png',
|
||||
area=(1180, 262, 1216, 298),
|
||||
search=(1160, 242, 1236, 318),
|
||||
color=(89, 96, 124),
|
||||
button=(1180, 262, 1216, 298),
|
||||
),
|
||||
)
|
||||
SYNTHESIZE_AMOUNT = ButtonWrapper(
|
||||
name='SYNTHESIZE_AMOUNT',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/SYNTHESIZE_AMOUNT.png',
|
||||
area=(683, 548, 1034, 568),
|
||||
search=(663, 528, 1054, 588),
|
||||
color=(122, 132, 147),
|
||||
button=(683, 548, 1034, 568),
|
||||
),
|
||||
)
|
||||
SYNTHESIZE_MINUS = ButtonWrapper(
|
||||
name='SYNTHESIZE_MINUS',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/SYNTHESIZE_MINUS.png',
|
||||
area=(575, 569, 605, 589),
|
||||
search=(555, 549, 625, 609),
|
||||
color=(235, 235, 235),
|
||||
button=(575, 569, 605, 589),
|
||||
),
|
||||
)
|
||||
SYNTHESIZE_PLUS = ButtonWrapper(
|
||||
name='SYNTHESIZE_PLUS',
|
||||
share=Button(
|
||||
file='./assets/share/item/synthesize/SYNTHESIZE_PLUS.png',
|
||||
area=(1125, 567, 1155, 589),
|
||||
search=(1105, 547, 1175, 609),
|
||||
color=(228, 228, 228),
|
||||
button=(1125, 567, 1155, 589),
|
||||
),
|
||||
)
|
211
tasks/item/synthesize.py
Normal file
@ -0,0 +1,211 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from module.base.timer import Timer
|
||||
from module.base.utils import color_similarity_2d, crop, image_size
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import Ocr
|
||||
from tasks.base.page import page_synthesize
|
||||
from tasks.combat.obtain import CombatObtain
|
||||
from tasks.item.assets.assets_item_synthesize import *
|
||||
from tasks.planner.keywords import ITEM_CLASSES
|
||||
from tasks.planner.model import ObtainedAmmount
|
||||
from tasks.planner.scan import OcrItemName
|
||||
|
||||
RARITY_COLOR = {
|
||||
'green': (68, 127, 124),
|
||||
'blue': (76, 124, 191),
|
||||
'purple': (141, 97, 203),
|
||||
}
|
||||
|
||||
|
||||
def image_color_count(image, color, threshold=221):
|
||||
mask = color_similarity_2d(image, color=color)
|
||||
cv2.inRange(mask, threshold, 255, dst=mask)
|
||||
sum_ = cv2.countNonZero(mask)
|
||||
return sum_
|
||||
|
||||
|
||||
class WhiteStrip(Ocr):
|
||||
def pre_process(self, image):
|
||||
mask = color_similarity_2d(image, color=(255, 255, 255))
|
||||
mask = cv2.inRange(mask, 180, 255, dst=mask)
|
||||
|
||||
mask = np.mean(mask, axis=0)
|
||||
point = np.array(cv2.findNonZero(mask))[:, 0, 1]
|
||||
x1, x2 = point.min(), point.max()
|
||||
|
||||
_, y = image_size(image)
|
||||
image = crop(image, (x1 - 5, 0, x2 + 5, y), copy=False)
|
||||
return image
|
||||
|
||||
|
||||
class SynthesizeItemName(OcrItemName, WhiteStrip):
|
||||
pass
|
||||
|
||||
|
||||
class Synthesize(CombatObtain):
|
||||
def item_get_rarity(self, button) -> str | None:
|
||||
"""
|
||||
Args:
|
||||
button:
|
||||
|
||||
Returns:
|
||||
str: Rarity color or None if no match
|
||||
|
||||
Pages:
|
||||
in: page_synthesize
|
||||
"""
|
||||
image = self.image_crop(button)
|
||||
image = cv2.GaussianBlur(image, (3, 3), 0)
|
||||
x2, y2 = image_size(image)
|
||||
y1 = y2 - int(y2 // 4)
|
||||
image = crop(image, (0, y1, x2, y2))
|
||||
# self.device.image_show(image)
|
||||
# print(image.shape)
|
||||
|
||||
# Must contain 30% target color at icon bottom
|
||||
minimum = x2 * (y2 - y1) * 0.3
|
||||
for rarity, color in RARITY_COLOR.items():
|
||||
count = image_color_count(image, color=color, threshold=221)
|
||||
# print(rarity, count, minimum)
|
||||
if count > minimum:
|
||||
return rarity
|
||||
|
||||
return None
|
||||
|
||||
def item_get_rarity_retry(self, button, skip_first_screenshot=True) -> str | None:
|
||||
timeout = Timer(1, count=3).start()
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
current = self.item_get_rarity(button)
|
||||
logger.attr('SynthesizeRarity', current)
|
||||
if current is not None:
|
||||
return current
|
||||
if timeout.reached():
|
||||
logger.warning(f'item_get_rarity_retry timeout')
|
||||
return None
|
||||
|
||||
def synthesize_rarity_set(self, rarity: str, skip_first_screenshot=True) -> bool:
|
||||
"""
|
||||
Args:
|
||||
rarity: "green" or "blue"
|
||||
note that rarity is the one you consume to synthesize
|
||||
skip_first_screenshot:
|
||||
|
||||
Returns:
|
||||
bool: If switched
|
||||
|
||||
Pages:
|
||||
in: page_synthesize
|
||||
"""
|
||||
logger.info(f'item_synthesize_rarity_set: {rarity}')
|
||||
|
||||
switched = False
|
||||
self.interval_clear(page_synthesize.check_button)
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
current = self.item_get_rarity(ENTRY_ITEM_FROM)
|
||||
logger.attr('SynthesizeRarity', current)
|
||||
if current is not None and current == rarity:
|
||||
break
|
||||
|
||||
# Click
|
||||
if self.ui_page_appear(page_synthesize, interval=2):
|
||||
self.device.click(SWITCH_RARITY)
|
||||
switched = True
|
||||
continue
|
||||
|
||||
return switched
|
||||
|
||||
def synthesize_rarity_reset(self, skip_first_screenshot=True):
|
||||
"""
|
||||
Reset rarity switch, so current item will pin on the first row
|
||||
|
||||
Returns:
|
||||
bool: If success
|
||||
"""
|
||||
|
||||
current = self.item_get_rarity_retry(ENTRY_ITEM_FROM)
|
||||
if current == 'blue':
|
||||
r1, r2 = 'green', 'blue'
|
||||
elif current == 'green':
|
||||
r1, r2 = 'blue', 'green'
|
||||
else:
|
||||
logger.error(f'item_synthesize_rarity_reset: Unknown current rarity {current}')
|
||||
return False
|
||||
|
||||
self.synthesize_rarity_set(r1, skip_first_screenshot=skip_first_screenshot)
|
||||
self.synthesize_rarity_set(r2, skip_first_screenshot=True)
|
||||
return True
|
||||
|
||||
def synthesize_obtain_get(self) -> list[ObtainedAmmount]:
|
||||
"""
|
||||
Update item amount from synthesize page
|
||||
"""
|
||||
logger.hr('Synthesize obtain get', level=2)
|
||||
items = []
|
||||
|
||||
def obtain_end():
|
||||
return self.item_get_rarity(ENTRY_ITEM_FROM) is not None
|
||||
|
||||
# Purple
|
||||
self.synthesize_rarity_set('blue')
|
||||
self._obtain_enter(ENTRY_ITEM_TO, appear_button=page_synthesize.check_button)
|
||||
item = self._obtain_parse()
|
||||
if item is not None:
|
||||
items.append(item)
|
||||
self._obtain_close(check_button=obtain_end)
|
||||
# Blue
|
||||
self._obtain_enter(ENTRY_ITEM_FROM, appear_button=page_synthesize.check_button)
|
||||
item = self._obtain_parse()
|
||||
if item is not None:
|
||||
items.append(item)
|
||||
self._obtain_close(check_button=obtain_end)
|
||||
# Green
|
||||
self.synthesize_rarity_set('green')
|
||||
self._obtain_enter(ENTRY_ITEM_FROM, appear_button=page_synthesize.check_button)
|
||||
item = self._obtain_parse()
|
||||
if item is not None:
|
||||
items.append(item)
|
||||
self._obtain_close(check_button=obtain_end)
|
||||
|
||||
logger.hr('Obtained Result')
|
||||
for item in items:
|
||||
# Pretend everything is full
|
||||
# item.value += 1000
|
||||
logger.info(f'Obtained item: {item.item.name}, {item.value}')
|
||||
"""
|
||||
<<< OBTAIN GET RESULT >>>
|
||||
ItemAmount: Arrow_of_the_Starchaser, 15
|
||||
ItemAmount: Arrow_of_the_Demon_Slayer, 68
|
||||
ItemAmount: Arrow_of_the_Beast_Hunter, 85
|
||||
"""
|
||||
self.planner.load_obtained_amount(items)
|
||||
self.planner_write()
|
||||
return items
|
||||
|
||||
def synthesize_get_item(self):
|
||||
ocr = SynthesizeItemName(ITEM_NAME)
|
||||
item = ocr.matched_single_line(self.device.image, keyword_classes=ITEM_CLASSES)
|
||||
if item is None:
|
||||
logger.warning('synthesize_get_item: Unknown item name')
|
||||
return None
|
||||
|
||||
return item
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
self = Synthesize('src')
|
||||
self.device.screenshot()
|
||||
self.synthesize_obtain_get()
|
@ -21,8 +21,11 @@ class OcrItemName(Ocr):
|
||||
def after_process(self, result):
|
||||
result = result.replace('念火之心', '忿火之心')
|
||||
result = re.sub('工造机$', '工造机杼', result)
|
||||
result = re.sub('工造轮', '工造迴轮', result)
|
||||
result = re.sub('工造迥?轮', '工造迴轮', result)
|
||||
result = re.sub('月狂[療撩]?牙', '月狂獠牙', result)
|
||||
# Error words on blank background
|
||||
result = re.sub('^[國東]', '', result)
|
||||
result = re.sub('時$', '', result)
|
||||
return result
|
||||
|
||||
|
||||
|