Add: Plane switchings in page_map

This commit is contained in:
LmeSzinc 2023-07-02 15:33:30 +08:00
parent c2712ab146
commit 53d6bd818f
14 changed files with 557 additions and 17 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -119,37 +119,51 @@ class KeywordExtract:
def load_keywords(self, keywords: list[str | int], lang='cn'):
text_map = self.text_map[lang]
self.keywords_id = [text_map.find(keyword)[0] for keyword in keywords]
self.keywords_id = [keyword for keyword in self.keywords_id if keyword != 0]
keywords_id = [text_map.find(keyword)[0] for keyword in keywords]
self.keywords_id = [keyword for keyword in keywords_id if keyword != 0]
def clear_keywords(self):
self.keywords_id = []
def write_keywords(
self,
keyword_class,
output_file: str,
output_file: str = '',
text_convert=text_to_variable,
generator: CodeGenerator = None
):
"""
Args:
keyword_class:
output_file:
text_convert:
generator: Reuse an existing code generator
"""
gen = CodeGenerator()
gen.Import(f"""
from .classes import {keyword_class}
""")
gen.CommentAutoGenerage('dev_tools.keyword_extract')
if generator is None:
gen = CodeGenerator()
gen.Import(f"""
from .classes import {keyword_class}
""")
gen.CommentAutoGenerage('dev_tools.keyword_extract')
else:
gen = generator
last_id = getattr(gen, 'last_id', 0)
for index, keyword in enumerate(self.keywords_id):
_, name = self.find_keyword(keyword, lang='en')
name = text_convert(replace_templates(name))
with gen.Object(key=name, object_class=keyword_class):
gen.ObjectAttr(key='id', value=index + 1)
gen.ObjectAttr(key='id', value=index + last_id + 1)
gen.ObjectAttr(key='name', value=name)
for lang in UI_LANGUAGES:
gen.ObjectAttr(key=lang, value=replace_templates(self.find_keyword(keyword, lang=lang)[1]))
gen.last_id = index + last_id + 1
print(f'Write {output_file}')
gen.write(output_file)
if output_file:
print(f'Write {output_file}')
gen.write(output_file)
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'))
@ -176,6 +190,7 @@ class KeywordExtract:
print(f'Write {output_file}')
gen.write(output_file)
self.clear_keywords()
def generate_assignment_keywords(self):
KeywordFromFile = namedtuple('KeywordFromFile', ('file', 'class_name', 'output_file'))
@ -187,6 +202,29 @@ class KeywordExtract:
self.load_keywords(deep_get(data, 'Name.Hash') for data in read_file(file).values())
self.write_keywords(keyword_class=keyword.class_name, output_file=keyword.output_file)
def generate_map_planes(self):
planes = {
'Herta': ['观景车厢', '主控舱段', '基座舱段', '收容舱段', '支援舱段'],
'Jarilo': ['行政区', '城郊雪原', '边缘通路', '铁卫禁区', '残响回廊', '永冬岭',
'磐岩镇', '大矿区', '铆钉镇', '机械聚落'],
'Luofu': ['星槎海中枢', '长乐天', '流云渡', '迴星港', '太卜司', '工造司'],
}
def text_convert(world_):
def text_convert_wrapper(name):
name = text_to_variable(name).replace('_', '')
name = f'{world_}_{name}'
return name
return text_convert_wrapper
gen = None
for world, plane in planes.items():
self.load_keywords(plane)
gen = self.write_keywords(keyword_class='MapPlane', output_file='',
text_convert=text_convert(world), generator=gen)
gen.write('./tasks/map/keywords/plane.py')
def generate(self):
self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响', '忘却之庭'])
self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
@ -205,6 +243,7 @@ class KeywordExtract:
self.write_keywords(keyword_class='BattlePassTab', output_file='./tasks/battle_pass/keywords/tab.py')
self.generate_assignment_keywords()
self.generate_forgotten_hall_stages()
self.generate_map_planes()
if __name__ == '__main__':

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):

View File

@ -190,20 +190,32 @@ class DraggableList:
return False
def select_row(self, row: Keyword, main: ModuleBase, skip_first_screenshot=True):
def get_selected_row(self, main: ModuleBase) -> Optional[OcrResultButton]:
"""
`load_rows()` must be called before `get_selected_row()`.
"""
for row in self.cur_buttons:
if self.is_row_selected(row, main=main):
return row
return None
def select_row(self, row: Keyword, main: ModuleBase, insight=True, skip_first_screenshot=True):
"""
Args:
row:
main:
insight: If call `insight_row()` before selecting
skip_first_screenshot:
Returns:
If success
"""
result = self.insight_row(
row, main=main, skip_first_screenshot=skip_first_screenshot)
if not result:
return False
if insight:
result = self.insight_row(
row, main=main, skip_first_screenshot=skip_first_screenshot)
if not result:
return False
logger.info(f'Select row: {row}')
skip_first_screenshot = True
interval = Timer(5)

View File

@ -1,3 +1,4 @@
from module.base.button import ButtonWrapper
from module.base.decorator import run_once
from module.base.timer import Timer
from module.exception import GameNotRunningError, GamePageUnknownError
@ -203,6 +204,62 @@ class UI(PopupHandler, StateMixin):
self.device.click(button)
retry.reset()
def ui_click(
self,
click_button,
check_button,
appear_button=None,
additional=None,
retry_wait=5,
skip_first_screenshot=True,
):
"""
Args:
click_button (ButtonWrapper):
check_button (ButtonWrapper, callable, list[ButtonWrapper], tuple[ButtonWrapper]):
appear_button (ButtonWrapper, callable, list[ButtonWrapper], tuple[ButtonWrapper]):
additional (callable):
retry_wait (int, float):
skip_first_screenshot (bool):
"""
if appear_button is None:
appear_button = click_button
logger.info(f'UI click: {appear_button} -> {check_button}')
def process_appear(button):
if isinstance(button, ButtonWrapper):
return self.appear(button)
elif callable(button):
return button()
elif isinstance(button, (list, tuple)):
for b in button:
if self.appear(b):
return True
return False
else:
return self.appear(button)
click_timer = Timer(retry_wait, count=retry_wait // 0.5)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
# End
if process_appear(check_button):
break
# Click
if click_timer.reached():
if process_appear(appear_button):
self.device.click(click_button)
click_timer.reset()
continue
if additional is not None:
if additional():
continue
def is_in_main(self):
return self.appear(page_main.check_button)

View File

@ -0,0 +1,55 @@
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 ```
OCR_PLANE = ButtonWrapper(
name='OCR_PLANE',
share=Button(
file='./assets/share/map/bigmap/OCR_PLANE.png',
area=(872, 138, 1252, 688),
search=(852, 118, 1272, 708),
color=(199, 199, 199),
button=(872, 138, 1252, 688),
),
)
PLANE_SCROLL = ButtonWrapper(
name='PLANE_SCROLL',
share=Button(
file='./assets/share/map/bigmap/PLANE_SCROLL.png',
area=(1252, 138, 1256, 688),
search=(1232, 118, 1276, 708),
color=(103, 103, 103),
button=(1252, 138, 1256, 688),
),
)
WORLD_HERTA = ButtonWrapper(
name='WORLD_HERTA',
share=Button(
file='./assets/share/map/bigmap/WORLD_HERTA.png',
area=(201, 332, 273, 394),
search=(181, 312, 293, 414),
color=(115, 122, 130),
button=(201, 332, 273, 394),
),
)
WORLD_JARILO = ButtonWrapper(
name='WORLD_JARILO',
share=Button(
file='./assets/share/map/bigmap/WORLD_JARILO.png',
area=(638, 138, 706, 203),
search=(618, 118, 726, 223),
color=(104, 113, 121),
button=(638, 138, 706, 203),
),
)
WORLD_LUOFU = ButtonWrapper(
name='WORLD_LUOFU',
share=Button(
file='./assets/share/map/bigmap/WORLD_LUOFU.png',
area=(983, 549, 1051, 612),
search=(963, 529, 1071, 632),
color=(103, 121, 105),
button=(983, 549, 1051, 612),
),
)

164
tasks/map/bigmap/plane.py Normal file
View File

@ -0,0 +1,164 @@
import re
from typing import Optional
from module.base.base import ModuleBase
from module.exception import ScriptError
from module.logger import logger
from module.ocr.ocr import Ocr, OcrResultButton
from module.ui.draggable_list import DraggableList
from module.ui.scroll import Scroll
from tasks.base.page import page_map, page_world
from tasks.base.ui import UI
from tasks.map.assets.assets_map_bigmap import *
from tasks.map.keywords import MapPlane, KEYWORDS_MAP_PLANE
from module.base.timer import Timer
def world_entrance(plane: MapPlane) -> ButtonWrapper:
if plane.is_HertaSpaceStation:
return WORLD_HERTA
if plane.is_JariloVI:
return WORLD_JARILO
if plane.is_Luofu:
return WORLD_LUOFU
raise ScriptError(f'world_entrance() got unknown plane: {plane}')
class OcrMapPlane(Ocr):
merge_thres_y = 20
def after_process(self, result):
result = super().after_process(result)
result = re.sub(r'[+→★“”,.,、。]', '', result).strip()
if self.lang == 'ch':
result = result.replace('迎星港', '迴星港')
return result
class DraggablePlaneList(DraggableList):
def is_row_selected(self, button: OcrResultButton, main: ModuleBase) -> bool:
# Items have an animation to be selected, check if the rightmost become black.
x = OCR_PLANE.area[2]
area = (x - 20, button.area[1], x, button.area[3])
if main.image_color_count(area, color=(40, 40, 40), threshold=221, count=100):
return True
return False
SCROLL_PLANE = Scroll(PLANE_SCROLL, color=(67, 67, 67), name='SCROLL_PLANE')
PLANE_LIST = DraggablePlaneList('PlaneList', keyword_class=MapPlane, ocr_class=OcrMapPlane, search_button=OCR_PLANE)
class BigmapPlane(UI):
def _bigmap_world_set(self, plane: MapPlane):
"""
Pages:
in: Any
out: page_map
"""
self.ui_goto(page_world)
self.ui_click(appear_button=page_world.check_button,
click_button=world_entrance(plane),
check_button=page_map.check_button)
def _bigmap_get_current_plane(self) -> Optional[MapPlane]:
"""
Get current plane.
After entering page_map, the current plane is selected by default.
Pages:
in: page_map
"""
PLANE_LIST.load_rows(main=self)
selected = PLANE_LIST.get_selected_row(main=self)
if selected is None:
return None
else:
return selected.matched_keyword
def _bigmap_get_current_plane_wrapped(self) -> MapPlane:
"""
Get current plane with reties.
"""
for n in range(2):
self.ui_ensure(page_map)
# Wait select animation
timeout = Timer(2).start()
skip_first_screenshot = True
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
current = self._bigmap_get_current_plane()
if current is not None:
return current
if timeout.reached():
logger.warning('No plane was selected')
if n == 0:
# Nothing selected, probably because it has been switched to page_map before running.
# Exit and re-enter should fix it.
self.ui_goto_main()
break
logger.error('Cannot find current plane, return the first plane in list instead')
try:
first = PLANE_LIST.cur_buttons[0].matched_keyword
if first is not None:
return first
else:
return KEYWORDS_MAP_PLANE.Herta_ParlorCar
except IndexError:
return KEYWORDS_MAP_PLANE.Herta_ParlorCar
def bigmap_plane_set(self, plane: MapPlane):
"""
Set map ti given plane.
Args:
plane:
Returns:
bool: If success.
Pages:
in: Any
out: page_map
"""
logger.info(f'Bigmap plane set: {plane}')
current = self._bigmap_get_current_plane_wrapped()
logger.attr('CurrentPlane', current)
if current.world != plane.world:
logger.info(f'Switch to world {plane.world}')
self._bigmap_world_set(plane)
PLANE_LIST.load_rows(main=self)
if plane.is_HertaSpaceStation:
PLANE_LIST.select_row(plane, main=self, insight=False)
return True
elif plane.is_JariloVI:
if plane in [
KEYWORDS_MAP_PLANE.Jarilo_AdministrativeDistrict,
KEYWORDS_MAP_PLANE.Jarilo_OutlyingSnowPlains,
KEYWORDS_MAP_PLANE.Jarilo_BackwaterPass,
KEYWORDS_MAP_PLANE.Jarilo_SilvermaneGuardRestrictedZone,
KEYWORDS_MAP_PLANE.Jarilo_CorridorofFadingEchoes,
KEYWORDS_MAP_PLANE.Jarilo_EverwinterHill,
]:
if SCROLL_PLANE.set_top(main=self):
PLANE_LIST.load_rows(main=self)
else:
if SCROLL_PLANE.set_bottom(main=self):
PLANE_LIST.load_rows(main=self)
PLANE_LIST.select_row(plane, main=self, insight=False)
return True
elif plane.is_Luofu:
PLANE_LIST.select_row(plane, main=self, insight=False)
return True
logger.error(f'Goto plane {plane} is not supported')
return False

View File

@ -0,0 +1,2 @@
import tasks.map.keywords.plane as KEYWORDS_MAP_PLANE
from tasks.map.keywords.classes import MapPlane

View File

@ -0,0 +1,38 @@
from dataclasses import dataclass
from functools import cached_property
from typing import ClassVar
from module.ocr.keyword import Keyword
@dataclass(repr=False)
class MapPlane(Keyword):
instances: ClassVar = {}
@cached_property
def world(self) -> str:
"""
Returns:
str: World name. Note that "Parlor Car" is considered as a plane of Herta.
"Herta" for Herta Space Station
"Jarilo" for Jarilo-VI
"Luofu" for The Xianzhou Luofu
"" for unknown
"""
for world in ['Herta', 'Jarilo', 'Luofu']:
if self.name.startswith(world):
return world
return ''
@cached_property
def is_HertaSpaceStation(self):
return self.world == 'Herta'
@cached_property
def is_JariloVI(self):
return self.world == 'Jarilo'
@cached_property
def is_Luofu(self):
return self.world == 'Luofu'

173
tasks/map/keywords/plane.py Normal file
View File

@ -0,0 +1,173 @@
from .classes import MapPlane
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Herta_ParlorCar = MapPlane(
id=1,
name='Herta_ParlorCar',
cn='观景车厢',
cht='觀景車廂',
en='Parlor Car',
jp='列車のラウンジ',
)
Herta_MasterControlZone = MapPlane(
id=2,
name='Herta_MasterControlZone',
cn='主控舱段',
cht='主控艙段',
en='Master Control Zone',
jp='主制御部分',
)
Herta_BaseZone = MapPlane(
id=3,
name='Herta_BaseZone',
cn='基座舱段',
cht='基座艙段',
en='Base Zone',
jp='ベース部分',
)
Herta_StorageZone = MapPlane(
id=4,
name='Herta_StorageZone',
cn='收容舱段',
cht='收容艙段',
en='Storage Zone',
jp='収容部分',
)
Herta_SupplyZone = MapPlane(
id=5,
name='Herta_SupplyZone',
cn='支援舱段',
cht='支援艙段',
en='Supply Zone',
jp='サポート部分',
)
Jarilo_AdministrativeDistrict = MapPlane(
id=6,
name='Jarilo_AdministrativeDistrict',
cn='行政区',
cht='行政區',
en='Administrative District',
jp='行政区',
)
Jarilo_OutlyingSnowPlains = MapPlane(
id=7,
name='Jarilo_OutlyingSnowPlains',
cn='城郊雪原',
cht='城郊雪原',
en='Outlying Snow Plains',
jp='郊外雪原',
)
Jarilo_BackwaterPass = MapPlane(
id=8,
name='Jarilo_BackwaterPass',
cn='边缘通路',
cht='邊緣通道',
en='Backwater Pass',
jp='外縁通路',
)
Jarilo_SilvermaneGuardRestrictedZone = MapPlane(
id=9,
name='Jarilo_SilvermaneGuardRestrictedZone',
cn='铁卫禁区',
cht='鐵衛禁區',
en='Silvermane Guard Restricted Zone',
jp='シルバーメイン禁区',
)
Jarilo_CorridorofFadingEchoes = MapPlane(
id=10,
name='Jarilo_CorridorofFadingEchoes',
cn='残响回廊',
cht='殘響迴廊',
en='Corridor of Fading Echoes',
jp='残響回廊',
)
Jarilo_EverwinterHill = MapPlane(
id=11,
name='Jarilo_EverwinterHill',
cn='永冬岭',
cht='永冬嶺',
en='Everwinter Hill',
jp='常冬峰',
)
Jarilo_BoulderTown = MapPlane(
id=12,
name='Jarilo_BoulderTown',
cn='磐岩镇',
cht='磐岩鎮',
en='Boulder Town',
jp='ボルダータウン',
)
Jarilo_GreatMine = MapPlane(
id=13,
name='Jarilo_GreatMine',
cn='大矿区',
cht='大礦區',
en='Great Mine',
jp='大鉱区',
)
Jarilo_RivetTown = MapPlane(
id=14,
name='Jarilo_RivetTown',
cn='铆钉镇',
cht='鉚釘鎮',
en='Rivet Town',
jp='リベットタウン',
)
Jarilo_RobotSettlement = MapPlane(
id=15,
name='Jarilo_RobotSettlement',
cn='机械聚落',
cht='機械聚落',
en='Robot Settlement',
jp='機械集落',
)
Luofu_CentralStarskiffHaven = MapPlane(
id=16,
name='Luofu_CentralStarskiffHaven',
cn='星槎海中枢',
cht='星槎海中樞',
en='Central Starskiff Haven',
jp='星槎海中枢',
)
Luofu_ExaltingSanctum = MapPlane(
id=17,
name='Luofu_ExaltingSanctum',
cn='长乐天',
cht='長樂天',
en='Exalting Sanctum',
jp='長楽天',
)
Luofu_Cloudford = MapPlane(
id=18,
name='Luofu_Cloudford',
cn='流云渡',
cht='流雲渡',
en='Cloudford',
jp='流雲渡し',
)
Luofu_StargazerNavalia = MapPlane(
id=19,
name='Luofu_StargazerNavalia',
cn='迴星港',
cht='迴星港',
en='Stargazer Navalia',
jp='廻星港',
)
Luofu_DivinationCommission = MapPlane(
id=20,
name='Luofu_DivinationCommission',
cn='太卜司',
cht='太卜司',
en='Divination Commission',
jp='太卜司',
)
Luofu_ArtisanshipCommission = MapPlane(
id=21,
name='Luofu_ArtisanshipCommission',
cn='工造司',
cht='工造司',
en='Artisanship Commission',
jp='工造司',
)