2024-05-11 18:31:42 +00:00
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
from module.base.timer import Timer
|
|
|
|
|
from module.exception import ScriptError
|
|
|
|
|
from module.logger import logger
|
|
|
|
|
from module.ocr.ocr import Digit
|
2024-05-17 18:35:13 +00:00
|
|
|
|
from tasks.combat.assets.assets_combat_obtain import *
|
2024-05-19 17:49:50 +00:00
|
|
|
|
from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE
|
2024-05-11 18:31:42 +00:00
|
|
|
|
from tasks.dungeon.keywords import DungeonList
|
2024-05-27 16:51:16 +00:00
|
|
|
|
from tasks.planner.keywords import ITEM_CLASSES, KEYWORDS_ITEM_CURRENCY
|
2024-05-17 18:35:13 +00:00
|
|
|
|
from tasks.planner.model import ObtainedAmmount, PlannerMixin
|
2024-05-19 16:21:56 +00:00
|
|
|
|
from tasks.planner.scan import OcrItemName
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OcrItemAmount(Digit):
|
|
|
|
|
def format_result(self, result):
|
2024-08-31 13:54:27 +00:00
|
|
|
|
res = re.split(r'[::;;]', result, maxsplit=1)
|
2024-05-11 18:31:42 +00:00
|
|
|
|
result = res[-1]
|
|
|
|
|
return super().format_result(result)
|
|
|
|
|
|
|
|
|
|
|
2024-05-17 18:35:13 +00:00
|
|
|
|
class CombatObtain(PlannerMixin):
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
Parse items that can be obtained from dungeon
|
|
|
|
|
|
|
|
|
|
Pages:
|
|
|
|
|
in: COMBAT_PREPARE
|
|
|
|
|
"""
|
2024-05-17 18:35:13 +00:00
|
|
|
|
# False to click again when combat ends
|
|
|
|
|
# True to exit and reenter to get obtained items
|
|
|
|
|
obtain_frequent_check = False
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
2024-05-28 13:18:33 +00:00
|
|
|
|
def _obtain_enter(self, entry, appear_button, skip_first_screenshot=True):
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
entry: Item entry
|
2024-05-28 13:18:33 +00:00
|
|
|
|
appear_button:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
skip_first_screenshot:
|
|
|
|
|
|
|
|
|
|
Pages:
|
|
|
|
|
in: COMBAT_PREPARE
|
|
|
|
|
out: ITEM_CLOSE
|
|
|
|
|
"""
|
|
|
|
|
logger.info(f'Obtain enter {entry}')
|
2024-05-28 13:18:33 +00:00
|
|
|
|
self.interval_clear(appear_button)
|
2024-05-11 18:31:42 +00:00
|
|
|
|
while 1:
|
|
|
|
|
if skip_first_screenshot:
|
|
|
|
|
skip_first_screenshot = False
|
|
|
|
|
else:
|
|
|
|
|
self.device.screenshot()
|
|
|
|
|
|
|
|
|
|
if self.appear(ITEM_CLOSE):
|
|
|
|
|
break
|
2024-05-28 13:18:33 +00:00
|
|
|
|
if self.appear(appear_button, interval=2):
|
2024-05-11 18:31:42 +00:00
|
|
|
|
self.device.click(entry)
|
2024-05-28 13:18:33 +00:00
|
|
|
|
self.interval_reset(appear_button)
|
2024-05-11 18:31:42 +00:00
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Wait animation
|
|
|
|
|
timeout = Timer(1.4, count=7).start()
|
|
|
|
|
skip_first_screenshot = True
|
|
|
|
|
while 1:
|
|
|
|
|
if skip_first_screenshot:
|
|
|
|
|
skip_first_screenshot = False
|
|
|
|
|
else:
|
|
|
|
|
self.device.screenshot()
|
|
|
|
|
|
|
|
|
|
if self.image_color_count(ITEM_NAME, color=(0, 0, 0), threshold=221, count=50):
|
|
|
|
|
break
|
|
|
|
|
if timeout.reached():
|
|
|
|
|
logger.warning('Wait obtain item timeout')
|
|
|
|
|
break
|
|
|
|
|
|
2024-05-28 13:18:33 +00:00
|
|
|
|
def _obtain_close(self, check_button, skip_first_screenshot=True):
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
Args:
|
2024-05-28 13:18:33 +00:00
|
|
|
|
check_button:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
skip_first_screenshot:
|
|
|
|
|
|
|
|
|
|
Pages:
|
|
|
|
|
in: ITEM_CLOSE
|
|
|
|
|
out: COMBAT_PREPARE
|
|
|
|
|
"""
|
|
|
|
|
logger.info(f'Obtain close')
|
|
|
|
|
self.interval_clear(ITEM_CLOSE)
|
|
|
|
|
while 1:
|
|
|
|
|
if skip_first_screenshot:
|
|
|
|
|
skip_first_screenshot = False
|
|
|
|
|
else:
|
|
|
|
|
self.device.screenshot()
|
|
|
|
|
|
2024-05-28 13:18:33 +00:00
|
|
|
|
if not self.appear(ITEM_CLOSE):
|
|
|
|
|
if callable(check_button):
|
|
|
|
|
if check_button():
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
if self.appear(check_button):
|
|
|
|
|
break
|
2024-05-11 18:31:42 +00:00
|
|
|
|
if self.appear_then_click(ITEM_CLOSE, interval=2):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2024-05-27 16:51:16 +00:00
|
|
|
|
def _obtain_get_entry(dungeon: DungeonList, index: int = 1, prev: ObtainedAmmount = None, start: int = 0):
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
dungeon: Current dungeon
|
|
|
|
|
index: 1 to 3, index to check
|
|
|
|
|
prev: Previous item checked
|
|
|
|
|
|
|
|
|
|
Returns:
|
2024-05-27 16:51:16 +00:00
|
|
|
|
int: Item entry index, or None if no more check needed
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
if (index > 1 and prev is None) or (index <= 1 and prev is not None):
|
|
|
|
|
raise ScriptError(f'_obtain_get_entry: index and prev must be set together, index={index}, prev={prev}')
|
|
|
|
|
|
|
|
|
|
if index > 3:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def may_obtain_one():
|
|
|
|
|
if prev is None:
|
2024-05-27 16:51:16 +00:00
|
|
|
|
if start:
|
|
|
|
|
return 1 + start
|
|
|
|
|
else:
|
|
|
|
|
return 1
|
2024-05-11 18:31:42 +00:00
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def may_obtain_multi():
|
|
|
|
|
if prev is None:
|
2024-05-27 16:51:16 +00:00
|
|
|
|
if start:
|
|
|
|
|
return 1 + start
|
|
|
|
|
else:
|
|
|
|
|
return 1
|
2024-05-11 18:31:42 +00:00
|
|
|
|
# End at the item with the lowest rarity
|
|
|
|
|
if prev.item.is_rarity_green:
|
|
|
|
|
return None
|
2024-05-27 16:51:16 +00:00
|
|
|
|
# End at credict
|
|
|
|
|
if prev.item == KEYWORDS_ITEM_CURRENCY.Credit:
|
|
|
|
|
return None
|
|
|
|
|
if start:
|
|
|
|
|
return index + start
|
|
|
|
|
else:
|
|
|
|
|
return index
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
|
|
|
|
if dungeon is None:
|
|
|
|
|
return may_obtain_multi()
|
|
|
|
|
if dungeon.is_Echo_of_War:
|
|
|
|
|
return may_obtain_one()
|
|
|
|
|
if dungeon.is_Cavern_of_Corrosion:
|
|
|
|
|
return None
|
|
|
|
|
if dungeon.is_Stagnant_Shadow:
|
|
|
|
|
return may_obtain_one()
|
|
|
|
|
if dungeon.is_Calyx_Golden:
|
|
|
|
|
if dungeon.is_Calyx_Golden_Treasures:
|
|
|
|
|
return may_obtain_one()
|
|
|
|
|
else:
|
|
|
|
|
return may_obtain_multi()
|
|
|
|
|
if dungeon.is_Calyx_Crimson:
|
|
|
|
|
return may_obtain_multi()
|
|
|
|
|
|
|
|
|
|
raise ScriptError(f'_obtain_get_entry: Cannot get entry from {dungeon}')
|
|
|
|
|
|
2024-05-15 18:59:04 +00:00
|
|
|
|
def _obtain_parse(self) -> ObtainedAmmount | None:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
Pages:
|
|
|
|
|
in: ITEM_CLOSE
|
|
|
|
|
"""
|
|
|
|
|
ocr = OcrItemName(ITEM_NAME)
|
|
|
|
|
item = ocr.matched_single_line(self.device.image, keyword_classes=ITEM_CLASSES)
|
|
|
|
|
ocr = OcrItemAmount(ITEM_AMOUNT)
|
|
|
|
|
amount = ocr.ocr_single_line(self.device.image)
|
|
|
|
|
|
|
|
|
|
if item is None:
|
|
|
|
|
logger.warning('_obtain_parse: Unknown item name')
|
|
|
|
|
return None
|
|
|
|
|
|
2024-05-15 18:59:04 +00:00
|
|
|
|
# logger.info(f'ObtainedAmmount: item={item}, value={amount}')
|
|
|
|
|
return ObtainedAmmount(
|
2024-05-11 18:31:42 +00:00
|
|
|
|
item=item,
|
2024-05-15 18:59:04 +00:00
|
|
|
|
value=amount,
|
2024-05-11 18:31:42 +00:00
|
|
|
|
)
|
|
|
|
|
|
2024-10-05 11:28:00 +00:00
|
|
|
|
def obtain_parse(self, skip_first_screenshot=True) -> ObtainedAmmount | None:
|
|
|
|
|
"""
|
|
|
|
|
Parse obtain item with retry
|
|
|
|
|
|
|
|
|
|
Pages:
|
|
|
|
|
in: ITEM_CLOSE
|
|
|
|
|
"""
|
|
|
|
|
timeout = Timer(1, count=3).start()
|
|
|
|
|
while 1:
|
|
|
|
|
if skip_first_screenshot:
|
|
|
|
|
skip_first_screenshot = False
|
|
|
|
|
else:
|
|
|
|
|
self.device.screenshot()
|
|
|
|
|
|
|
|
|
|
if timeout.reached():
|
|
|
|
|
logger.error('obtain_parse timeout')
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
obtain = self._obtain_parse()
|
|
|
|
|
if obtain is not None:
|
|
|
|
|
return obtain
|
|
|
|
|
else:
|
|
|
|
|
self.screenshot_tracking_add()
|
|
|
|
|
|
2024-05-15 18:59:04 +00:00
|
|
|
|
def obtain_get(self, dungeon=None, skip_first_screenshot=True) -> list[ObtainedAmmount]:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
dungeon: Current dungeon,
|
|
|
|
|
or None for no early stop optimization
|
|
|
|
|
skip_first_screenshot:
|
|
|
|
|
|
|
|
|
|
Returns:
|
2024-05-15 18:59:04 +00:00
|
|
|
|
list[ObtainedAmmount]:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
|
|
|
|
Pages:
|
|
|
|
|
in: COMBAT_PREPARE
|
|
|
|
|
out: COMBAT_PREPARE
|
|
|
|
|
"""
|
|
|
|
|
logger.hr('Obtain get', level=2)
|
|
|
|
|
if not skip_first_screenshot:
|
|
|
|
|
self.device.screenshot()
|
|
|
|
|
|
|
|
|
|
index = 1
|
|
|
|
|
prev = None
|
|
|
|
|
items = []
|
2024-05-27 16:51:16 +00:00
|
|
|
|
dic_entry = {
|
|
|
|
|
1: OBTAIN_1,
|
|
|
|
|
2: OBTAIN_2,
|
|
|
|
|
3: OBTAIN_3,
|
|
|
|
|
4: OBTAIN_4,
|
|
|
|
|
}
|
2024-05-19 17:49:50 +00:00
|
|
|
|
|
|
|
|
|
self._find_may_obtain()
|
|
|
|
|
|
2024-05-27 16:51:16 +00:00
|
|
|
|
trailblaze_exp = False
|
2024-05-11 18:31:42 +00:00
|
|
|
|
for _ in range(5):
|
2024-05-27 16:51:16 +00:00
|
|
|
|
if not trailblaze_exp and self.appear(OBTAIN_TRAILBLAZE_EXP):
|
|
|
|
|
trailblaze_exp = True
|
|
|
|
|
logger.attr('trailblaze_exp', trailblaze_exp)
|
|
|
|
|
|
|
|
|
|
entry_index = self._obtain_get_entry(dungeon, index=index, prev=prev, start=int(trailblaze_exp))
|
|
|
|
|
if entry_index is None:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
logger.info('Obtain get end')
|
|
|
|
|
break
|
2024-05-27 16:51:16 +00:00
|
|
|
|
try:
|
|
|
|
|
entry = dic_entry[entry_index]
|
|
|
|
|
except KeyError:
|
|
|
|
|
logger.error(f'No obtain entry for {entry_index}')
|
|
|
|
|
break
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
2024-05-28 13:18:33 +00:00
|
|
|
|
self._obtain_enter(entry, appear_button=COMBAT_PREPARE)
|
2024-10-05 11:28:00 +00:00
|
|
|
|
item = self.obtain_parse()
|
2024-06-04 08:00:48 +00:00
|
|
|
|
if item is not None:
|
|
|
|
|
if item.item == KEYWORDS_ITEM_CURRENCY.Trailblaze_EXP:
|
|
|
|
|
logger.warning('Trailblaze_EXP is in obtain list, OBTAIN_TRAILBLAZE_EXP may need to verify')
|
|
|
|
|
index += 1
|
|
|
|
|
prev = item
|
|
|
|
|
else:
|
|
|
|
|
items.append(item)
|
|
|
|
|
index += 1
|
|
|
|
|
prev = item
|
|
|
|
|
else:
|
2024-05-11 18:31:42 +00:00
|
|
|
|
index += 1
|
2024-05-28 13:18:33 +00:00
|
|
|
|
self._obtain_close(check_button=MAY_OBTAIN)
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
2024-05-15 18:59:04 +00:00
|
|
|
|
logger.hr('Obtained Result')
|
2024-05-11 18:31:42 +00:00
|
|
|
|
for item in items:
|
2024-05-17 18:35:13 +00:00
|
|
|
|
# Pretend everything is full
|
|
|
|
|
# item.value += 1000
|
2024-05-15 18:59:04 +00:00
|
|
|
|
logger.info(f'Obtained item: {item.item.name}, {item.value}')
|
2024-05-11 18:31:42 +00:00
|
|
|
|
"""
|
|
|
|
|
<<< OBTAIN GET RESULT >>>
|
|
|
|
|
ItemAmount: Arrow_of_the_Starchaser, 15
|
|
|
|
|
ItemAmount: Arrow_of_the_Demon_Slayer, 68
|
|
|
|
|
ItemAmount: Arrow_of_the_Beast_Hunter, 85
|
|
|
|
|
"""
|
2024-05-15 18:59:04 +00:00
|
|
|
|
self.planner.load_obtained_amount(items)
|
2024-06-17 18:19:59 +00:00
|
|
|
|
with self.config.multi_set():
|
|
|
|
|
self.planner_write()
|
|
|
|
|
# Sync to dashboard
|
|
|
|
|
for item in items:
|
|
|
|
|
if item.item.name == 'Credit':
|
|
|
|
|
self.config.stored.Credit.value = item.value
|
|
|
|
|
|
2024-05-11 18:31:42 +00:00
|
|
|
|
return items
|
|
|
|
|
|
2024-05-30 12:26:45 +00:00
|
|
|
|
def obtained_is_full(self, dungeon: DungeonList | None, wave_done=0, obtain_get=True) -> bool:
|
2024-05-17 18:35:13 +00:00
|
|
|
|
if dungeon is None:
|
|
|
|
|
self.obtain_frequent_check = False
|
|
|
|
|
return False
|
|
|
|
|
row = self.planner.row_come_from_dungeon(dungeon)
|
|
|
|
|
if row is None:
|
|
|
|
|
self.obtain_frequent_check = False
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Update
|
2024-05-30 12:26:45 +00:00
|
|
|
|
if obtain_get:
|
|
|
|
|
self.obtain_get(dungeon)
|
2024-05-17 18:35:13 +00:00
|
|
|
|
|
|
|
|
|
# Check progress
|
|
|
|
|
row = self.planner.row_come_from_dungeon(dungeon)
|
|
|
|
|
if row is None:
|
|
|
|
|
logger.error(f'obtained_is_full: Row disappeared after obtain_get')
|
|
|
|
|
self.obtain_frequent_check = False
|
|
|
|
|
return False
|
|
|
|
|
if not row.need_farm():
|
|
|
|
|
logger.info('Planner row full')
|
|
|
|
|
self.obtain_frequent_check = False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
# obtain_frequent_check
|
2024-05-27 19:56:30 +00:00
|
|
|
|
approaching = row.is_approaching_total(wave_done)
|
|
|
|
|
logger.attr('is_approaching_total', approaching)
|
|
|
|
|
self.obtain_frequent_check = approaching
|
2024-05-17 18:35:13 +00:00
|
|
|
|
return False
|
|
|
|
|
|
2024-05-19 17:49:50 +00:00
|
|
|
|
def _find_may_obtain(self, skip_first_screenshot=True):
|
|
|
|
|
logger.info('Find may obtain')
|
|
|
|
|
while 1:
|
|
|
|
|
if skip_first_screenshot:
|
|
|
|
|
skip_first_screenshot = False
|
|
|
|
|
else:
|
|
|
|
|
self.device.screenshot()
|
2024-05-21 04:35:45 +00:00
|
|
|
|
|
2024-05-19 17:49:50 +00:00
|
|
|
|
if MAY_OBTAIN.match_template(self.device.image):
|
|
|
|
|
OBTAIN_1.load_offset(MAY_OBTAIN)
|
2024-05-21 04:35:45 +00:00
|
|
|
|
OBTAIN_2.load_offset(MAY_OBTAIN)
|
|
|
|
|
OBTAIN_3.load_offset(MAY_OBTAIN)
|
2024-05-27 16:51:16 +00:00
|
|
|
|
OBTAIN_4.load_offset(MAY_OBTAIN)
|
2024-05-19 17:49:50 +00:00
|
|
|
|
return True
|
|
|
|
|
|
2024-05-11 18:31:42 +00:00
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2024-05-17 18:35:13 +00:00
|
|
|
|
self = CombatObtain('src')
|
2024-05-11 18:31:42 +00:00
|
|
|
|
self.device.screenshot()
|
|
|
|
|
self.obtain_get()
|