Add: character and combat support (#38)
* Add: combat support * Fix: team_set logic when using support in combat * Add: Support character selection * Upd: optimize support character get logic * Fix: bug in get_character_by_name * Upd: use keywords to generate support config * Fix: Correct comment language to English * Fix: Remove debug statements * Upd: Improve UI icons and positioning * Upd: Refactor support * Upd: character keyword extract * Upd:Character icon * Upd: Character icon * Upd: Character icon * Upd: Optimize the parameter in SupportListScroll * Upd: Refactor support * Fix: Bug in Dungeon * Fix: Corrected comments in Combat and Support * Upd: Modified parameter * Refactor: Support logic * Refactor: Support logic * Refactor: Support logic * Upd: Character icon * Upd: Character icon * Refactor: Support logic * Refactor: Support logic
BIN
assets/character/Arlan.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/character/Asta.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/character/Bailu.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/Bronya.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/CaelumtheDestruction.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/CaelumthePreservation.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/character/Clara.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/DanHeng.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/character/Gepard.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/Herta.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/character/Himeko.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/Hook.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/JingYuan.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/character/Luocha.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/March7th.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/Natasha.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/character/Pela.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/Qingque.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/Sampo.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/character/Seele.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/Serval.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/SilverWolf.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/character/StelletheDestruction.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/StellethePreservation.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/Sushang.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/character/Tingyun.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/Welt.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/character/Yanqing.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/character/Yukong.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/share/combat/support/COMBAT_SUPPORT_ADD.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
assets/share/combat/support/COMBAT_SUPPORT_LIST.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/share/combat/support/COMBAT_SUPPORT_LIST_GRID.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
assets/share/combat/support/COMBAT_SUPPORT_LIST_SCROLL.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
assets/share/combat/support/COMBAT_SUPPORT_SELECTED.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/share/combat/team/COMBAT_TEAM_DISMISSSUPPORT.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/share/combat/team/COMBAT_TEAM_SUPPORT.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
@ -42,7 +42,9 @@
|
|||||||
"Dungeon": {
|
"Dungeon": {
|
||||||
"Name": "Calyx_Golden_Treasures",
|
"Name": "Calyx_Golden_Treasures",
|
||||||
"NameAtDoubleCalyx": "Calyx_Golden_Treasures",
|
"NameAtDoubleCalyx": "Calyx_Golden_Treasures",
|
||||||
"Team": 1
|
"Team": 1,
|
||||||
|
"Support": "when_daily",
|
||||||
|
"SupportCharacter": "FirstCharacter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DailyQuest": {
|
"DailyQuest": {
|
||||||
|
@ -25,7 +25,16 @@ def dungeon_name(name: str) -> str:
|
|||||||
name = re.sub('Bud_of_(.*)', r'Calyx_Crimson_\1', name).replace('Calyx_Crimson_Calyx_Crimson_', 'Calyx_Crimson_')
|
name = re.sub('Bud_of_(.*)', r'Calyx_Crimson_\1', name).replace('Calyx_Crimson_Calyx_Crimson_', 'Calyx_Crimson_')
|
||||||
name = re.sub('Shape_of_(.*)', r'Stagnant_Shadow_\1', name)
|
name = re.sub('Shape_of_(.*)', r'Stagnant_Shadow_\1', name)
|
||||||
if name in ['Destructions_Beginning', 'End_of_the_Eternal_Freeze']:
|
if name in ['Destructions_Beginning', 'End_of_the_Eternal_Freeze']:
|
||||||
name = 'Echo_of_War_' + name
|
name = f'Echo_of_War_{name}'
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
nickname_count = 0
|
||||||
|
|
||||||
|
|
||||||
|
def character_name(name: str) -> str:
|
||||||
|
name = text_to_variable(name)
|
||||||
|
name = re.sub('_', '', name)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
@ -44,6 +53,7 @@ class TextMap:
|
|||||||
data = {}
|
data = {}
|
||||||
for id_, text in read_file(file).items():
|
for id_, text in read_file(file).items():
|
||||||
text = text.replace('\u00A0', '')
|
text = text.replace('\u00A0', '')
|
||||||
|
text = text.replace(r'{NICKNAME}', 'Trailblazer')
|
||||||
data[int(id_)] = text
|
data[int(id_)] = text
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -177,6 +187,18 @@ class KeywordExtract:
|
|||||||
quest_keywords = [self.text_map[lang].find(quest_hash)[1] for quest_hash in quests_hash]
|
quest_keywords = [self.text_map[lang].find(quest_hash)[1] for quest_hash in quests_hash]
|
||||||
self.load_keywords(quest_keywords, lang)
|
self.load_keywords(quest_keywords, lang)
|
||||||
|
|
||||||
|
def load_character_name_keywords(self, lang='en'):
|
||||||
|
file_name = 'ItemConfigAvatarPlayerIcon.json'
|
||||||
|
path = os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', file_name)
|
||||||
|
character_data = read_file(path)
|
||||||
|
characters_hash = [character_data[key]["ItemName"]["Hash"] for key in character_data]
|
||||||
|
|
||||||
|
text_map = self.text_map[lang]
|
||||||
|
keywords_id = sorted(
|
||||||
|
{text_map.find(keyword)[1] for keyword in characters_hash}
|
||||||
|
)
|
||||||
|
self.load_keywords(keywords_id, lang)
|
||||||
|
|
||||||
def generate_forgotten_hall_stages(self):
|
def generate_forgotten_hall_stages(self):
|
||||||
keyword_class = "ForgottenHallStage"
|
keyword_class = "ForgottenHallStage"
|
||||||
output_file = './tasks/forgotten_hall/keywords/stage.py'
|
output_file = './tasks/forgotten_hall/keywords/stage.py'
|
||||||
@ -230,6 +252,11 @@ class KeywordExtract:
|
|||||||
text_convert=text_convert(world), generator=gen)
|
text_convert=text_convert(world), generator=gen)
|
||||||
gen.write('./tasks/map/keywords/plane.py')
|
gen.write('./tasks/map/keywords/plane.py')
|
||||||
|
|
||||||
|
def generate_character_keywords(self):
|
||||||
|
self.load_character_name_keywords()
|
||||||
|
self.write_keywords(keyword_class='CharacterList', output_file='./tasks/character/keywords/character_list.py',
|
||||||
|
text_convert=character_name)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响', '忘却之庭'])
|
self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响', '忘却之庭'])
|
||||||
self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
|
self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
|
||||||
@ -249,6 +276,7 @@ class KeywordExtract:
|
|||||||
self.generate_assignment_keywords()
|
self.generate_assignment_keywords()
|
||||||
self.generate_forgotten_hall_stages()
|
self.generate_forgotten_hall_stages()
|
||||||
self.generate_map_planes()
|
self.generate_map_planes()
|
||||||
|
self.generate_character_keywords()
|
||||||
self.load_keywords(['养成材料', '光锥', '遗器', '其他材料', '消耗品', '任务', '贵重物'])
|
self.load_keywords(['养成材料', '光锥', '遗器', '其他材料', '消耗品', '任务', '贵重物'])
|
||||||
self.write_keywords(keyword_class='ItemTab', text_convert=lambda name: name.replace(' ', ''),
|
self.write_keywords(keyword_class='ItemTab', text_convert=lambda name: name.replace(' ', ''),
|
||||||
output_file='./tasks/item/keywords/tab.py')
|
output_file='./tasks/item/keywords/tab.py')
|
||||||
|
@ -227,6 +227,50 @@
|
|||||||
5,
|
5,
|
||||||
6
|
6
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"Support": {
|
||||||
|
"type": "select",
|
||||||
|
"value": "when_daily",
|
||||||
|
"option": [
|
||||||
|
"do_not_use",
|
||||||
|
"always_use",
|
||||||
|
"when_daily"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"SupportCharacter": {
|
||||||
|
"type": "select",
|
||||||
|
"value": "FirstCharacter",
|
||||||
|
"option": [
|
||||||
|
"FirstCharacter",
|
||||||
|
"Arlan",
|
||||||
|
"Asta",
|
||||||
|
"Bailu",
|
||||||
|
"Bronya",
|
||||||
|
"Clara",
|
||||||
|
"DanHeng",
|
||||||
|
"Gepard",
|
||||||
|
"Herta",
|
||||||
|
"Himeko",
|
||||||
|
"Hook",
|
||||||
|
"JingYuan",
|
||||||
|
"Kafka",
|
||||||
|
"Luocha",
|
||||||
|
"March7th",
|
||||||
|
"Natasha",
|
||||||
|
"Pela",
|
||||||
|
"Qingque",
|
||||||
|
"Sampo",
|
||||||
|
"Seele",
|
||||||
|
"Serval",
|
||||||
|
"SilverWolf",
|
||||||
|
"Sushang",
|
||||||
|
"Tingyun",
|
||||||
|
"TrailblazertheDestruction",
|
||||||
|
"TrailblazerthePreservation",
|
||||||
|
"Welt",
|
||||||
|
"Yanqing",
|
||||||
|
"Yukong"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -81,6 +81,13 @@ Dungeon:
|
|||||||
Team:
|
Team:
|
||||||
value: 1
|
value: 1
|
||||||
option: [ 1, 2, 3, 4, 5, 6 ]
|
option: [ 1, 2, 3, 4, 5, 6 ]
|
||||||
|
Support:
|
||||||
|
value: when_daily
|
||||||
|
option: [do_not_use, always_use, when_daily]
|
||||||
|
SupportCharacter:
|
||||||
|
# Options will be injected in config updater
|
||||||
|
value: FirstCharacter
|
||||||
|
option: [FirstCharacter, ]
|
||||||
|
|
||||||
Assignment:
|
Assignment:
|
||||||
Duration:
|
Duration:
|
||||||
|
@ -42,6 +42,8 @@ class GeneratedConfig:
|
|||||||
Dungeon_Name = 'Calyx_Golden_Treasures' # Calyx_Golden_Memories, Calyx_Golden_Aether, Calyx_Golden_Treasures, Calyx_Crimson_Destruction, Calyx_Crimson_Preservation, Calyx_Crimson_Hunt, Calyx_Crimson_Abundance, Calyx_Crimson_Erudition, Calyx_Crimson_Harmony, Calyx_Crimson_Nihility, Stagnant_Shadow_Quanta, Stagnant_Shadow_Gust, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Blaze, Stagnant_Shadow_Spike, Stagnant_Shadow_Rime, Stagnant_Shadow_Mirage, Stagnant_Shadow_Icicle, Stagnant_Shadow_Doom, Cavern_of_Corrosion_Path_of_Gelid_Wind, Cavern_of_Corrosion_Path_of_Jabbing_Punch, Cavern_of_Corrosion_Path_of_Drifting, Cavern_of_Corrosion_Path_of_Providence, Cavern_of_Corrosion_Path_of_Holy_Hymn, Cavern_of_Corrosion_Path_of_Conflagration
|
Dungeon_Name = 'Calyx_Golden_Treasures' # Calyx_Golden_Memories, Calyx_Golden_Aether, Calyx_Golden_Treasures, Calyx_Crimson_Destruction, Calyx_Crimson_Preservation, Calyx_Crimson_Hunt, Calyx_Crimson_Abundance, Calyx_Crimson_Erudition, Calyx_Crimson_Harmony, Calyx_Crimson_Nihility, Stagnant_Shadow_Quanta, Stagnant_Shadow_Gust, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Blaze, Stagnant_Shadow_Spike, Stagnant_Shadow_Rime, Stagnant_Shadow_Mirage, Stagnant_Shadow_Icicle, Stagnant_Shadow_Doom, Cavern_of_Corrosion_Path_of_Gelid_Wind, Cavern_of_Corrosion_Path_of_Jabbing_Punch, Cavern_of_Corrosion_Path_of_Drifting, Cavern_of_Corrosion_Path_of_Providence, Cavern_of_Corrosion_Path_of_Holy_Hymn, Cavern_of_Corrosion_Path_of_Conflagration
|
||||||
Dungeon_NameAtDoubleCalyx = 'Calyx_Golden_Treasures' # do_not_participate, Calyx_Golden_Memories, Calyx_Golden_Aether, Calyx_Golden_Treasures, Calyx_Crimson_Destruction, Calyx_Crimson_Preservation, Calyx_Crimson_Hunt, Calyx_Crimson_Abundance, Calyx_Crimson_Erudition, Calyx_Crimson_Harmony, Calyx_Crimson_Nihility
|
Dungeon_NameAtDoubleCalyx = 'Calyx_Golden_Treasures' # do_not_participate, Calyx_Golden_Memories, Calyx_Golden_Aether, Calyx_Golden_Treasures, Calyx_Crimson_Destruction, Calyx_Crimson_Preservation, Calyx_Crimson_Hunt, Calyx_Crimson_Abundance, Calyx_Crimson_Erudition, Calyx_Crimson_Harmony, Calyx_Crimson_Nihility
|
||||||
Dungeon_Team = 1 # 1, 2, 3, 4, 5, 6
|
Dungeon_Team = 1 # 1, 2, 3, 4, 5, 6
|
||||||
|
Dungeon_Support = 'when_daily' # do_not_use, always_use, when_daily
|
||||||
|
Dungeon_SupportCharacter = 'FirstCharacter' # FirstCharacter, Arlan, Asta, Bailu, Bronya, Clara, DanHeng, Gepard, Herta, Himeko, Hook, JingYuan, Kafka, Luocha, March7th, Natasha, Pela, Qingque, Sampo, Seele, Serval, SilverWolf, Sushang, Tingyun, TrailblazertheDestruction, TrailblazerthePreservation, Welt, Yanqing, Yukong
|
||||||
|
|
||||||
# Group `Assignment`
|
# Group `Assignment`
|
||||||
Assignment_Duration = 20 # 4, 8, 12, 20
|
Assignment_Duration = 20 # 4, 8, 12, 20
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import re
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from cached_property import cached_property
|
from cached_property import cached_property
|
||||||
|
|
||||||
from deploy.Windows.utils import DEPLOY_TEMPLATE, poor_yaml_read, poor_yaml_write
|
from deploy.Windows.utils import DEPLOY_TEMPLATE, poor_yaml_read, poor_yaml_write
|
||||||
from module.base.timer import timer
|
from module.base.timer import timer
|
||||||
from module.config.server import to_server, to_package, VALID_PACKAGE, VALID_CHANNEL_PACKAGE
|
from module.config.server import to_package, VALID_PACKAGE, VALID_CHANNEL_PACKAGE
|
||||||
from module.config.utils import *
|
from module.config.utils import *
|
||||||
|
|
||||||
CONFIG_IMPORT = '''
|
CONFIG_IMPORT = '''
|
||||||
@ -290,6 +289,16 @@ class ConfigGenerator:
|
|||||||
if value:
|
if value:
|
||||||
deep_set(new, keys=['Dungeon', 'NameAtDoubleCalyx', dungeon], value=value)
|
deep_set(new, keys=['Dungeon', 'NameAtDoubleCalyx', dungeon], value=value)
|
||||||
|
|
||||||
|
from tasks.character.keywords import CharacterList
|
||||||
|
ingame_lang = gui_lang_to_ingame_lang(lang)
|
||||||
|
characters = deep_get(self.argument, keys='Dungeon.SupportCharacter.option')
|
||||||
|
for character in CharacterList.instances.values():
|
||||||
|
if character.name in characters:
|
||||||
|
value = character.__getattribute__(ingame_lang)
|
||||||
|
if "Trailblazer" in value:
|
||||||
|
continue
|
||||||
|
deep_set(new, keys=['Dungeon', 'SupportCharacter', character.name], value=value)
|
||||||
|
|
||||||
# GUI i18n
|
# GUI i18n
|
||||||
for path, _ in deep_iter(self.gui, depth=2):
|
for path, _ in deep_iter(self.gui, depth=2):
|
||||||
group, key = path
|
group, key = path
|
||||||
@ -362,6 +371,12 @@ class ConfigGenerator:
|
|||||||
dungeons = [dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_daily_dungeon]
|
dungeons = [dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_daily_dungeon]
|
||||||
deep_set(self.argument, keys='Dungeon.Name.option', value=dungeons)
|
deep_set(self.argument, keys='Dungeon.Name.option', value=dungeons)
|
||||||
deep_set(self.args, keys='Dungeon.Dungeon.Name.option', value=dungeons)
|
deep_set(self.args, keys='Dungeon.Dungeon.Name.option', value=dungeons)
|
||||||
|
|
||||||
|
from tasks.character.keywords import CharacterList
|
||||||
|
characters = ['FirstCharacter'] + [character.name for character in CharacterList.instances.values()]
|
||||||
|
deep_set(self.argument, keys='Dungeon.SupportCharacter.option', value=characters)
|
||||||
|
deep_set(self.args, keys='Dungeon.Dungeon.SupportCharacter.option', value=characters)
|
||||||
|
|
||||||
dungeons = deep_get(self.argument, keys='Dungeon.NameAtDoubleCalyx.option')
|
dungeons = deep_get(self.argument, keys='Dungeon.NameAtDoubleCalyx.option')
|
||||||
dungeons += [dungeon.name for dungeon in DungeonList.instances.values()
|
dungeons += [dungeon.name for dungeon in DungeonList.instances.values()
|
||||||
if dungeon.is_Calyx_Golden or dungeon.is_Calyx_Crimson]
|
if dungeon.is_Calyx_Golden or dungeon.is_Calyx_Crimson]
|
||||||
|
@ -233,6 +233,46 @@
|
|||||||
"4": "4",
|
"4": "4",
|
||||||
"5": "5",
|
"5": "5",
|
||||||
"6": "6"
|
"6": "6"
|
||||||
|
},
|
||||||
|
"Support": {
|
||||||
|
"name": "Enable buddy support",
|
||||||
|
"help": "Whether to enable buddy support",
|
||||||
|
"do_not_use": "do_not_use",
|
||||||
|
"always_use": "always_use",
|
||||||
|
"when_daily": "when_daily"
|
||||||
|
},
|
||||||
|
"SupportCharacter": {
|
||||||
|
"name": "Dungeon.SupportCharacter.name",
|
||||||
|
"help": "Dungeon.SupportCharacter.help",
|
||||||
|
"FirstCharacter": "FirstCharacter",
|
||||||
|
"Arlan": "Arlan",
|
||||||
|
"Asta": "Asta",
|
||||||
|
"Bailu": "Bailu",
|
||||||
|
"Bronya": "Bronya",
|
||||||
|
"Clara": "Clara",
|
||||||
|
"DanHeng": "Dan Heng",
|
||||||
|
"Gepard": "Gepard",
|
||||||
|
"Herta": "Herta",
|
||||||
|
"Himeko": "Himeko",
|
||||||
|
"Hook": "Hook",
|
||||||
|
"JingYuan": "Jing Yuan",
|
||||||
|
"Kafka": "Kafka",
|
||||||
|
"Luocha": "Luocha",
|
||||||
|
"March7th": "March 7th",
|
||||||
|
"Natasha": "Natasha",
|
||||||
|
"Pela": "Pela",
|
||||||
|
"Qingque": "Qingque",
|
||||||
|
"Sampo": "Sampo",
|
||||||
|
"Seele": "Seele",
|
||||||
|
"Serval": "Serval",
|
||||||
|
"SilverWolf": "Silver Wolf",
|
||||||
|
"Sushang": "Sushang",
|
||||||
|
"Tingyun": "Tingyun",
|
||||||
|
"TrailblazertheDestruction": "Trailblazer: the Destruction",
|
||||||
|
"TrailblazerthePreservation": "Trailblazer: the Preservation",
|
||||||
|
"Welt": "Welt",
|
||||||
|
"Yanqing": "Yanqing",
|
||||||
|
"Yukong": "Yukong"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Assignment": {
|
"Assignment": {
|
||||||
|
@ -233,6 +233,46 @@
|
|||||||
"4": "4",
|
"4": "4",
|
||||||
"5": "5",
|
"5": "5",
|
||||||
"6": "6"
|
"6": "6"
|
||||||
|
},
|
||||||
|
"Support": {
|
||||||
|
"name": "Dungeon.Support.name",
|
||||||
|
"help": "Dungeon.Support.help",
|
||||||
|
"do_not_use": "do_not_use",
|
||||||
|
"always_use": "always_use",
|
||||||
|
"when_daily": "when_daily"
|
||||||
|
},
|
||||||
|
"SupportCharacter": {
|
||||||
|
"name": "Dungeon.SupportCharacter.name",
|
||||||
|
"help": "Dungeon.SupportCharacter.help",
|
||||||
|
"FirstCharacter": "FirstCharacter",
|
||||||
|
"Arlan": "アーラン",
|
||||||
|
"Asta": "アスター",
|
||||||
|
"Bailu": "白露",
|
||||||
|
"Bronya": "ブローニャ",
|
||||||
|
"Clara": "クラーラ",
|
||||||
|
"DanHeng": "丹恒",
|
||||||
|
"Gepard": "ジェパード",
|
||||||
|
"Herta": "ヘルタ",
|
||||||
|
"Himeko": "姫子",
|
||||||
|
"Hook": "フック",
|
||||||
|
"JingYuan": "景元",
|
||||||
|
"Kafka": "カフカ",
|
||||||
|
"Luocha": "羅刹",
|
||||||
|
"March7th": "三月なのか",
|
||||||
|
"Natasha": "ナターシャ",
|
||||||
|
"Pela": "ペラ",
|
||||||
|
"Qingque": "青雀",
|
||||||
|
"Sampo": "サンポ",
|
||||||
|
"Seele": "ゼーレ",
|
||||||
|
"Serval": "セーバル",
|
||||||
|
"SilverWolf": "銀狼",
|
||||||
|
"Sushang": "素裳",
|
||||||
|
"Tingyun": "停雲",
|
||||||
|
"TrailblazertheDestruction": "Trailblazer・壊滅",
|
||||||
|
"TrailblazerthePreservation": "Trailblazer・存護",
|
||||||
|
"Welt": "ヴェルト",
|
||||||
|
"Yanqing": "彦卿",
|
||||||
|
"Yukong": "御空"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Assignment": {
|
"Assignment": {
|
||||||
|
@ -233,6 +233,46 @@
|
|||||||
"4": "4",
|
"4": "4",
|
||||||
"5": "5",
|
"5": "5",
|
||||||
"6": "6"
|
"6": "6"
|
||||||
|
},
|
||||||
|
"Support": {
|
||||||
|
"name": "启用好友支援",
|
||||||
|
"help": "是否启用好友支援",
|
||||||
|
"do_not_use": "否",
|
||||||
|
"always_use": "是",
|
||||||
|
"when_daily": "仅当每日任务需要时"
|
||||||
|
},
|
||||||
|
"SupportCharacter": {
|
||||||
|
"name": "好友支援角色",
|
||||||
|
"help": "选择好友支援角色,未找到则选择默认(第一个)角色",
|
||||||
|
"FirstCharacter": "支援列表第一个角色",
|
||||||
|
"Arlan": "阿兰",
|
||||||
|
"Asta": "艾丝妲",
|
||||||
|
"Bailu": "白露",
|
||||||
|
"Bronya": "布洛妮娅",
|
||||||
|
"Clara": "克拉拉",
|
||||||
|
"DanHeng": "丹恒",
|
||||||
|
"Gepard": "杰帕德",
|
||||||
|
"Herta": "黑塔",
|
||||||
|
"Himeko": "姬子",
|
||||||
|
"Hook": "虎克",
|
||||||
|
"JingYuan": "景元",
|
||||||
|
"Kafka": "卡芙卡(未实装)",
|
||||||
|
"Luocha": "罗刹",
|
||||||
|
"March7th": "三月七",
|
||||||
|
"Natasha": "娜塔莎",
|
||||||
|
"Pela": "佩拉",
|
||||||
|
"Qingque": "青雀",
|
||||||
|
"Sampo": "桑博",
|
||||||
|
"Seele": "希儿",
|
||||||
|
"Serval": "希露瓦",
|
||||||
|
"SilverWolf": "银狼",
|
||||||
|
"Sushang": "素裳",
|
||||||
|
"Tingyun": "停云",
|
||||||
|
"TrailblazertheDestruction": "开拓者•毁灭",
|
||||||
|
"TrailblazerthePreservation": "开拓者•存护",
|
||||||
|
"Welt": "瓦尔特",
|
||||||
|
"Yanqing": "彦卿",
|
||||||
|
"Yukong": "驭空"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Assignment": {
|
"Assignment": {
|
||||||
|
@ -233,6 +233,46 @@
|
|||||||
"4": "4",
|
"4": "4",
|
||||||
"5": "5",
|
"5": "5",
|
||||||
"6": "6"
|
"6": "6"
|
||||||
|
},
|
||||||
|
"Support": {
|
||||||
|
"name": "Dungeon.Support.name",
|
||||||
|
"help": "Dungeon.Support.help",
|
||||||
|
"do_not_use": "do_not_use",
|
||||||
|
"always_use": "always_use",
|
||||||
|
"when_daily": "when_daily"
|
||||||
|
},
|
||||||
|
"SupportCharacter": {
|
||||||
|
"name": "Dungeon.SupportCharacter.name",
|
||||||
|
"help": "Dungeon.SupportCharacter.help",
|
||||||
|
"FirstCharacter": "FirstCharacter",
|
||||||
|
"Arlan": "阿蘭",
|
||||||
|
"Asta": "艾絲妲",
|
||||||
|
"Bailu": "白露",
|
||||||
|
"Bronya": "布洛妮婭",
|
||||||
|
"Clara": "克拉拉",
|
||||||
|
"DanHeng": "丹恆",
|
||||||
|
"Gepard": "傑帕德",
|
||||||
|
"Herta": "黑塔",
|
||||||
|
"Himeko": "姬子",
|
||||||
|
"Hook": "虎克",
|
||||||
|
"JingYuan": "景元",
|
||||||
|
"Kafka": "卡芙卡",
|
||||||
|
"Luocha": "羅剎",
|
||||||
|
"March7th": "三月七",
|
||||||
|
"Natasha": "娜塔莎",
|
||||||
|
"Pela": "佩拉",
|
||||||
|
"Qingque": "青雀",
|
||||||
|
"Sampo": "桑博",
|
||||||
|
"Seele": "希兒",
|
||||||
|
"Serval": "希露瓦",
|
||||||
|
"SilverWolf": "銀狼",
|
||||||
|
"Sushang": "素裳",
|
||||||
|
"Tingyun": "停雲",
|
||||||
|
"TrailblazertheDestruction": "Trailblazer•毀滅",
|
||||||
|
"TrailblazerthePreservation": "Trailblazer•存護",
|
||||||
|
"Welt": "瓦爾特",
|
||||||
|
"Yanqing": "彥卿",
|
||||||
|
"Yukong": "馭空"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Assignment": {
|
"Assignment": {
|
||||||
|
2
tasks/character/keywords/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import tasks.character.keywords.character_list as KEYWORD_CHARACTER_LIST
|
||||||
|
from tasks.character.keywords.classes import CharacterList
|
229
tasks/character/keywords/character_list.py
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
from .classes import CharacterList
|
||||||
|
|
||||||
|
# This file was auto-generated, do not modify it manually. To generate:
|
||||||
|
# ``` python -m dev_tools.keyword_extract ```
|
||||||
|
|
||||||
|
Arlan = CharacterList(
|
||||||
|
id=1,
|
||||||
|
name='Arlan',
|
||||||
|
cn='阿兰',
|
||||||
|
cht='阿蘭',
|
||||||
|
en='Arlan',
|
||||||
|
jp='アーラン',
|
||||||
|
)
|
||||||
|
Asta = CharacterList(
|
||||||
|
id=2,
|
||||||
|
name='Asta',
|
||||||
|
cn='艾丝妲',
|
||||||
|
cht='艾絲妲',
|
||||||
|
en='Asta',
|
||||||
|
jp='アスター',
|
||||||
|
)
|
||||||
|
Bailu = CharacterList(
|
||||||
|
id=3,
|
||||||
|
name='Bailu',
|
||||||
|
cn='白露',
|
||||||
|
cht='白露',
|
||||||
|
en='Bailu',
|
||||||
|
jp='白露',
|
||||||
|
)
|
||||||
|
Bronya = CharacterList(
|
||||||
|
id=4,
|
||||||
|
name='Bronya',
|
||||||
|
cn='布洛妮娅',
|
||||||
|
cht='布洛妮婭',
|
||||||
|
en='Bronya',
|
||||||
|
jp='ブローニャ',
|
||||||
|
)
|
||||||
|
Clara = CharacterList(
|
||||||
|
id=5,
|
||||||
|
name='Clara',
|
||||||
|
cn='克拉拉',
|
||||||
|
cht='克拉拉',
|
||||||
|
en='Clara',
|
||||||
|
jp='クラーラ',
|
||||||
|
)
|
||||||
|
DanHeng = CharacterList(
|
||||||
|
id=6,
|
||||||
|
name='DanHeng',
|
||||||
|
cn='丹恒',
|
||||||
|
cht='丹恆',
|
||||||
|
en='Dan Heng',
|
||||||
|
jp='丹恒',
|
||||||
|
)
|
||||||
|
Gepard = CharacterList(
|
||||||
|
id=7,
|
||||||
|
name='Gepard',
|
||||||
|
cn='杰帕德',
|
||||||
|
cht='傑帕德',
|
||||||
|
en='Gepard',
|
||||||
|
jp='ジェパード',
|
||||||
|
)
|
||||||
|
Herta = CharacterList(
|
||||||
|
id=8,
|
||||||
|
name='Herta',
|
||||||
|
cn='黑塔',
|
||||||
|
cht='黑塔',
|
||||||
|
en='Herta',
|
||||||
|
jp='ヘルタ',
|
||||||
|
)
|
||||||
|
Himeko = CharacterList(
|
||||||
|
id=9,
|
||||||
|
name='Himeko',
|
||||||
|
cn='姬子',
|
||||||
|
cht='姬子',
|
||||||
|
en='Himeko',
|
||||||
|
jp='姫子',
|
||||||
|
)
|
||||||
|
Hook = CharacterList(
|
||||||
|
id=10,
|
||||||
|
name='Hook',
|
||||||
|
cn='虎克',
|
||||||
|
cht='虎克',
|
||||||
|
en='Hook',
|
||||||
|
jp='フック',
|
||||||
|
)
|
||||||
|
JingYuan = CharacterList(
|
||||||
|
id=11,
|
||||||
|
name='JingYuan',
|
||||||
|
cn='景元',
|
||||||
|
cht='景元',
|
||||||
|
en='Jing Yuan',
|
||||||
|
jp='景元',
|
||||||
|
)
|
||||||
|
Kafka = CharacterList(
|
||||||
|
id=12,
|
||||||
|
name='Kafka',
|
||||||
|
cn='卡芙卡',
|
||||||
|
cht='卡芙卡',
|
||||||
|
en='Kafka',
|
||||||
|
jp='カフカ',
|
||||||
|
)
|
||||||
|
Luocha = CharacterList(
|
||||||
|
id=13,
|
||||||
|
name='Luocha',
|
||||||
|
cn='罗刹',
|
||||||
|
cht='羅剎',
|
||||||
|
en='Luocha',
|
||||||
|
jp='羅刹',
|
||||||
|
)
|
||||||
|
March7th = CharacterList(
|
||||||
|
id=14,
|
||||||
|
name='March7th',
|
||||||
|
cn='三月七',
|
||||||
|
cht='三月七',
|
||||||
|
en='March 7th',
|
||||||
|
jp='三月なのか',
|
||||||
|
)
|
||||||
|
Natasha = CharacterList(
|
||||||
|
id=15,
|
||||||
|
name='Natasha',
|
||||||
|
cn='娜塔莎',
|
||||||
|
cht='娜塔莎',
|
||||||
|
en='Natasha',
|
||||||
|
jp='ナターシャ',
|
||||||
|
)
|
||||||
|
Pela = CharacterList(
|
||||||
|
id=16,
|
||||||
|
name='Pela',
|
||||||
|
cn='佩拉',
|
||||||
|
cht='佩拉',
|
||||||
|
en='Pela',
|
||||||
|
jp='ペラ',
|
||||||
|
)
|
||||||
|
Qingque = CharacterList(
|
||||||
|
id=17,
|
||||||
|
name='Qingque',
|
||||||
|
cn='青雀',
|
||||||
|
cht='青雀',
|
||||||
|
en='Qingque',
|
||||||
|
jp='青雀',
|
||||||
|
)
|
||||||
|
Sampo = CharacterList(
|
||||||
|
id=18,
|
||||||
|
name='Sampo',
|
||||||
|
cn='桑博',
|
||||||
|
cht='桑博',
|
||||||
|
en='Sampo',
|
||||||
|
jp='サンポ',
|
||||||
|
)
|
||||||
|
Seele = CharacterList(
|
||||||
|
id=19,
|
||||||
|
name='Seele',
|
||||||
|
cn='希儿',
|
||||||
|
cht='希兒',
|
||||||
|
en='Seele',
|
||||||
|
jp='ゼーレ',
|
||||||
|
)
|
||||||
|
Serval = CharacterList(
|
||||||
|
id=20,
|
||||||
|
name='Serval',
|
||||||
|
cn='希露瓦',
|
||||||
|
cht='希露瓦',
|
||||||
|
en='Serval',
|
||||||
|
jp='セーバル',
|
||||||
|
)
|
||||||
|
SilverWolf = CharacterList(
|
||||||
|
id=21,
|
||||||
|
name='SilverWolf',
|
||||||
|
cn='银狼',
|
||||||
|
cht='銀狼',
|
||||||
|
en='Silver Wolf',
|
||||||
|
jp='銀狼',
|
||||||
|
)
|
||||||
|
Sushang = CharacterList(
|
||||||
|
id=22,
|
||||||
|
name='Sushang',
|
||||||
|
cn='素裳',
|
||||||
|
cht='素裳',
|
||||||
|
en='Sushang',
|
||||||
|
jp='素裳',
|
||||||
|
)
|
||||||
|
Tingyun = CharacterList(
|
||||||
|
id=23,
|
||||||
|
name='Tingyun',
|
||||||
|
cn='停云',
|
||||||
|
cht='停雲',
|
||||||
|
en='Tingyun',
|
||||||
|
jp='停雲',
|
||||||
|
)
|
||||||
|
TrailblazertheDestruction = CharacterList(
|
||||||
|
id=24,
|
||||||
|
name='TrailblazertheDestruction',
|
||||||
|
cn='Trailblazer•毁灭',
|
||||||
|
cht='Trailblazer•毀滅',
|
||||||
|
en='Trailblazer: the Destruction',
|
||||||
|
jp='Trailblazer・壊滅',
|
||||||
|
)
|
||||||
|
TrailblazerthePreservation = CharacterList(
|
||||||
|
id=25,
|
||||||
|
name='TrailblazerthePreservation',
|
||||||
|
cn='Trailblazer•存护',
|
||||||
|
cht='Trailblazer•存護',
|
||||||
|
en='Trailblazer: the Preservation',
|
||||||
|
jp='Trailblazer・存護',
|
||||||
|
)
|
||||||
|
Welt = CharacterList(
|
||||||
|
id=26,
|
||||||
|
name='Welt',
|
||||||
|
cn='瓦尔特',
|
||||||
|
cht='瓦爾特',
|
||||||
|
en='Welt',
|
||||||
|
jp='ヴェルト',
|
||||||
|
)
|
||||||
|
Yanqing = CharacterList(
|
||||||
|
id=27,
|
||||||
|
name='Yanqing',
|
||||||
|
cn='彦卿',
|
||||||
|
cht='彥卿',
|
||||||
|
en='Yanqing',
|
||||||
|
jp='彦卿',
|
||||||
|
)
|
||||||
|
Yukong = CharacterList(
|
||||||
|
id=28,
|
||||||
|
name='Yukong',
|
||||||
|
cn='驭空',
|
||||||
|
cht='馭空',
|
||||||
|
en='Yukong',
|
||||||
|
jp='御空',
|
||||||
|
)
|
8
tasks/character/keywords/classes.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import ClassVar
|
||||||
|
|
||||||
|
from module.ocr.keyword import Keyword
|
||||||
|
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class CharacterList(Keyword):
|
||||||
|
instances: ClassVar = {}
|
55
tasks/combat/assets/assets_combat_support.py
Normal 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 ```
|
||||||
|
|
||||||
|
COMBAT_SUPPORT_ADD = ButtonWrapper(
|
||||||
|
name='COMBAT_SUPPORT_ADD',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/support/COMBAT_SUPPORT_ADD.png',
|
||||||
|
area=(1032, 649, 1132, 680),
|
||||||
|
search=(1012, 629, 1152, 700),
|
||||||
|
color=(228, 228, 228),
|
||||||
|
button=(1032, 649, 1132, 680),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
COMBAT_SUPPORT_LIST = ButtonWrapper(
|
||||||
|
name='COMBAT_SUPPORT_LIST',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/support/COMBAT_SUPPORT_LIST.png',
|
||||||
|
area=(57, 637, 100, 680),
|
||||||
|
search=(37, 617, 120, 700),
|
||||||
|
color=(212, 213, 215),
|
||||||
|
button=(57, 637, 100, 680),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
COMBAT_SUPPORT_LIST_GRID = ButtonWrapper(
|
||||||
|
name='COMBAT_SUPPORT_LIST_GRID',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/support/COMBAT_SUPPORT_LIST_GRID.png',
|
||||||
|
area=(64, 115, 159, 634),
|
||||||
|
search=(44, 95, 179, 654),
|
||||||
|
color=(119, 108, 132),
|
||||||
|
button=(64, 115, 159, 634),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
COMBAT_SUPPORT_LIST_SCROLL = ButtonWrapper(
|
||||||
|
name='COMBAT_SUPPORT_LIST_SCROLL',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/support/COMBAT_SUPPORT_LIST_SCROLL.png',
|
||||||
|
area=(448, 112, 452, 610),
|
||||||
|
search=(428, 92, 472, 630),
|
||||||
|
color=(127, 133, 150),
|
||||||
|
button=(448, 112, 452, 610),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
COMBAT_SUPPORT_SELECTED = ButtonWrapper(
|
||||||
|
name='COMBAT_SUPPORT_SELECTED',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/support/COMBAT_SUPPORT_SELECTED.png',
|
||||||
|
area=(69, 114, 91, 116),
|
||||||
|
search=(49, 94, 111, 136),
|
||||||
|
color=(254, 254, 254),
|
||||||
|
button=(69, 114, 91, 116),
|
||||||
|
),
|
||||||
|
)
|
@ -3,6 +3,16 @@ from module.base.button import Button, ButtonWrapper
|
|||||||
# This file was auto-generated, do not modify it manually. To generate:
|
# This file was auto-generated, do not modify it manually. To generate:
|
||||||
# ``` python -m dev_tools.button_extract ```
|
# ``` python -m dev_tools.button_extract ```
|
||||||
|
|
||||||
|
COMBAT_TEAM_DISMISSSUPPORT = ButtonWrapper(
|
||||||
|
name='COMBAT_TEAM_DISMISSSUPPORT',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/team/COMBAT_TEAM_DISMISSSUPPORT.png',
|
||||||
|
area=(1127, 477, 1154, 501),
|
||||||
|
search=(1107, 457, 1174, 521),
|
||||||
|
color=(132, 140, 150),
|
||||||
|
button=(1127, 477, 1154, 501),
|
||||||
|
),
|
||||||
|
)
|
||||||
COMBAT_TEAM_PREPARE = ButtonWrapper(
|
COMBAT_TEAM_PREPARE = ButtonWrapper(
|
||||||
name='COMBAT_TEAM_PREPARE',
|
name='COMBAT_TEAM_PREPARE',
|
||||||
cn=Button(
|
cn=Button(
|
||||||
@ -13,6 +23,16 @@ COMBAT_TEAM_PREPARE = ButtonWrapper(
|
|||||||
button=(958, 641, 1193, 676),
|
button=(958, 641, 1193, 676),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
COMBAT_TEAM_SUPPORT = ButtonWrapper(
|
||||||
|
name='COMBAT_TEAM_SUPPORT',
|
||||||
|
share=Button(
|
||||||
|
file='./assets/share/combat/team/COMBAT_TEAM_SUPPORT.png',
|
||||||
|
area=(1123, 477, 1158, 503),
|
||||||
|
search=(1103, 457, 1178, 523),
|
||||||
|
color=(195, 215, 201),
|
||||||
|
button=(1123, 477, 1158, 503),
|
||||||
|
),
|
||||||
|
)
|
||||||
TEAM_1 = ButtonWrapper(
|
TEAM_1 = ButtonWrapper(
|
||||||
name='TEAM_1',
|
name='TEAM_1',
|
||||||
share=Button(
|
share=Button(
|
||||||
|
@ -3,15 +3,17 @@ from module.logger import logger
|
|||||||
from tasks.base.assets.assets_base_page import CLOSE
|
from tasks.base.assets.assets_base_page import CLOSE
|
||||||
from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT
|
from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT
|
||||||
from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE
|
from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE
|
||||||
from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_PREPARE
|
from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_PREPARE, COMBAT_TEAM_SUPPORT, COMBAT_TEAM_DISMISSSUPPORT
|
||||||
|
from tasks.combat.assets.assets_combat_support import COMBAT_SUPPORT_ADD, COMBAT_SUPPORT_LIST
|
||||||
from tasks.combat.interact import CombatInteract
|
from tasks.combat.interact import CombatInteract
|
||||||
from tasks.combat.prepare import CombatPrepare
|
from tasks.combat.prepare import CombatPrepare
|
||||||
from tasks.combat.state import CombatState
|
from tasks.combat.state import CombatState
|
||||||
from tasks.combat.team import CombatTeam
|
from tasks.combat.team import CombatTeam
|
||||||
|
from tasks.combat.support import CombatSupport
|
||||||
from tasks.map.control.joystick import MapControlJoystick
|
from tasks.map.control.joystick import MapControlJoystick
|
||||||
|
|
||||||
|
|
||||||
class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJoystick):
|
class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSupport, MapControlJoystick):
|
||||||
def handle_combat_prepare(self):
|
def handle_combat_prepare(self):
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
@ -63,10 +65,12 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJ
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def combat_prepare(self, team=1):
|
def combat_prepare(self, team=1, support_character: str = None):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
team: 1 to 6.
|
team: 1 to 6.
|
||||||
|
skip_first_screenshot:
|
||||||
|
support_character: Support character name
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if success to enter combat
|
bool: True if success to enter combat
|
||||||
@ -78,6 +82,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJ
|
|||||||
"""
|
"""
|
||||||
logger.hr('Combat prepare')
|
logger.hr('Combat prepare')
|
||||||
skip_first_screenshot = True
|
skip_first_screenshot = True
|
||||||
|
pre_set_team = bool(support_character)
|
||||||
while 1:
|
while 1:
|
||||||
if skip_first_screenshot:
|
if skip_first_screenshot:
|
||||||
skip_first_screenshot = False
|
skip_first_screenshot = False
|
||||||
@ -89,6 +94,13 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJ
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# Click
|
# Click
|
||||||
|
if self.appear(COMBAT_TEAM_SUPPORT) and support_character:
|
||||||
|
if pre_set_team:
|
||||||
|
self.team_set(team)
|
||||||
|
pre_set_team = False
|
||||||
|
continue
|
||||||
|
self.support_set(support_character)
|
||||||
|
continue
|
||||||
if self.appear(COMBAT_TEAM_PREPARE, interval=2):
|
if self.appear(COMBAT_TEAM_PREPARE, interval=2):
|
||||||
self.team_set(team)
|
self.team_set(team)
|
||||||
self.device.click(COMBAT_TEAM_PREPARE)
|
self.device.click(COMBAT_TEAM_PREPARE)
|
||||||
@ -260,7 +272,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJ
|
|||||||
self.device.click(COMBAT_EXIT)
|
self.device.click(COMBAT_EXIT)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def combat(self, team: int = 1, wave_limit: int = 0, skip_first_screenshot=True):
|
def combat(self, team: int = 1, wave_limit: int = 0, skip_first_screenshot=True, support_character: str = None):
|
||||||
"""
|
"""
|
||||||
Combat until trailblaze power runs out.
|
Combat until trailblaze power runs out.
|
||||||
|
|
||||||
@ -268,6 +280,9 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJ
|
|||||||
team: 1 to 6.
|
team: 1 to 6.
|
||||||
wave_limit: Limit combat runs, 0 means no limit.
|
wave_limit: Limit combat runs, 0 means no limit.
|
||||||
skip_first_screenshot:
|
skip_first_screenshot:
|
||||||
|
use_support: "do_not_use", "always_use", "when_daily"
|
||||||
|
is_daily: True if is a daily task
|
||||||
|
support_character: Support character name
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if trailblaze power exhausted
|
bool: True if trailblaze power exhausted
|
||||||
@ -287,7 +302,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, MapControlJ
|
|||||||
logger.hr('Combat', level=2)
|
logger.hr('Combat', level=2)
|
||||||
logger.info(f'Combat, team={team}, wave={self.combat_wave_done}/{self.combat_wave_limit}')
|
logger.info(f'Combat, team={team}, wave={self.combat_wave_done}/{self.combat_wave_limit}')
|
||||||
# Prepare
|
# Prepare
|
||||||
prepare = self.combat_prepare(team)
|
prepare = self.combat_prepare(team, support_character)
|
||||||
if not prepare:
|
if not prepare:
|
||||||
self.combat_exit()
|
self.combat_exit()
|
||||||
break
|
break
|
||||||
|
217
tasks/combat/support.py
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from scipy import signal
|
||||||
|
from module.base.timer import Timer
|
||||||
|
from module.base.utils import area_size, crop, rgb2luma, load_image
|
||||||
|
from module.logger import logger
|
||||||
|
from module.ui.scroll import Scroll
|
||||||
|
from tasks.base.ui import UI
|
||||||
|
from tasks.combat.assets.assets_combat_support import COMBAT_SUPPORT_ADD, COMBAT_SUPPORT_LIST, \
|
||||||
|
COMBAT_SUPPORT_LIST_SCROLL, COMBAT_SUPPORT_SELECTED
|
||||||
|
from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_SUPPORT, COMBAT_TEAM_DISMISSSUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
class SupportCharacter:
|
||||||
|
_image_cache = {}
|
||||||
|
|
||||||
|
def __init__(self, name, screenshot, similarity=0.85):
|
||||||
|
self.name = name
|
||||||
|
self.image = self._scale_character()
|
||||||
|
self.screenshot = screenshot
|
||||||
|
self.similarity = similarity
|
||||||
|
self.button = self._find_character()
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
# __bool__ is called when use an object of the class in a boolean context
|
||||||
|
return self.button is not None
|
||||||
|
|
||||||
|
def _scale_character(self):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
Image: Character image after scaled
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.name in SupportCharacter._image_cache:
|
||||||
|
logger.info(f"Using cached image of {self.name}")
|
||||||
|
return SupportCharacter._image_cache[self.name]
|
||||||
|
|
||||||
|
img = load_image(f"assets/character/{self.name}.png")
|
||||||
|
scaled_img = cv2.resize(img, (85, 82))
|
||||||
|
SupportCharacter._image_cache[self.name] = scaled_img
|
||||||
|
logger.info(f"Character {self.name} image cached")
|
||||||
|
return scaled_img
|
||||||
|
|
||||||
|
def _find_character(self):
|
||||||
|
character = np.array(self.image)
|
||||||
|
support_list_img = self.screenshot
|
||||||
|
res = cv2.matchTemplate(character, support_list_img, cv2.TM_CCOEFF_NORMED)
|
||||||
|
|
||||||
|
_, max_val, _, max_loc = cv2.minMaxLoc(res)
|
||||||
|
|
||||||
|
character_width = character.shape[1]
|
||||||
|
character_height = character.shape[0]
|
||||||
|
|
||||||
|
return (max_loc[0], max_loc[1], max_loc[0] + character_width, max_loc[1] + character_height) \
|
||||||
|
if max_val >= self.similarity else None
|
||||||
|
|
||||||
|
def selected_icon_search(self):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
tuple: (x1, y1, x2, y2) of selected icon search area
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
self.button[0], self.button[1] - 5, self.button[0] + 30, self.button[1]) if self.button else None
|
||||||
|
|
||||||
|
|
||||||
|
class SupportListScroll(Scroll):
|
||||||
|
def cal_position(self, main):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
main (ModuleBase):
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 0 to 1.
|
||||||
|
"""
|
||||||
|
image = main.device.image
|
||||||
|
|
||||||
|
temp_area = list(self.area)
|
||||||
|
temp_area[0] = int(temp_area[0] * 0.98)
|
||||||
|
temp_area[2] = int(temp_area[2] * 1.02)
|
||||||
|
|
||||||
|
line = rgb2luma(crop(image, temp_area)).flatten()
|
||||||
|
width = area_size(temp_area)[0]
|
||||||
|
parameters = {
|
||||||
|
"height": 180,
|
||||||
|
"prominence": 30,
|
||||||
|
"distance": width * 0.75,
|
||||||
|
}
|
||||||
|
peaks, _ = signal.find_peaks(line, **parameters)
|
||||||
|
peaks //= width
|
||||||
|
self.length = len(peaks)
|
||||||
|
middle = np.mean(peaks)
|
||||||
|
|
||||||
|
position = (middle - self.length / 2) / (self.total - self.length)
|
||||||
|
position = position if position > 0 else 0.0
|
||||||
|
position = position if position < 1 else 1.0
|
||||||
|
logger.attr(
|
||||||
|
self.name, f"{position:.2f} ({middle}-{self.length / 2})/({self.total}-{self.length})")
|
||||||
|
return position
|
||||||
|
|
||||||
|
|
||||||
|
class CombatSupport(UI):
|
||||||
|
def support_set(self, support_character_name: str = "FirstCharacter"):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
support_character_name: Support character name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: If clicked
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
in: COMBAT_PREPARE
|
||||||
|
mid: COMBAT_SUPPORT_LIST
|
||||||
|
out: COMBAT_PREPARE
|
||||||
|
"""
|
||||||
|
logger.hr("Combat support")
|
||||||
|
skip_first_screenshot = True
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
self.device.screenshot()
|
||||||
|
|
||||||
|
# End
|
||||||
|
if self.appear(COMBAT_TEAM_DISMISSSUPPORT):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Click
|
||||||
|
if self.appear(COMBAT_TEAM_SUPPORT, interval=2):
|
||||||
|
self.device.click(COMBAT_TEAM_SUPPORT)
|
||||||
|
self.interval_reset(COMBAT_TEAM_SUPPORT)
|
||||||
|
continue
|
||||||
|
if self.appear(COMBAT_SUPPORT_LIST, interval=2):
|
||||||
|
if support_character_name != "FirstCharacter":
|
||||||
|
self._search_support(
|
||||||
|
support_character_name) # Search support
|
||||||
|
self.device.click(COMBAT_SUPPORT_ADD)
|
||||||
|
self.interval_reset(COMBAT_SUPPORT_LIST)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def _search_support(self, support_character_name: str = "JingYuan"):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
support_character_name: Support character name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if found support else False
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
in: COMBAT_SUPPORT_LIST
|
||||||
|
out: COMBAT_SUPPORT_LIST
|
||||||
|
"""
|
||||||
|
logger.hr("Combat support search")
|
||||||
|
scroll = SupportListScroll(area=COMBAT_SUPPORT_LIST_SCROLL.area, color=(194, 196, 205),
|
||||||
|
name=COMBAT_SUPPORT_LIST_SCROLL.name)
|
||||||
|
if scroll.appear(main=self):
|
||||||
|
if not scroll.at_bottom(main=self):
|
||||||
|
scroll.set_bottom(main=self)
|
||||||
|
scroll.set_top(main=self)
|
||||||
|
|
||||||
|
logger.info("Searching support")
|
||||||
|
skip_first_screenshot = False
|
||||||
|
character = None
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
self.device.screenshot()
|
||||||
|
|
||||||
|
if not support_character_name.startswith("Trailblazer"):
|
||||||
|
character = SupportCharacter(support_character_name, self.device.image)
|
||||||
|
else:
|
||||||
|
character = SupportCharacter(f"Stelle{support_character_name[11:]}",
|
||||||
|
self.device.image) or SupportCharacter(
|
||||||
|
f"Caelum{support_character_name[11:]}", self.device.image)
|
||||||
|
|
||||||
|
if character:
|
||||||
|
logger.info("Support found")
|
||||||
|
if self._select_support(character):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning("Support not selected")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not scroll.at_bottom(main=self):
|
||||||
|
scroll.next_page(main=self)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logger.info("Support not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _select_support(self, character: SupportCharacter):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
character: Support character
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
in: COMBAT_SUPPORT_LIST
|
||||||
|
out: COMBAT_SUPPORT_LIST
|
||||||
|
"""
|
||||||
|
logger.hr("Combat support select")
|
||||||
|
COMBAT_SUPPORT_SELECTED.matched_button.search = character.selected_icon_search()
|
||||||
|
skip_first_screenshot = False
|
||||||
|
interval = Timer(2)
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
self.device.screenshot()
|
||||||
|
|
||||||
|
# End
|
||||||
|
if self.match_template(COMBAT_SUPPORT_SELECTED):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if interval.reached():
|
||||||
|
self.device.click(character)
|
||||||
|
interval.reset()
|
||||||
|
continue
|
@ -7,11 +7,16 @@ from tasks.dungeon.ui import DungeonUI
|
|||||||
|
|
||||||
|
|
||||||
class Dungeon(DungeonUI, DungeonEvent, Combat):
|
class Dungeon(DungeonUI, DungeonEvent, Combat):
|
||||||
def run(self, dungeon: DungeonList = None, team: int = None):
|
def run(self, dungeon: DungeonList = None, team: int = None, use_support: str = None, is_daily: bool = False,
|
||||||
|
support_character: str = None):
|
||||||
if dungeon is None:
|
if dungeon is None:
|
||||||
dungeon = DungeonList.find(self.config.Dungeon_Name)
|
dungeon = DungeonList.find(self.config.Dungeon_Name)
|
||||||
if team is None:
|
if team is None:
|
||||||
team = self.config.Dungeon_Team
|
team = self.config.Dungeon_Team
|
||||||
|
if use_support is None:
|
||||||
|
use_support = self.config.Dungeon_Support
|
||||||
|
if support_character is None:
|
||||||
|
support_character = self.config.Dungeon_SupportCharacter if use_support == "always_use" or use_support == "when_daily" and is_daily else None
|
||||||
|
|
||||||
# UI switches
|
# UI switches
|
||||||
switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
||||||
@ -26,7 +31,7 @@ class Dungeon(DungeonUI, DungeonEvent, Combat):
|
|||||||
self._dungeon_nav_goto(calyx)
|
self._dungeon_nav_goto(calyx)
|
||||||
if remain := self.get_double_event_remain():
|
if remain := self.get_double_event_remain():
|
||||||
self.dungeon_goto(calyx)
|
self.dungeon_goto(calyx)
|
||||||
if self.combat(team, wave_limit=remain):
|
if self.combat(team, wave_limit=remain, support_character=support_character):
|
||||||
self.delay_dungeon_task(calyx)
|
self.delay_dungeon_task(calyx)
|
||||||
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
||||||
|
|
||||||
@ -38,7 +43,7 @@ class Dungeon(DungeonUI, DungeonEvent, Combat):
|
|||||||
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
||||||
self.dungeon_goto(dungeon)
|
self.dungeon_goto(dungeon)
|
||||||
|
|
||||||
self.combat(team)
|
self.combat(team=team, support_character=support_character)
|
||||||
self.delay_dungeon_task(dungeon)
|
self.delay_dungeon_task(dungeon)
|
||||||
|
|
||||||
def delay_dungeon_task(self, dungeon):
|
def delay_dungeon_task(self, dungeon):
|
||||||
|