diff --git a/assets/share/combat/state/COMBAT_AUTO.png b/assets/share/combat/state/COMBAT_AUTO.png new file mode 100644 index 000000000..5d5d06ee5 Binary files /dev/null and b/assets/share/combat/state/COMBAT_AUTO.png differ diff --git a/assets/share/combat/state/COMBAT_PAUSE.png b/assets/share/combat/state/COMBAT_PAUSE.png new file mode 100644 index 000000000..6d3050731 Binary files /dev/null and b/assets/share/combat/state/COMBAT_PAUSE.png differ diff --git a/assets/share/combat/state/COMBAT_SPEED_2X.png b/assets/share/combat/state/COMBAT_SPEED_2X.png new file mode 100644 index 000000000..148361d62 Binary files /dev/null and b/assets/share/combat/state/COMBAT_SPEED_2X.png differ diff --git a/module/base/button.py b/module/base/button.py index 34ec111d0..a59c15e8c 100644 --- a/module/base/button.py +++ b/module/base/button.py @@ -197,6 +197,10 @@ class ButtonWrapper(Resource): def button(self) -> tuple[int, int, int, int]: return self.matched_button.button + @property + def button_offset(self) -> tuple[int, int]: + return self.matched_button._button_offset + @property def width(self) -> int: return area_size(self.area)[0] diff --git a/tasks/combat/assets/assets_combat_state.py b/tasks/combat/assets/assets_combat_state.py new file mode 100644 index 000000000..4103c6487 --- /dev/null +++ b/tasks/combat/assets/assets_combat_state.py @@ -0,0 +1,35 @@ +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_AUTO = ButtonWrapper( + name='COMBAT_AUTO', + share=Button( + file='./assets/share/combat/state/COMBAT_AUTO.png', + area=(1113, 22, 1183, 58), + search=(1093, 2, 1203, 78), + color=(135, 137, 131), + button=(1113, 22, 1183, 58), + ), +) +COMBAT_PAUSE = ButtonWrapper( + name='COMBAT_PAUSE', + share=Button( + file='./assets/share/combat/state/COMBAT_PAUSE.png', + area=(1218, 27, 1245, 53), + search=(1198, 7, 1265, 73), + color=(125, 129, 134), + button=(1218, 27, 1245, 53), + ), +) +COMBAT_SPEED_2X = ButtonWrapper( + name='COMBAT_SPEED_2X', + share=Button( + file='./assets/share/combat/state/COMBAT_SPEED_2X.png', + area=(1029, 22, 1099, 58), + search=(1009, 2, 1119, 78), + color=(138, 143, 146), + button=(1029, 22, 1099, 58), + ), +) diff --git a/tasks/combat/state.py b/tasks/combat/state.py new file mode 100644 index 000000000..7080fae57 --- /dev/null +++ b/tasks/combat/state.py @@ -0,0 +1,80 @@ +import cv2 +from scipy import signal + +from module.base.timer import Timer +from module.base.utils import rgb2gray +from tasks.base.ui import UI +from tasks.combat.assets.assets_combat_state import COMBAT_AUTO, COMBAT_PAUSE, COMBAT_SPEED_2X + + +class CombatState(UI): + _combat_click_interval = Timer(1, count=2) + + def is_combat_executing(self) -> bool: + appear = self.appear(COMBAT_PAUSE) + if appear: + if COMBAT_PAUSE.button_offset[0] <= 5: + return True + + return False + + def is_combat_auto(self) -> bool: + image = rgb2gray(self.image_crop(COMBAT_AUTO)) + line = cv2.reduce(image, 1, cv2.REDUCE_AVG).flatten() + # [122 122 122 182 141 127 139 135 130 135 136 141 147 149 149 150 147 145 + # 148 150 150 150 150 150 144 138 134 141 136 133 173 183 130 128 127 126] + parameters = { + # Border is about 188-190 + 'height': 160, + # Background is about 120-122 + 'prominence': 35, + } + peaks, _ = signal.find_peaks(line, **parameters) + return len(peaks) == 2 + + def is_combat_speed_2x(self) -> bool: + image = rgb2gray(self.image_crop(COMBAT_SPEED_2X)) + line = cv2.reduce(image, 1, cv2.REDUCE_AVG).flatten() + # [122 122 122 182 141 127 139 135 130 135 136 141 147 149 149 150 147 145 + # 148 150 150 150 150 150 144 138 134 141 136 133 173 183 130 128 127 126] + parameters = { + # Border is about 188-190 + 'height': 160, + # Background is about 120-122 + 'prominence': 35, + } + peaks, _ = signal.find_peaks(line, **parameters) + return len(peaks) == 2 + + def handle_combat_state(self, auto=True, speed_2x=True): + """ + Set combat auto and 2X speed. Enable both by default. + + Returns: + bool: If clicked + """ + if not self.is_combat_executing(): + return False + + if speed_2x and not self.is_combat_speed_2x(): + if self._combat_click_interval.reached(): + self.device.click(COMBAT_SPEED_2X) + self._combat_click_interval.reset() + return True + if not speed_2x and self.is_combat_speed_2x(): + if self._combat_click_interval.reached(): + self.device.click(COMBAT_SPEED_2X) + self._combat_click_interval.reset() + return True + if auto and not self.is_combat_auto(): + if self._combat_click_interval.reached(): + self.device.click(COMBAT_AUTO) + self._combat_click_interval.reset() + return True + if not auto and self.is_combat_auto(): + if self._combat_click_interval.reached(): + self.device.click(COMBAT_AUTO) + self._combat_click_interval.reset() + return True + + return False