mirror of
https://github.com/TheFunny/ArisuAutoSweeper
synced 2025-12-16 19:55:12 +00:00
Compare commits
4 Commits
baac90ecf0
...
f8404edd9e
| Author | SHA1 | Date | |
|---|---|---|---|
| f8404edd9e | |||
| ff3ec041d2 | |||
| 99074a1575 | |||
| 7862fa6cb8 |
BIN
assets/en/cafe/BOX_SEARCH.png
Normal file
BIN
assets/en/cafe/BOX_SEARCH.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1018 KiB |
BIN
assets/jp/cafe/BOX_SEARCH.png
Normal file
BIN
assets/jp/cafe/BOX_SEARCH.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1018 KiB |
@ -77,6 +77,12 @@ class DeployConfig(ConfigModel):
|
||||
self.config_template = {}
|
||||
self.read()
|
||||
|
||||
self.set_repo()
|
||||
|
||||
self.write()
|
||||
self.show_config()
|
||||
|
||||
def set_repo(self):
|
||||
# Bypass webui.config.DeployConfig.__setattr__()
|
||||
# Don't write these into deploy.yaml
|
||||
if self.Repository == 'cn':
|
||||
@ -84,9 +90,6 @@ class DeployConfig(ConfigModel):
|
||||
if self.Repository == 'global':
|
||||
super().__setattr__('Repository', 'https://github.com/TheFunny/ArisuAutoSweeper')
|
||||
|
||||
self.write()
|
||||
self.show_config()
|
||||
|
||||
def show_config(self):
|
||||
logger.hr("Show deploy config", 1)
|
||||
for k, v in self.config.items():
|
||||
|
||||
@ -5,6 +5,7 @@ import time
|
||||
from typing import Generator, List, Tuple
|
||||
|
||||
import requests
|
||||
|
||||
from deploy.Windows.config import ExecutionError
|
||||
from deploy.Windows.git import GitManager
|
||||
from deploy.Windows.pip import PipManager
|
||||
@ -20,6 +21,7 @@ from module.webui.utils import TaskHandler, get_next_time
|
||||
class Updater(DeployConfig, GitManager, PipManager):
|
||||
def __init__(self, file=DEPLOY_CONFIG):
|
||||
super().__init__(file=file)
|
||||
self.set_repo()
|
||||
self.state = 0
|
||||
self.event: threading.Event = None
|
||||
|
||||
|
||||
@ -20,6 +20,23 @@ BOX_CAFE = ButtonWrapper(
|
||||
button=(33, 130, 1247, 569),
|
||||
),
|
||||
)
|
||||
BOX_SEARCH = ButtonWrapper(
|
||||
name='BOX_SEARCH',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/BOX_SEARCH.png',
|
||||
area=(61, 72, 1176, 653),
|
||||
search=(41, 52, 1196, 673),
|
||||
color=(172, 180, 188),
|
||||
button=(61, 72, 1176, 653),
|
||||
),
|
||||
en=Button(
|
||||
file='./assets/en/cafe/BOX_SEARCH.png',
|
||||
area=(61, 72, 1176, 653),
|
||||
search=(41, 52, 1196, 673),
|
||||
color=(172, 180, 188),
|
||||
button=(61, 72, 1176, 653),
|
||||
),
|
||||
)
|
||||
CAFE_FIRST = ButtonWrapper(
|
||||
name='CAFE_FIRST',
|
||||
jp=Button(
|
||||
|
||||
@ -1,18 +1,12 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
from enum import Enum
|
||||
|
||||
from module.logger import logger
|
||||
from module.base.timer import Timer
|
||||
from module.base.button import ClickButton
|
||||
from module.base.decorator import Config
|
||||
from module.base.utils.utils import area_offset
|
||||
from module.ocr.ocr import Digit
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from module.ui.switch import Switch
|
||||
from tasks.base.page import page_cafe
|
||||
from tasks.base.ui import UI
|
||||
from tasks.cafe.assets.assets_cafe import *
|
||||
|
||||
from tasks.cafe.ui import CafeUI
|
||||
|
||||
SWITCH_CAFE = Switch('Cafe_switch')
|
||||
SWITCH_CAFE.add_state('off', CHANGE_CAFE_NOT_SELECTED)
|
||||
@ -33,103 +27,17 @@ class CafeStatus(Enum):
|
||||
FINISHED = -1
|
||||
|
||||
|
||||
class Cafe(UI):
|
||||
template = CLICKABLE_TEMPLATE
|
||||
class Cafe(CafeUI):
|
||||
@Config.when(Emulator_GameLanguage='jp')
|
||||
def is_second_cafe_on(self):
|
||||
return self.config.Cafe_SecondCafe
|
||||
|
||||
@staticmethod
|
||||
def merge_points(points, threshold=3):
|
||||
if len(points) <= 1:
|
||||
return points
|
||||
result = []
|
||||
for point in points:
|
||||
if not result:
|
||||
result.append(point)
|
||||
continue
|
||||
if point[0] - result[-1][0] < threshold and point[1] - result[-1][1] < threshold:
|
||||
result[-1] = ((point[0] + result[-1][0]) // 2, (point[1] + result[-1][1]) // 2)
|
||||
continue
|
||||
result.append(point)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _extract_clickable_from_image(image):
|
||||
# convert to hsv for better color matching
|
||||
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
|
||||
# set color range
|
||||
lower_hsv = np.array([18, 200, 220])
|
||||
upper_hsv = np.array([30, 255, 255])
|
||||
# get mask
|
||||
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
|
||||
# generate result
|
||||
return cv2.bitwise_and(image, image, mask=mask)
|
||||
|
||||
def _match_clickable_points(self, image, threshold=0.8):
|
||||
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||||
template = cv2.cvtColor(self.template.matched_button.image, cv2.COLOR_RGB2GRAY)
|
||||
|
||||
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
||||
loc = np.where(res >= threshold)
|
||||
return [point for point in zip(*loc[::-1])]
|
||||
|
||||
def _get_clickable_buttons(self, threshold=0.8, offset=(0, 0)):
|
||||
image = self.device.image
|
||||
h, w = image.shape[:2]
|
||||
image = cv2.rectangle(image, (0, 10), (w - 25, h - 10), (0, 0, 0), 50)
|
||||
image = self._extract_clickable_from_image(image)
|
||||
points = self._match_clickable_points(image, threshold)
|
||||
points = self.merge_points(points)
|
||||
if not points:
|
||||
return []
|
||||
area = area_offset((0, 0, self.template.width, self.template.height), offset)
|
||||
return [
|
||||
ClickButton(
|
||||
button=area_offset(area, offset=point),
|
||||
name=self.template.name
|
||||
)
|
||||
for point in points
|
||||
]
|
||||
|
||||
def _reset_cafe_position(self, direction: str):
|
||||
width = BOX_CAFE.width
|
||||
height = BOX_CAFE.height
|
||||
r = np.random.uniform(0.6, 0.8)
|
||||
vector_down = (width * r, height * r)
|
||||
vector_up = (width * r, -height * r)
|
||||
vector_left = (-width * r, 0)
|
||||
vector_right = (width * r, 0)
|
||||
random_r = (-5, -5, 5, 5)
|
||||
match direction:
|
||||
case 'init':
|
||||
self.device.pinch()
|
||||
self.device.swipe_vector(vector_down, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_up, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_up, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
case 'left':
|
||||
self.device.swipe_vector(vector_left, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_left, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
case 'right':
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
# solve too much swipe causing restart
|
||||
self.device.click_record_clear()
|
||||
|
||||
def _get_reward_num(self):
|
||||
ocr = Digit(OCR_CAFE)
|
||||
num = ocr.detect_and_ocr(self.device.image)
|
||||
if len(num) != 1:
|
||||
logger.warning(f'Invalid reward num: {num}')
|
||||
num = float(num[0].ocr_text.rstrip('%'))
|
||||
logger.attr('Reward', num)
|
||||
return num
|
||||
|
||||
def _cafe_additional(self) -> bool:
|
||||
if self.appear_then_click(INVENTORY):
|
||||
return True
|
||||
if self.appear_then_click(MOMOTALK_CLOSE):
|
||||
return True
|
||||
@Config.when(Emulator_GameLanguage=None)
|
||||
def is_second_cafe_on(self):
|
||||
return False
|
||||
|
||||
is_second_cafe_on = property(is_second_cafe_on)
|
||||
|
||||
def _handle_cafe(self, status):
|
||||
match status:
|
||||
case CafeStatus.STUDENT_LIST:
|
||||
@ -137,7 +45,7 @@ class Cafe(UI):
|
||||
if not self.appear(STUDENT_LIST):
|
||||
return CafeStatus.OCR
|
||||
case CafeStatus.OCR:
|
||||
reward = self._get_reward_num()
|
||||
reward = self.get_reward_num()
|
||||
if reward == 0:
|
||||
return CafeStatus.GOT
|
||||
if self.appear_then_click(CHECK_REWARD):
|
||||
@ -157,14 +65,14 @@ class Cafe(UI):
|
||||
if not self.appear(GET_REWARD_CLOSE):
|
||||
return CafeStatus.CLICK
|
||||
case CafeStatus.CLICK:
|
||||
buttons = self._get_clickable_buttons(offset=(45, 10))
|
||||
buttons = self.get_clickable_buttons(offset=(45, 10))
|
||||
self.click = len(buttons)
|
||||
logger.attr('Clickable', self.click)
|
||||
if not buttons:
|
||||
return CafeStatus.CHECK
|
||||
self.click_with_interval(buttons[0], interval=1)
|
||||
case CafeStatus.CHECK:
|
||||
buttons = self._get_clickable_buttons()
|
||||
buttons = self.get_clickable_buttons()
|
||||
if not self.is_adjust_on:
|
||||
if not buttons:
|
||||
return CafeStatus.FINISHED
|
||||
@ -177,11 +85,11 @@ class Cafe(UI):
|
||||
return CafeStatus.CLICK
|
||||
match self.check:
|
||||
case 1:
|
||||
self._reset_cafe_position('left')
|
||||
self.reset_cafe_position('left')
|
||||
case 2:
|
||||
self._reset_cafe_position('right')
|
||||
self.reset_cafe_position('right')
|
||||
case 3:
|
||||
self._reset_cafe_position('init')
|
||||
self.reset_cafe_position('init')
|
||||
case 4:
|
||||
return CafeStatus.FINISHED
|
||||
case CafeStatus.FINISHED:
|
||||
@ -190,16 +98,6 @@ class Cafe(UI):
|
||||
logger.warning(f'Invalid status: {status}')
|
||||
return status
|
||||
|
||||
@Config.when(Emulator_GameLanguage='jp')
|
||||
def is_second_cafe_on(self):
|
||||
return self.config.Cafe_SecondCafe
|
||||
|
||||
@Config.when(Emulator_GameLanguage=None)
|
||||
def is_second_cafe_on(self):
|
||||
return False
|
||||
|
||||
is_second_cafe_on = property(is_second_cafe_on)
|
||||
|
||||
def run(self):
|
||||
self.click = 0
|
||||
self.check = 0
|
||||
@ -212,7 +110,7 @@ class Cafe(UI):
|
||||
|
||||
status = CafeStatus.STUDENT_LIST
|
||||
loading_timer = Timer(2).start()
|
||||
action_timer = Timer(1, count=1) # cant be too fast
|
||||
action_timer = Timer(1, count=1)
|
||||
is_list = False
|
||||
is_reset = False
|
||||
is_second = False
|
||||
@ -224,7 +122,7 @@ class Cafe(UI):
|
||||
|
||||
self.device.screenshot()
|
||||
|
||||
if self.ui_additional() or self._cafe_additional():
|
||||
if self.ui_additional() or self.cafe_additional():
|
||||
continue
|
||||
|
||||
if not loading_timer.reached():
|
||||
@ -246,7 +144,7 @@ class Cafe(UI):
|
||||
continue
|
||||
|
||||
if is_touch_on and not is_reset and status == CafeStatus.CLICK:
|
||||
self._reset_cafe_position('init')
|
||||
self.reset_cafe_position('init')
|
||||
is_reset = True
|
||||
continue
|
||||
|
||||
|
||||
72
tasks/cafe/ui.py
Normal file
72
tasks/cafe/ui.py
Normal file
@ -0,0 +1,72 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import Digit
|
||||
from tasks.base.ui import UI
|
||||
from tasks.cafe.assets.assets_cafe import *
|
||||
|
||||
|
||||
class CafeUI(UI):
|
||||
template = CLICKABLE_TEMPLATE
|
||||
|
||||
def get_reward_num(self):
|
||||
ocr = Digit(OCR_CAFE)
|
||||
num = ocr.detect_and_ocr(self.device.image)
|
||||
if len(num) != 1:
|
||||
logger.warning(f'Invalid reward num: {num}')
|
||||
num = float(num[0].ocr_text.rstrip('%'))
|
||||
logger.attr('Reward', num)
|
||||
return num
|
||||
|
||||
@staticmethod
|
||||
def extract_clickable_from_image(image):
|
||||
# convert to hsv for better color matching
|
||||
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
|
||||
# set color range
|
||||
lower_hsv = np.array([18, 200, 220])
|
||||
upper_hsv = np.array([30, 255, 255])
|
||||
# get mask
|
||||
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
|
||||
# generate result
|
||||
return cv2.bitwise_and(image, image, mask=mask)
|
||||
|
||||
def get_clickable_buttons(self, similarity=0.8, offset=(45, 10)):
|
||||
image = self.extract_clickable_from_image(self.device.image)
|
||||
self.template.matched_button._button_offset = offset
|
||||
self.template.load_offset(self.template)
|
||||
self.template.load_search(BOX_SEARCH.area)
|
||||
points = self.template.match_multi_template(image, similarity)
|
||||
return points
|
||||
|
||||
def reset_cafe_position(self, direction: str):
|
||||
width = BOX_CAFE.width
|
||||
height = BOX_CAFE.height
|
||||
r = np.random.uniform(0.6, 0.8)
|
||||
vector_down = (width * r, height * r)
|
||||
vector_up = (width * r, -height * r)
|
||||
vector_left = (-width * r, 0)
|
||||
vector_right = (width * r, 0)
|
||||
random_r = (-5, -5, 5, 5)
|
||||
match direction:
|
||||
case 'init':
|
||||
self.device.pinch()
|
||||
self.device.swipe_vector(vector_down, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_up, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_up, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
case 'left':
|
||||
self.device.swipe_vector(vector_left, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_left, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
case 'right':
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
# solve too much swipe causing restart
|
||||
self.device.click_record_clear()
|
||||
|
||||
def cafe_additional(self) -> bool:
|
||||
if self.appear_then_click(INVENTORY):
|
||||
return True
|
||||
if self.appear_then_click(MOMOTALK_CLOSE):
|
||||
return True
|
||||
return False
|
||||
@ -21,12 +21,12 @@ class ScrimmageStatus(Enum):
|
||||
class Scrimmage(ScrimmageUI):
|
||||
@property
|
||||
def scrimmage_info(self):
|
||||
bounty = (SELECT_TRINITY, SELECT_GEHENNA, SELECT_MILLENNIUM)
|
||||
scrimmage = (SELECT_TRINITY, SELECT_GEHENNA, SELECT_MILLENNIUM)
|
||||
check = (CHECK_TRINITY, CHECK_GEHENNA, CHECK_MILLENNIUM)
|
||||
stage = (self.config.Trinity_Stage, self.config.Gehenna_Stage, self.config.Millennium_Stage)
|
||||
count = (self.config.Trinity_Count, self.config.Gehenna_Count, self.config.Millennium_Count)
|
||||
ap = (10 if stage == 1 else 15 for stage in stage)
|
||||
info = zip(bounty, check, stage, count, ap)
|
||||
info = zip(scrimmage, check, stage, count, ap)
|
||||
return filter(lambda x: x[3] > 0, info)
|
||||
|
||||
@property
|
||||
@ -38,7 +38,7 @@ class Scrimmage(ScrimmageUI):
|
||||
return task
|
||||
|
||||
def error_handler(self):
|
||||
action = self.config.Bounty_OnError
|
||||
action = self.config.Scrimmage_OnError
|
||||
if action == 'stop':
|
||||
raise RequestHumanTakeover
|
||||
elif action == 'skip':
|
||||
|
||||
Loading…
Reference in New Issue
Block a user