diff --git a/assets/share/combat/skill/CHECK_A.png b/assets/share/combat/skill/CHECK_A.png new file mode 100644 index 000000000..bb14b6de6 Binary files /dev/null and b/assets/share/combat/skill/CHECK_A.png differ diff --git a/assets/share/combat/skill/CHECK_E.png b/assets/share/combat/skill/CHECK_E.png new file mode 100644 index 000000000..6d9a4ca11 Binary files /dev/null and b/assets/share/combat/skill/CHECK_E.png differ diff --git a/assets/share/combat/skill/IN_SKILL.png b/assets/share/combat/skill/IN_SKILL.png new file mode 100644 index 000000000..dd8e36905 Binary files /dev/null and b/assets/share/combat/skill/IN_SKILL.png differ diff --git a/assets/share/combat/skill/USE_A.png b/assets/share/combat/skill/USE_A.png new file mode 100644 index 000000000..d0a30d74a Binary files /dev/null and b/assets/share/combat/skill/USE_A.png differ diff --git a/assets/share/combat/skill/USE_E.png b/assets/share/combat/skill/USE_E.png new file mode 100644 index 000000000..be89fd80c Binary files /dev/null and b/assets/share/combat/skill/USE_E.png differ diff --git a/assets/share/combat/skill/USE_Q1.png b/assets/share/combat/skill/USE_Q1.png new file mode 100644 index 000000000..03f7359a9 Binary files /dev/null and b/assets/share/combat/skill/USE_Q1.png differ diff --git a/assets/share/combat/skill/USE_Q2.png b/assets/share/combat/skill/USE_Q2.png new file mode 100644 index 000000000..cce2f041a Binary files /dev/null and b/assets/share/combat/skill/USE_Q2.png differ diff --git a/assets/share/combat/skill/USE_Q3.png b/assets/share/combat/skill/USE_Q3.png new file mode 100644 index 000000000..12628584e Binary files /dev/null and b/assets/share/combat/skill/USE_Q3.png differ diff --git a/assets/share/combat/skill/USE_Q4.png b/assets/share/combat/skill/USE_Q4.png new file mode 100644 index 000000000..1c78f563b Binary files /dev/null and b/assets/share/combat/skill/USE_Q4.png differ diff --git a/assets/share/combat/skill/USE_Q_AIM.png b/assets/share/combat/skill/USE_Q_AIM.png new file mode 100644 index 000000000..0a93681b0 Binary files /dev/null and b/assets/share/combat/skill/USE_Q_AIM.png differ diff --git a/tasks/combat/assets/assets_combat_skill.py b/tasks/combat/assets/assets_combat_skill.py new file mode 100644 index 000000000..8ea230b77 --- /dev/null +++ b/tasks/combat/assets/assets_combat_skill.py @@ -0,0 +1,105 @@ +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 ``` + +CHECK_A = ButtonWrapper( + name='CHECK_A', + share=Button( + file='./assets/share/combat/skill/CHECK_A.png', + area=(967, 513, 1087, 623), + search=(947, 493, 1107, 643), + color=(91, 91, 97), + button=(967, 513, 1087, 623), + ), +) +CHECK_E = ButtonWrapper( + name='CHECK_E', + share=Button( + file='./assets/share/combat/skill/CHECK_E.png', + area=(1111, 429, 1261, 519), + search=(1091, 409, 1280, 539), + color=(80, 81, 89), + button=(1111, 429, 1261, 519), + ), +) +IN_SKILL = ButtonWrapper( + name='IN_SKILL', + share=Button( + file='./assets/share/combat/skill/IN_SKILL.png', + area=(35, 619, 59, 635), + search=(15, 599, 79, 655), + color=(106, 107, 110), + button=(35, 619, 59, 635), + ), +) +USE_A = ButtonWrapper( + name='USE_A', + share=Button( + file='./assets/share/combat/skill/USE_A.png', + area=(1037, 579, 1097, 639), + search=(1017, 559, 1117, 659), + color=(107, 108, 111), + button=(1037, 579, 1097, 639), + ), +) +USE_E = ButtonWrapper( + name='USE_E', + share=Button( + file='./assets/share/combat/skill/USE_E.png', + area=(1154, 490, 1214, 550), + search=(1134, 470, 1234, 570), + color=(120, 123, 127), + button=(1154, 490, 1214, 550), + ), +) +USE_Q1 = ButtonWrapper( + name='USE_Q1', + share=Button( + file='./assets/share/combat/skill/USE_Q1.png', + area=(217, 548, 253, 578), + search=(197, 528, 273, 598), + color=(145, 151, 225), + button=(217, 548, 253, 578), + ), +) +USE_Q2 = ButtonWrapper( + name='USE_Q2', + share=Button( + file='./assets/share/combat/skill/USE_Q2.png', + area=(405, 546, 441, 576), + search=(385, 526, 461, 596), + color=(155, 131, 207), + button=(405, 546, 441, 576), + ), +) +USE_Q3 = ButtonWrapper( + name='USE_Q3', + share=Button( + file='./assets/share/combat/skill/USE_Q3.png', + area=(593, 545, 629, 575), + search=(573, 525, 649, 595), + color=(208, 208, 202), + button=(593, 545, 629, 575), + ), +) +USE_Q4 = ButtonWrapper( + name='USE_Q4', + share=Button( + file='./assets/share/combat/skill/USE_Q4.png', + area=(781, 545, 817, 575), + search=(761, 525, 837, 595), + color=(151, 113, 206), + button=(781, 545, 817, 575), + ), +) +USE_Q_AIM = ButtonWrapper( + name='USE_Q_AIM', + share=Button( + file='./assets/share/combat/skill/USE_Q_AIM.png', + area=(1074, 521, 1174, 621), + search=(1054, 501, 1194, 641), + color=(82, 60, 64), + button=(1074, 521, 1174, 621), + ), +) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index e86f69106..c0e7933d8 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -6,13 +6,14 @@ from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_PREPARE, COMBAT_TEAM_SUPPORT from tasks.combat.interact import CombatInteract from tasks.combat.prepare import CombatPrepare +from tasks.combat.skill import CombatSkill from tasks.combat.state import CombatState from tasks.combat.support import CombatSupport from tasks.combat.team import CombatTeam from tasks.map.control.joystick import MapControlJoystick -class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSupport, MapControlJoystick): +class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSupport, CombatSkill, MapControlJoystick): def handle_combat_prepare(self): """ Returns: diff --git a/tasks/combat/skill.py b/tasks/combat/skill.py new file mode 100644 index 000000000..d8403002e --- /dev/null +++ b/tasks/combat/skill.py @@ -0,0 +1,126 @@ +from module.base.button import match_template +from module.base.timer import Timer +from module.logger import logger +from tasks.base.ui import UI +from tasks.combat.assets.assets_combat_skill import * + + +class CombatSkill(UI): + def is_in_skill(self) -> bool: + """ + Combat paused, require manual skill use + """ + if not self.appear(IN_SKILL): + return False + + if not self.image_color_count(IN_SKILL, color=(255, 255, 255), threshold=221, count=50): + return False + + return True + + def _skill_click(self, button, skip_first_screenshot=True): + """ + Click a skill button. + Not in skill page means skill has been used and skill animation is ongoing + """ + logger.info(f'Skill use: {button}') + interval = Timer(1) + clicked = False + prev_image = None + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.is_in_skill(): + if interval.reached(): + prev_image = self.image_crop(button) + self.device.click(button) + interval.reset() + clicked = True + continue + else: + # Skill animation on going + if clicked: + logger.info(f'Skill used: {button}') + break + # New skill icon + if prev_image is not None: + if not match_template(self.image_crop(button), prev_image): + logger.info(f'Skill used: {button}') + break + + def _skill_switch(self, check_button, click_button, skip_first_screenshot=True): + """ + Switch to A or E + """ + logger.info(f'Skill switch: {check_button}') + interval = Timer(1) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + # Raw brown border + if self.image_color_count(check_button, color=(220, 196, 145), threshold=221, count=50): + logger.info(f'Skill switched: {check_button}') + break + + if self.is_in_skill(): + if interval.reached(): + self.device.click(click_button) + interval.reset() + continue + + def wait_next_skill(self, expected_end=None, skip_first_screenshot=True): + """ + Args: + expected_end: A function returns bool, True represents end. + skip_first_screenshot: + + Returns: + bool: True if is_in_skill + False if triggered expected_end + """ + logger.info('Wait next skill') + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.is_in_skill(): + return True + if callable(expected_end) and expected_end(): + logger.info(f'Combat execute ended at {expected_end.__name__}') + return False + + def use_A(self) -> bool: + logger.hr('Use A') + self._skill_switch(check_button=CHECK_A, click_button=USE_A) + self._skill_click(USE_A) + return True + + def use_E(self) -> bool: + logger.hr('Use E') + self._skill_switch(check_button=CHECK_E, click_button=USE_E) + self._skill_click(USE_E) + return True + + def use_Q(self, position: int) -> bool: + """ + Args: + position: 1 to 4 + """ + logger.hr(f'Use Q {position}') + try: + button = [USE_Q1, USE_Q2, USE_Q3, USE_Q4][position - 1] + except IndexError: + logger.error(f'use_Q: position {position} does not exist') + return False + + self._skill_click(button) + self._skill_click(USE_Q_AIM) + return True