Add: battle pass recognition (#49)

* Add: battle pass mission recognition

* Fix: typo

* Add: version mission recognition

* Fix: typo

* Upd: remove version mission

* Upd: keep ui of version quest
This commit is contained in:
Hengyu 2023-08-02 19:07:46 +08:00 committed by GitHub
parent 7e4c9b0258
commit 28d5c43602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 367 additions and 19 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -14,7 +14,7 @@ UI_LANGUAGES = ['cn', 'cht', 'en', 'jp']
def text_to_variable(text):
text = re.sub("'s |s' ", '_', text)
text = re.sub('[ \-—:\'/•]+', '_', text)
text = re.sub(r'[(),#]|</?\w+>', '', text)
text = re.sub(r'[(),#"?]|</?\w+>', '', text)
# text = re.sub(r'[#_]?\d+(_times?)?', '', text)
return text
@ -182,13 +182,25 @@ class KeywordExtract:
self.clear_keywords()
return gen
def load_daily_quests_keywords(self, lang='cn'):
daily_quest = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'DailyQuest.json'))
def load_quests(self, quests, lang='cn'):
"""
Load a set of quest keywords
Args:
quests: iterable quest id collection
lang:
"""
quest_data = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'QuestData.json'))
quests_hash = [quest_data[quest_id]["QuestTitle"]["Hash"] for quest_id in daily_quest]
quest_keywords = [self.text_map[lang].find(quest_hash)[1] for quest_hash in quests_hash]
quests_hash = [quest_data[str(quest_id)]["QuestTitle"]["Hash"] for quest_id in quests]
quest_keywords = list(dict.fromkeys([self.text_map[lang].find(quest_hash)[1] for quest_hash in quests_hash]))
self.load_keywords(quest_keywords, lang)
def generate_daily_quests(self):
daily_quest = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'DailyQuest.json'))
self.load_quests(daily_quest.keys())
self.write_keywords(keyword_class='DailyQuest', output_file='./tasks/daily/keywords/daily_quest.py')
def load_character_name_keywords(self, lang='en'):
file_name = 'ItemConfigAvatarPlayerIcon.json'
path = os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', file_name)
@ -259,26 +271,40 @@ class KeywordExtract:
self.write_keywords(keyword_class='CharacterList', output_file='./tasks/character/keywords/character_list.py',
text_convert=character_name)
def generate_battle_pass_quests(self):
battle_pass_quests = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'BattlePassConfig.json'))
latest_quests = list(battle_pass_quests.values())[-1]
quests = deep_get(latest_quests, "DailyQuestList") + deep_get(latest_quests, "WeekQuestList") + deep_get(
latest_quests, "WeekOrder1")
self.load_quests(quests)
self.write_keywords(keyword_class='BattlePassQuest', output_file='./tasks/battle_pass/keywords/quest.py')
def generate(self):
self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响', '忘却之庭'])
self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
self.load_keywords(['行动摘要', '生存索引', '每日实训'])
self.write_keywords(keyword_class='DungeonTab', output_file='./tasks/dungeon/keywords/tab.py')
self.load_daily_quests_keywords()
self.write_keywords(keyword_class='DailyQuest', output_file='./tasks/daily/keywords/daily_quest.py')
self.load_keywords(['前往', '领取', '进行中', '已领取', '本日活跃度已满'])
self.write_keywords(keyword_class='DailyQuestState', output_file='./tasks/daily/keywords/daily_quest_state.py')
self.load_keywords(['领取', '追踪'])
self.write_keywords(keyword_class='BattlePassQuestState',
output_file='./tasks/battle_pass/keywords/quest_state.py')
self.load_keywords(list(self.iter_guide()))
self.write_keywords(keyword_class='DungeonList', output_file='./tasks/dungeon/keywords/dungeon.py',
text_convert=dungeon_name)
self.load_keywords(['传送', '追踪'])
self.write_keywords(keyword_class='DungeonEntrance', output_file='./tasks/dungeon/keywords/dungeon_entrance.py')
self.load_keywords(['奖励', '任务'])
self.load_keywords(['奖励', '任务', ])
self.write_keywords(keyword_class='BattlePassTab', output_file='./tasks/battle_pass/keywords/tab.py')
self.load_keywords(['本日任务', '本周任务', '本期任务'])
self.write_keywords(keyword_class='BattlePassMissionTab',
output_file='./tasks/battle_pass/keywords/mission_tab.py')
self.generate_assignment_keywords()
self.generate_forgotten_hall_stages()
self.generate_map_planes()
self.generate_character_keywords()
self.generate_daily_quests()
self.generate_battle_pass_quests()
self.load_keywords(['养成材料', '光锥', '遗器', '其他材料', '消耗品', '任务', '贵重物'])
self.write_keywords(keyword_class='ItemTab', text_convert=lambda name: name.replace(' ', ''),
output_file='./tasks/item/keywords/tab.py')

View File

@ -6,7 +6,7 @@ from typing import ClassVar
from module.exception import ScriptError
import module.config.server as server
REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。!??·•\-—/\\\n\t()()「」『』【】]')
REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。!??·•\-—/\\\n\t()\[\]()「」『』【】]')
def parse_name(n):
@ -116,6 +116,10 @@ class Keyword:
def __post_init__(self):
self.__class__.instances[self.id] = self
@classmethod
def _compare(cls, name, keyword):
return name == keyword
@classmethod
def find(cls, name, in_current_server=False, ignore_punctuation=True):
"""
@ -153,7 +157,7 @@ class Keyword:
for instance in cls.instances.values():
for keyword in instance._keywords_to_find(
in_current_server=in_current_server, ignore_punctuation=ignore_punctuation):
if name == keyword:
if cls._compare(name, keyword):
return instance
# Not found

View File

@ -53,6 +53,26 @@ MISSIONS_LOADED = ButtonWrapper(
button=(1163, 573, 1179, 589),
),
)
MISSION_PAGE_SCROLL = ButtonWrapper(
name='MISSION_PAGE_SCROLL',
share=Button(
file='./assets/share/battle_pass/MISSION_PAGE_SCROLL.png',
area=(1186, 215, 1190, 601),
search=(1166, 195, 1210, 621),
color=(140, 140, 140),
button=(1186, 215, 1190, 601),
),
)
OCR_BATTLE_PASS_QUEST = ButtonWrapper(
name='OCR_BATTLE_PASS_QUEST',
share=Button(
file='./assets/share/battle_pass/OCR_BATTLE_PASS_QUEST.png',
area=(372, 216, 1173, 591),
search=(352, 196, 1193, 611),
color=(185, 184, 180),
button=(372, 216, 1173, 591),
),
)
OCR_LEVEL = ButtonWrapper(
name='OCR_LEVEL',
share=Button(
@ -73,6 +93,16 @@ OCR_REMAINING_TIME = ButtonWrapper(
button=(289, 89, 630, 132),
),
)
PERIOD_MISSION_CLICK = ButtonWrapper(
name='PERIOD_MISSION_CLICK',
share=Button(
file='./assets/share/battle_pass/PERIOD_MISSION_CLICK.png',
area=(128, 478, 323, 578),
search=(108, 458, 343, 598),
color=(72, 65, 45),
button=(128, 478, 323, 578),
),
)
REWARDS_CHECK = ButtonWrapper(
name='REWARDS_CHECK',
share=Button(
@ -113,3 +143,23 @@ REWARDS_LOADED = ButtonWrapper(
button=(173, 286, 248, 324),
),
)
TODAY_MISSION_CLICK = ButtonWrapper(
name='TODAY_MISSION_CLICK',
share=Button(
file='./assets/share/battle_pass/TODAY_MISSION_CLICK.png',
area=(128, 192, 323, 334),
search=(108, 172, 343, 354),
color=(65, 59, 40),
button=(128, 192, 323, 334),
),
)
WEEK_MISSION_CLICK = ButtonWrapper(
name='WEEK_MISSION_CLICK',
share=Button(
file='./assets/share/battle_pass/WEEK_MISSION_CLICK.png',
area=(128, 336, 323, 477),
search=(108, 316, 343, 497),
color=(68, 62, 42),
button=(128, 336, 323, 477),
),
)

View File

@ -1,4 +1,5 @@
import datetime
import re
import numpy as np
@ -6,14 +7,16 @@ from module.base.timer import Timer
from module.base.utils import get_color
from module.config.utils import get_server_next_update
from module.logger.logger import logger
from module.ocr.ocr import Digit, Duration
from module.ocr.ocr import Digit, Duration, Ocr
from module.ocr.utils import split_and_pair_buttons
from module.ui.scroll import Scroll
from module.ui.switch import Switch
from tasks.base.assets.assets_base_page import BATTLE_PASS_CHECK, MAIN_GOTO_BATTLE_PASS
from tasks.base.assets.assets_base_popup import GET_REWARD
from tasks.base.page import page_battle_pass, page_main
from tasks.base.ui import UI
from tasks.battle_pass.assets.assets_battle_pass import *
from tasks.battle_pass.keywords import KEYWORD_BATTLE_PASS_TAB
from tasks.battle_pass.keywords import *
SWITCH_BATTLE_PASS_TAB = Switch('BattlePassTab', is_selector=True)
SWITCH_BATTLE_PASS_TAB.add_state(
@ -28,6 +31,48 @@ SWITCH_BATTLE_PASS_TAB.add_state(
)
class BattlePassMissionTab(Switch):
def get(self, main):
"""
Args:
main (ModuleBase):
Returns:
str: state name or 'unknown'.
"""
for data in self.state_list:
if main.image_color_count(data['check_button'], color=(250, 233, 153)):
return data['state']
return 'unknown'
SWITCH_BATTLE_PASS_MISSION_TAB = BattlePassMissionTab('BattlePassMissionTab', is_selector=True)
SWITCH_BATTLE_PASS_MISSION_TAB.add_state(
KEYWORD_BATTLE_PASS_MISSION_TAB.Today_Missions,
check_button=TODAY_MISSION_CLICK,
click_button=TODAY_MISSION_CLICK
)
SWITCH_BATTLE_PASS_MISSION_TAB.add_state(
KEYWORD_BATTLE_PASS_MISSION_TAB.This_Week_Missions,
check_button=WEEK_MISSION_CLICK,
click_button=WEEK_MISSION_CLICK
)
SWITCH_BATTLE_PASS_MISSION_TAB.add_state(
KEYWORD_BATTLE_PASS_MISSION_TAB.This_Period_Missions,
check_button=PERIOD_MISSION_CLICK,
click_button=PERIOD_MISSION_CLICK
)
class BattlePassQuestOcr(Ocr):
def after_process(self, result):
result = super().after_process(result)
if self.lang == 'ch':
result = re.sub("[jJ]", "", result)
return result
class BattlePassUI(UI):
MAX_LEVEL = 50
@ -46,7 +91,7 @@ class BattlePassUI(UI):
logger.info('Rewards tab loaded')
break
def _battle_pass_wait_missions_loaded(self, skip_first_screenshot=True):
def _battle_pass_wait_missions_loaded(self, skip_first_screenshot=True, has_scroll=True):
timeout = Timer(2, count=4).start()
while 1:
if skip_first_screenshot:
@ -57,6 +102,11 @@ class BattlePassUI(UI):
if timeout.reached():
logger.warning('Wait missions tab loaded timeout')
break
if has_scroll:
if self.appear(MISSION_PAGE_SCROLL):
logger.info('Rewards tab loaded')
break
else:
color = get_color(self.device.image, MISSIONS_LOADED.area)
if np.mean(color) > 128:
logger.info('Missions tab loaded')
@ -77,10 +127,21 @@ class BattlePassUI(UI):
if SWITCH_BATTLE_PASS_TAB.set(state, main=self):
logger.info(f'Tab goto {state}, wait until loaded')
if state == KEYWORD_BATTLE_PASS_TAB.Missions:
self._battle_pass_wait_missions_loaded()
self._battle_pass_wait_missions_loaded(has_scroll=False)
if state == KEYWORD_BATTLE_PASS_TAB.Rewards:
self._battle_pass_wait_rewards_loaded()
def battle_pass_mission_tab_goto(self, state: KEYWORD_BATTLE_PASS_MISSION_TAB):
self.battle_pass_goto(KEYWORD_BATTLE_PASS_TAB.Missions)
if SWITCH_BATTLE_PASS_MISSION_TAB.set(state, main=self):
logger.info(f'Tab goto {state}, wait until loaded')
if state == KEYWORD_BATTLE_PASS_MISSION_TAB.Today_Missions:
self._battle_pass_wait_missions_loaded(has_scroll=False)
if state == KEYWORD_BATTLE_PASS_MISSION_TAB.This_Week_Missions:
self._battle_pass_wait_missions_loaded()
if state == KEYWORD_BATTLE_PASS_MISSION_TAB.This_Period_Missions:
self._battle_pass_wait_missions_loaded()
def handle_choose_gifts(self, interval=5):
"""
Popup when you have purchase Nameless Glory
@ -181,6 +242,43 @@ class BattlePassUI(UI):
self._claim_rewards()
return current_level
def ocr_single_page(self):
"""
Returns incomplete quests only
"""
ocr = BattlePassQuestOcr(OCR_BATTLE_PASS_QUEST)
results = ocr.matched_ocr(self.device.image, [BattlePassQuest, BattlePassQuestState])
def completed_state(state):
return state != KEYWORD_BATTLE_PASS_QUEST_STATE.Navigate
return [incomplete_quest for incomplete_quest, _ in
split_and_pair_buttons(results, split_func=completed_state, relative_area=(0, 0, 800, 100))]
def battle_pass_quests_recognition(self, page: KEYWORD_BATTLE_PASS_MISSION_TAB,
has_scroll=True) -> list[BattlePassQuest]:
"""
Args:
page:
has_scroll: need to scroll to recognize all quests
Returns:
"""
logger.info(f"Recognizing battle pass quests at {page}")
self.battle_pass_mission_tab_goto(page)
if not has_scroll:
results = self.ocr_single_page()
else:
scroll = Scroll(MISSION_PAGE_SCROLL, color=(198, 198, 198))
scroll.set_top(main=self)
results = self.ocr_single_page()
while not scroll.at_bottom(main=self):
scroll.next_page(main=self)
results += [result for result in self.ocr_single_page() if result not in results]
results = [result.matched_keyword for result in results]
return results
def has_battle_pass_entrance(self, skip_first_screenshot=True):
"""
Pages:

View File

@ -1,2 +1,6 @@
import tasks.battle_pass.keywords.mission_tab as KEYWORD_BATTLE_PASS_MISSION_TAB
import tasks.battle_pass.keywords.quest as KEYWORD_BATTLE_PASS_QUEST
import tasks.battle_pass.keywords.quest_state as KEYWORD_BATTLE_PASS_QUEST_STATE
import tasks.battle_pass.keywords.tab as KEYWORD_BATTLE_PASS_TAB
from tasks.battle_pass.keywords.classes import BattlePassTab
from tasks.battle_pass.keywords.classes import BattlePassTab, BattlePassMissionTab, BattlePassQuest, \
BattlePassQuestState

View File

@ -1,3 +1,4 @@
import re
from dataclasses import dataclass
from typing import ClassVar
@ -7,3 +8,25 @@ from module.ocr.keyword import Keyword
@dataclass(repr=False)
class BattlePassTab(Keyword):
instances: ClassVar = {}
@dataclass(repr=False)
class BattlePassMissionTab(Keyword):
instances: ClassVar = {}
@dataclass(repr=False)
class BattlePassQuest(Keyword):
instances: ClassVar = {}
@classmethod
def _compare(cls, name, keyword):
def remove_digit(text):
return re.sub(r"\d", "", text)
return remove_digit(name) == remove_digit(keyword)
@dataclass(repr=False)
class BattlePassQuestState(Keyword):
instances: ClassVar = {}

View File

@ -0,0 +1,29 @@
from .classes import BattlePassMissionTab
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Today_Missions = BattlePassMissionTab(
id=1,
name='Today_Missions',
cn='本日任务',
cht='本日任務',
en="Today's Missions",
jp='本日のクエスト',
)
This_Week_Missions = BattlePassMissionTab(
id=2,
name='This_Week_Missions',
cn='本周任务',
cht='本週任務',
en="This Week's Missions",
jp='今週のクエスト',
)
This_Period_Missions = BattlePassMissionTab(
id=3,
name='This_Period_Missions',
cn='本期任务',
cht='本期任務',
en="This Period's Missions",
jp='今期のクエスト',
)

View File

@ -0,0 +1,93 @@
from .classes import BattlePassQuest
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Log_in_to_the_game = BattlePassQuest(
id=1,
name='Log_in_to_the_game',
cn='登录游戏',
cht='登入遊戲',
en='Log in to the game',
jp='ゲームにログインする',
)
Consume_1_Trailblaze_Power = BattlePassQuest(
id=2,
name='Consume_1_Trailblaze_Power',
cn='累计消耗1点开拓力',
cht='累積消耗1點開拓力',
en='Consume 1 Trailblaze Power',
jp='累計で開拓力×1を消費する',
)
Dispatch_1_assignments = BattlePassQuest(
id=3,
name='Dispatch_1_assignments',
cn='派遣1次委托',
cht='派遣1次委託',
en='Dispatch 1 assignment(s)',
jp='依頼に1回派遣する',
)
Reach_500_on_Daily_Training_Activity = BattlePassQuest(
id=4,
name='Reach_500_on_Daily_Training_Activity',
cn='每日实训活跃度达到500',
cht='每日實訓活躍度達到500',
en='Reach 500 on Daily Training Activity',
jp='デイリー訓練のアクティブ度が500に到達する',
)
Complete_Simulated_Universe_1_times = BattlePassQuest(
id=5,
name='Complete_Simulated_Universe_1_times',
cn='完成1次「模拟宇宙」',
cht='完成1次「模擬宇宙」',
en='Complete Simulated Universe 1 time(s)',
jp='「模擬宇宙」を1回クリアする',
)
Clear_Calyx_1_times = BattlePassQuest(
id=6,
name='Clear_Calyx_1_times',
cn='完成1次「拟造花萼」',
cht='完成1次「擬造花萼」',
en='Clear Calyx 1 time(s)',
jp='「疑似花萼」を1回クリアする',
)
Complete_Echo_of_War_1_times = BattlePassQuest(
id=7,
name='Complete_Echo_of_War_1_times',
cn='完成1次「历战余响」',
cht='完成1次「歷戰餘響」',
en='Complete Echo of War 1 time(s)',
jp='「歴戦余韻」を1回クリアする',
)
Use_300000_credits = BattlePassQuest(
id=8,
name='Use_300000_credits',
cn='累计消耗30万信用点',
cht='累積消耗30萬信用點',
en='Use 300,000 credits',
jp='累計で信用ポイント×30万を消費する',
)
Synthesize_Consumables_1_times = BattlePassQuest(
id=9,
name='Synthesize_Consumables_1_times',
cn='累计合成消耗品1次',
cht='累積合成消耗品1次',
en='Synthesize Consumables 1 time(s)',
jp='消耗品を累計1回合成する',
)
Clear_Stagnant_Shadow_1_times = BattlePassQuest(
id=10,
name='Clear_Stagnant_Shadow_1_times',
cn='完成1次「凝滞虚影」',
cht='完成1次「凝滯虛影」',
en='Clear Stagnant Shadow 1 time(s)',
jp='「凝結虚影」を1回クリアする',
)
Clear_Cavern_of_Corrosion_1_times = BattlePassQuest(
id=11,
name='Clear_Cavern_of_Corrosion_1_times',
cn='完成1次「侵蚀隧洞」',
cht='完成1次「侵蝕隧洞」',
en='Clear Cavern of Corrosion 1 time(s)',
jp='「侵蝕トンネル」を1回クリアする',
)

View File

@ -0,0 +1,21 @@
from .classes import BattlePassQuestState
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Claim = BattlePassQuestState(
id=1,
name='Claim',
cn='领取',
cht='領取',
en='Claim',
jp='受取',
)
Navigate = BattlePassQuestState(
id=2,
name='Navigate',
cn='追踪',
cht='追蹤',
en='Navigate',
jp='追跡',
)

0
tasks/rogue/selector.py Normal file
View File