0
0
mirror of https://github.com/ok-oldking/ok-wuthering-waves.git synced 2025-04-24 08:25:16 +00:00

优化声骸拾取

This commit is contained in:
firedcto@gmail.com 2025-04-07 10:03:49 +08:00
parent 7d3210532a
commit 51d9e4e441
17 changed files with 995 additions and 337 deletions

BIN
assets/images/75.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
assets/images/76.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

File diff suppressed because it is too large Load Diff

View File

@ -160,6 +160,8 @@ config = {
["src.task.TacetTask", "TacetTask"],
["src.task.FarmEchoTask", "FarmEchoTask"],
["src.task.FarmWorldBossTask", "FarmWorldBossTask"],
# ["src.task.FarmMapTask", "FarmMapTask"],
# ["src.task.Farm13CTask", "Farm13CTask"],
["ok", "DiagnosisTask"],
], 'trigger_tasks': [
["src.task.AutoCombatTask", "AutoCombatTask"],

View File

@ -56,7 +56,7 @@ numpy==2.2.4
# shapely
rapidocr==2.0.6
# via -r .\requirements.in
ok-script==0.0.521
ok-script==0.0.522
# via -r .\requirements.in
omegaconf==2.3.0
# via ok-rapidocr-dml

View File

@ -1,7 +1,6 @@
from qfluentwidgets import FluentIcon
from ok import Logger, BaseScene
from src.task.BaseWWTask import BaseWWTask
logger = Logger.get_logger(__name__)

View File

@ -20,6 +20,7 @@ class AutoCombatTask(BaseCombatTask, TriggerTask):
self.scene: WWScene | None = None
self.default_config.update({
'Auto Target': True,
'Auto Pick Echo After Combat': True,
})
self.config_description = {
'Auto Target': 'Turn off to enable auto combat only when manually target enemy using middle click'
@ -38,9 +39,10 @@ class AutoCombatTask(BaseCombatTask, TriggerTask):
break
except NotInCombatException as e:
logger.info(f'auto_combat_task_out_of_combat {e}')
# if self.debug:
# self.screenshot(f'auto_combat_task_out_of_combat {e}')
break
if ret:
self.combat_end()
if self.config.get('Auto Pick Echo After Combat'):
while self.yolo_find_echo(use_color=False, walk=False, turn=False)[1]:
pass
return ret

View File

@ -3,7 +3,7 @@ from qfluentwidgets import FluentIcon
from ok import FindFeature, Logger
from ok import TriggerTask
from src.scene.WWScene import WWScene
from src.task.BaseWWTask import BaseWWTask
from src.task.BaseWWTask import BaseWWTask, f_white_color
logger = Logger.get_logger(__name__)
@ -69,8 +69,3 @@ class AutoPickTask(TriggerTask, BaseWWTask, FindFeature):
return True
self.next_frame()
f_white_color = {
'r': (235, 255), # Red range
'g': (235, 255), # Green range
'b': (235, 255) # Blue range
}

View File

@ -8,10 +8,15 @@ from ok import BaseTask, Logger, find_boxes_by_name, og, Box
from ok import CannotFindException
import cv2
logger = Logger.get_logger(__name__)
number_re = re.compile(r'^(\d+)$')
stamina_re = re.compile(r'^(\d+)/(\d+)$')
f_white_color = {
'r': (235, 255), # Red range
'g': (235, 255), # Green range
'b': (235, 255) # Blue range
}
class BaseWWTask(BaseTask):
map_zoomed = False
@ -112,7 +117,21 @@ class BaseWWTask(BaseTask):
def find_f_with_text(self, target_text=None):
f = self.find_one('pick_up_f_hcenter_vcenter', box=self.f_search_box, threshold=0.8)
if f and target_text:
if not f:
return None
start = time.time()
percent = 0.0
while time.time() - start < 1:
percent = self.calculate_color_percentage(f_white_color, f)
if percent > 0.5:
break
self.next_frame()
self.log_debug(f'f white color percent: {percent} wait')
if percent < 0.5:
return None
if target_text:
search_text_box = f.copy(x_offset=f.width * 5, width_offset=f.width * 7, height_offset=1.5 * f.height,
y_offset=-0.8 * f.height, name='search_text_box')
text = self.ocr(box=search_text_box, match=target_text, target_height=540)
@ -129,11 +148,17 @@ class BaseWWTask(BaseTask):
start = time.time()
ended = False
while time.time() - start < time_out:
self.next_frame()
if end_condition:
ended = end_condition()
if ended:
break
treasure_icon = find_function()
if isinstance(treasure_icon, list):
if len(treasure_icon) > 0:
treasure_icon = treasure_icon[0]
else:
treasure_icon = None
if not treasure_icon:
if not end_condition:
self.log_info('find_function not found, break')
@ -142,6 +167,7 @@ class BaseWWTask(BaseTask):
self.next_frame()
continue
x, y = treasure_icon.center()
self.draw_boxes(treasure_icon)
y = max(0, y - self.height_of_screen(0.05))
next_direction = self.get_direction(x, y, self.width, self.height)
if next_direction != last_direction:
@ -149,8 +175,8 @@ class BaseWWTask(BaseTask):
self.send_key_up(last_direction)
self.sleep(0.02)
last_direction = next_direction
self.send_key_down(next_direction)
self.next_frame()
if next_direction:
self.send_key_down(next_direction)
if last_direction:
self.send_key_up(last_direction)
self.sleep(0.02)
@ -375,7 +401,7 @@ class BaseWWTask(BaseTask):
logger.info(f"found a claim reward")
return True
def find_echo(self, threshold=0.5) -> Box:
def find_echo(self, threshold=0.5):
"""
Main function to load ONNX model, perform inference, draw bounding boxes, and display the output image.
@ -387,10 +413,25 @@ class BaseWWTask(BaseTask):
list: List of dictionaries containing detection information such as class_id, class_name, confidence, etc.
"""
# Load the ONNX model
boxes = og.my_app.yolo_detect(self.frame, label=12)
boxes = og.my_app.yolo_detect(self.frame, threshold=threshold, label=12)
ret = sorted(boxes, key=lambda detection: abs(detection.y - self.height/2), reverse=True)
return ret
def yolo_find_all(self, threshold=0.3):
"""
Main function to load ONNX model, perform inference, draw bounding boxes, and display the output image.
Args:
onnx_model (str): Path to the ONNX model.
input_image (ndarray): Path to the input image.
Returns:
list: List of dictionaries containing detection information such as class_id, class_name, confidence, etc.
"""
# Load the ONNX model
boxes = og.my_app.yolo_detect(self.frame, threshold=threshold, label=-1)
ret = sorted(boxes, key=lambda detection: detection.confidence, reverse=True)
if ret:
return ret[0]
return ret
def pick_echo(self):
if self.find_f_with_text(target_text=self.absorb_echo_text()):
@ -398,22 +439,26 @@ class BaseWWTask(BaseTask):
if not self.handle_claim_button():
return True
def yolo_find_echo(self, use_color=True):
def yolo_find_echo(self, use_color=True, walk=True, turn=True):
max_echo_count = 0
if self.pick_echo():
self.sleep(0.5)
return True
return True, True
front_box = self.box_of_screen(0.35, 0.35, 0.65, 0.53, hcenter=True)
color_threshold = 0.02
for i in range(4):
self.center_camera()
echo = self.find_echo()
if turn:
self.center_camera()
echos = self.find_echo()
if self.debug:
self.draw_boxes('echo', echo)
if echo and echo.center()[1] > self.height_of_screen(0.55):
self.log_info(f'yolo found echo {echo}')
self.draw_boxes('echo', echos)
max_echo_count = max(max_echo_count, len(echos))
self.log_debug(f'max_echo_count {max_echo_count}')
if echos and echos[0].center()[1] < self.height_of_screen(0.45):
self.log_info(f'yolo found echo {echos}')
if self.debug:
self.screenshot('echo_yolo_picked')
return self.walk_to_box(self.find_echo, time_out=15, end_condition=self.pick_echo)
return self.walk_to_box(self.find_echo, time_out=15, end_condition=self.pick_echo), max_echo_count > 1
if use_color:
color_percent = self.calculate_color_percentage(echo_color, front_box)
self.log_debug(f'pick_echo color_percent:{color_percent}')
@ -421,17 +466,26 @@ class BaseWWTask(BaseTask):
if self.debug:
self.screenshot('echo_color_picked')
self.log_debug(f'found color_percent {color_percent} > {color_threshold}, walk now')
return self.walk_find_echo()
return self.walk_find_echo(), max_echo_count > 1
if not turn and i==0:
return False, max_echo_count > 1
self.send_key('a', down_time=0.05)
self.sleep(0.5)
self.center_camera()
picked = self.walk_find_echo()
return picked
if walk:
picked = self.walk_find_echo()
return picked, max_echo_count > 1
return False, max_echo_count > 1
def center_camera(self):
self.click(0.5, 0.5, down_time=0.2, after_sleep=0.5, key='middle')
def turn_direction(self, direction):
if direction != 'w':
self.send_key(direction, down_time=0.05, after_sleep=0.5)
self.center_camera()
def walk_find_echo(self, backward_time=1):
if self.walk_until_f(time_out=4, backward_time=backward_time, target_text=self.absorb_echo_text(),
raise_if_not_found=False): # find and pick echo
@ -441,6 +495,7 @@ class BaseWWTask(BaseTask):
def incr_drop(self, dropped):
if dropped:
self.info['Echo Count'] = self.info.get('Echo Count', 0) + 1
self.info['Echo per Hour'] = round(self.info.get('Echo Count', 0) / max(time.time() - self.start_time, 1) * 3600)
def should_check_monthly_card(self):
if self.next_monthly_card_start > 0:

234
src/task/Farm13CTask.py Normal file
View File

@ -0,0 +1,234 @@
import math
import re
import time
import cv2
import numpy as np
from qfluentwidgets import FluentIcon
from ok import Logger
from src.task.BaseCombatTask import BaseCombatTask
from src.task.WWOneTimeTask import WWOneTimeTask
logger = Logger.get_logger(__name__)
class Farm13CTask(WWOneTimeTask, BaseCombatTask):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.icon = FluentIcon.FLAG
self.description = "Farm selected Tacet Suppression until out of stamina, will use the backup stamina, you need to be able to teleport from the menu(F2)"
self.name = "Tacet Suppression (Must explore first to be able to teleport)"
default_config = {
'Which Tacet Suppression to Farm': 1 # starts with 1
}
self.row_per_page = 5
self.total_number = 11
default_config.update(self.default_config)
self.config_description = {
'Which Tacet Suppression to Farm': 'the Nth number in the Tacet Suppression list (F2)',
}
self.default_config = default_config
self.max_star_distance = 1000
self.last_star = None
self.last_angle = None
self.stuck_keys = [['space', 0.2], ['a',2], ['d',2]]
self.stuck_index = 0
self.last_cords = None
self.cords_re = re.compile('^\d+,\d+,\d+$')
@property
def star_move_distance_threshold(self):
return self.height_of_screen(0.005)
def run(self):
self.last_star = None
self.last_angle = None
while True:
echos = self.yolo_find_all()
self.draw_boxes("yolo", echos)
self.sleep(0.1)
def get_cords(self):
cords = self.ocr(0.01, 0.95, 0.21, 1, match=self.cords_re, log=True)
if cords:
return cords[0].name
def get_angle(self):
arrow_template = self.get_feature_by_name('arrow')
original_mat = arrow_template.mat
max_conf = 0
max_angle = 0
max_target = None
max_mat = None
(h, w) = arrow_template.mat.shape[:2]
# self.log_debug(f'turn_east h:{h} w:{w}')
center = (w // 2, h // 2)
target_box = self.get_box_by_name('box_arrow')
# if self.debug:
# self.screenshot('arrow_original', original_ mat)
for angle in range(0, 360):
# Rotate the template image
rotation_matrix = cv2.getRotationMatrix2D(center, -angle, 1.0)
arrow_template.mat = cv2.warpAffine(original_mat, rotation_matrix, (w, h))
arrow_template.mask = np.where(np.all(arrow_template.mat == [0, 0, 0], axis=2), 0, 255).astype(np.uint8)
target = self.find_one(f'arrow_{angle}', box=target_box,
template=arrow_template, threshold=0.01)
# if self.debug and angle % 90 == 0:
# self.screenshot(f'arrow_rotated_{angle}', arrow_template.mat)
if target and target.confidence > max_conf:
max_conf = target.confidence
max_angle = angle
max_target = target
max_mat = arrow_template.mat
arrow_template.mat = original_mat
# arrow_template.mask = None
# if self.debug and max_mat is not None:
# self.screenshot('max_mat',frame=max_mat)
# self.log_debug(f'turn_east max_conf: {max_conf} {max_angle}')
return max_angle , max_target
def find_next_star(self):
stars = self.find_stars()
min_distance = self.max_star_distance
nearest_star = None
x2, y2 = self.get_box_by_name('box_minimap').center()
for star in stars:
if not self.last_star:
x1, y1 = star.center()
angle = calculate_angle_clockwise(x1, y1, x2, y2)
if self.last_angle is not None and abs(angle - self.last_angle) < 90:
self.log_debug(f'old path continue {abs(angle - self.last_angle)} {star}')
continue
self.last_star = star
self.log_debug(f'no last star return nearest {self.last_star} {self.last_angle}')
return -1, star
distance = star.center_distance(self.last_star)
if distance < min_distance:
min_distance = distance
nearest_star = star
if nearest_star:
self.last_star = nearest_star
self.log_debug(f'nearest_star: {min_distance} {nearest_star}')
return min_distance, nearest_star
def go_to_star(self):
current_direction = None
self.center_camera()
while True:
self.sleep(0.01)
if self.in_combat():
if current_direction is not None:
self.mouse_up(key='right')
self.send_key_up(current_direction)
current_direction = None
self.combat_once()
while self.yolo_find_echo(use_color=False, walk=False)[1]:
self.sleep(0.5)
cords = self.get_cords()
if cords and cords == self.last_cords:
logger.info(f'might be stuck, try jump')
self.send_key(self.stuck_keys[self.stuck_index % 3][0], down_time=self.stuck_keys[self.stuck_index % 3][1], after_sleep=0.5)
self.stuck_index += 1
continue
self.last_cords = cords
stars = self.find_stars()
if not stars:
self.log_info('cannot find any stars, stop farming', notify=True)
break
star = stars[0]
angle = self.get_angle_to_star(star)
if current_direction == 'w':
if 4 <= angle <= 90:
minor_adjust = 'd'
elif -90 <= angle <= -4:
minor_adjust = 'a'
else:
minor_adjust = None
if minor_adjust:
# if self.debug:
# self.screenshot(f'minor_adjust_{minor_adjust}_{angle}')
self.log_info(f'minor_adjust to {minor_adjust}_{angle}')
self.send_key_down(minor_adjust)
# self.center_camera()
self.sleep(0.3)
self.send_key_up(minor_adjust)
continue
if -45 <= angle <= 45:
new_direction = 'w'
elif 45 < angle <= 135:
new_direction = 'd'
elif -135 < angle <= -45:
new_direction = 'a'
else:
new_direction = 's'
if current_direction != new_direction:
self.log_info(f'changed direction {angle} {current_direction} -> {new_direction}')
if self.debug:
self.screenshot(f'{current_direction}_{new_direction}_{angle}')
if current_direction:
self.send_key_up(current_direction)
self.sleep(0.2)
self.turn_direction(new_direction)
self.send_key_down('w')
self.sleep(0.2)
self.mouse_down(key='right')
current_direction = 'w'
self.sleep(1)
if current_direction is not None:
self.mouse_up(key='right')
self.send_key_up(current_direction)
def get_angle_to_star(self, star):
x1, y1 = self.get_box_by_name('box_minimap').center()
x2, y2 = star.center()
target_angle = calculate_angle_clockwise(x1, y1, x2, y2)
my_angle = self.get_angle()[0]
if my_angle >= target_angle:
turn_angle = -(my_angle - target_angle)
else:
turn_angle = target_angle - my_angle
if turn_angle > 180:
turn_angle = 360 - turn_angle
if turn_angle < -180:
turn_angle = 360 + turn_angle
logger.debug(f'go to turn_angle {my_angle} {target_angle} {turn_angle}')
return turn_angle
def find_stars(self):
box_minimap = self.get_box_by_name('box_minimap')
stars = self.find_feature('star', threshold=0.5, box=box_minimap)
sorted_stars = sorted(stars, key=lambda star: - box_minimap.center_distance(star))
# if self.debug:
# self.screenshot('starts', show_box=True)
# self.screenshot('stars_mask', frame=mask_star(self.frame), show_box=True)
return sorted_stars
def calculate_angle_clockwise(x1, y1, x2, y2):
"""
Calculates angle (radians) from horizontal right to line (x1,y1)->(x2,y2).
Positive clockwise, negative counter-clockwise.
"""
dx = x2 - x1
dy = y2 - y1
# math.atan2(dy, dx) gives angle from positive x-axis, positive CCW.
# Negate for positive CW convention.
return math.degrees(math.atan2(dy, dx))
star_color = {
'r': (190, 220), # Red range
'g': (190, 220), # Green range
'b': (190, 220) # Blue range
}
def mask_star(image):
# return image
return create_color_mask(image, star_color)
def create_color_mask(image, color_ranges):
mask = cv2.inRange(image, (color_ranges['b'][0], color_ranges['g'][0], color_ranges['r'][0]), (color_ranges['b'][1], color_ranges['g'][1], color_ranges['r'][1]))
return mask

View File

@ -1,3 +1,5 @@
import time
from qfluentwidgets import FluentIcon
from ok import Logger
@ -27,7 +29,7 @@ class FarmEchoTask(WWOneTimeTask, BaseCombatTask):
self.add_exit_after_config()
def run(self):
super().run()
WWOneTimeTask.run(self)
self.set_check_monthly_card()
self.ensure_main(time_out=180)
if not self.in_team()[0]:
@ -54,7 +56,7 @@ class FarmEchoTask(WWOneTimeTask, BaseCombatTask):
self.combat_once()
logger.info(f'farm echo move {self.config.get("Boss")} walk_until_f to find echo')
dropped = self.yolo_find_echo()
dropped = self.yolo_find_echo()[0]
self.incr_drop(dropped)
self.sleep(0.5)
self.send_key('esc')
@ -63,10 +65,6 @@ class FarmEchoTask(WWOneTimeTask, BaseCombatTask):
self.wait_in_team_and_world(time_out=120)
self.sleep(2)
def incr_drop(self, dropped):
if dropped:
self.info['Echo Count'] = self.info.get('Echo Count', 0) + 1
def choose_level(self, start):
y = 0.17
x = 0.15

250
src/task/FarmMapTask.py Normal file
View File

@ -0,0 +1,250 @@
import math
import re
import time
import cv2
import numpy as np
from qfluentwidgets import FluentIcon
from ok import Logger
from src.task.BaseCombatTask import BaseCombatTask
from src.task.WWOneTimeTask import WWOneTimeTask
logger = Logger.get_logger(__name__)
class FarmMapTask(WWOneTimeTask, BaseCombatTask):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.icon = FluentIcon.FLAG
self.description = "Farm selected Tacet Suppression until out of stamina, will use the backup stamina, you need to be able to teleport from the menu(F2)"
self.name = "Tacet Suppression (Must explore first to be able to teleport)"
default_config = {
'Which Tacet Suppression to Farm': 1 # starts with 1
}
self.row_per_page = 5
self.total_number = 11
default_config.update(self.default_config)
self.config_description = {
'Which Tacet Suppression to Farm': 'the Nth number in the Tacet Suppression list (F2)',
}
self.default_config = default_config
self.max_star_distance = 1000
self.last_star = None
self.last_angle = None
self.stuck_keys = [['space', 0.2], ['a',2], ['d',2]]
self.stuck_index = 0
self.last_cords = None
self.cords_re = re.compile('^\d+,\d+,\d+$')
@property
def star_move_distance_threshold(self):
return self.height_of_screen(0.02)
def run(self):
self.last_star = None
self.last_angle = None
self.go_to_star()
def get_cords(self):
cords = self.ocr(0.01, 0.95, 0.21, 1, match=self.cords_re, log=True)
if cords:
return cords[0].name
def get_angle(self):
arrow_template = self.get_feature_by_name('arrow')
original_mat = arrow_template.mat
max_conf = 0
max_angle = 0
max_target = None
max_mat = None
(h, w) = arrow_template.mat.shape[:2]
# self.log_debug(f'turn_east h:{h} w:{w}')
center = (w // 2, h // 2)
target_box = self.get_box_by_name('box_arrow')
# if self.debug:
# self.screenshot('arrow_original', original_ mat)
for angle in range(0, 360):
# Rotate the template image
rotation_matrix = cv2.getRotationMatrix2D(center, -angle, 1.0)
arrow_template.mat = cv2.warpAffine(original_mat, rotation_matrix, (w, h))
arrow_template.mask = np.where(np.all(arrow_template.mat == [0, 0, 0], axis=2), 0, 255).astype(np.uint8)
target = self.find_one(f'arrow_{angle}', box=target_box,
template=arrow_template, threshold=0.01)
# if self.debug and angle % 90 == 0:
# self.screenshot(f'arrow_rotated_{angle}', arrow_template.mat)
if target and target.confidence > max_conf:
max_conf = target.confidence
max_angle = angle
max_target = target
max_mat = arrow_template.mat
arrow_template.mat = original_mat
# arrow_template.mask = None
# if self.debug and max_mat is not None:
# self.screenshot('max_mat',frame=max_mat)
# self.log_debug(f'turn_east max_conf: {max_conf} {max_angle}')
return max_angle , max_target
def set_last_angle(self, angle):
self.last_angle = angle
self.info_set('Last Angle', angle)
def find_next_star(self):
stars = self.find_stars()
min_distance = self.max_star_distance
nearest_star = None
x2, y2 = self.get_box_by_name('box_minimap').center()
for star in stars:
if not self.last_star:
x1, y1 = star.center()
angle = calculate_angle_clockwise(x1, y1, x2, y2)
if self.last_angle is not None and abs(angle - self.last_angle) < 90:
self.log_debug(f'old path continue {abs(angle - self.last_angle)} {star}')
continue
self.last_star = star
self.log_debug(f'no last star return nearest {self.last_star} {self.last_angle}')
return -1, star, 360 - angle
distance = star.center_distance(self.last_star)
if distance < min_distance:
min_distance = distance
nearest_star = star
if nearest_star:
self.last_star = nearest_star
self.log_debug(f'nearest_star: {min_distance} {nearest_star}')
return min_distance, nearest_star, -1
def go_to_star(self):
return self.yolo_find_echo(use_color=False, walk=False)
current_direction = None
self.center_camera()
current_adjust = None
while True:
self.sleep(0.01)
if self.in_combat():
if current_direction is not None:
self.mouse_up(key='right')
self.send_key_up(current_direction)
current_direction = None
self.combat_once()
while self.yolo_find_echo(use_color=False, walk=False)[1]:
self.sleep(0.5)
continue
distance, star, last_angle = self.find_next_star()
if not star:
self.log_info('cannot find any stars, stop farming', notify=True)
break
if distance == 0:
logger.info(f'might be stuck, try {[self.stuck_index % 3]}')
self.send_key(self.stuck_keys[self.stuck_index % 3][0], down_time=self.stuck_keys[self.stuck_index % 3][1], after_sleep=0.5)
self.stuck_index += 1
continue
elif distance > self.star_move_distance_threshold:
if self.debug:
self.screenshot('star_moved')
self.log_info(f'star moved continue forward {distance} {self.star_move_distance_threshold}')
self.last_star = None
self.sleep(4)
continue
if last_angle > 0:
self.set_last_angle(last_angle)
angle = self.get_angle_to_star(star)
if current_direction == 'w':
if 4 <= angle <= 70:
minor_adjust = 'd'
elif -70 <= angle <= -4:
minor_adjust = 'a'
else:
minor_adjust = None
if minor_adjust != current_adjust and minor_adjust is not None:
if current_adjust:
self.send_key_up(current_adjust)
# if self.debug:
# self.screenshot(f'minor_adjust_{minor_adjust}_{angle}')
self.log_info(f'minor_adjust to {minor_adjust}_{angle}')
self.send_key_down(minor_adjust)
# self.center_camera()
self.sleep(0.5)
current_adjust = minor_adjust
continue
if current_adjust:
self.send_key_up(current_adjust)
current_adjust = None
if -45 <= angle <= 45:
new_direction = 'w'
elif 45 < angle <= 135:
new_direction = 'd'
elif -135 < angle <= -45:
new_direction = 'a'
else:
new_direction = 's'
if current_direction != new_direction:
self.log_info(f'changed direction {angle} {current_direction} -> {new_direction}')
if self.debug:
self.screenshot(f'{current_direction}_{new_direction}_{angle}')
if current_direction:
self.send_key_up(current_direction)
self.sleep(0.2)
self.turn_direction(new_direction)
self.send_key_down('w')
self.sleep(0.2)
self.mouse_down(key='right')
current_direction = 'w'
self.sleep(1)
if current_direction is not None:
self.mouse_up(key='right')
self.send_key_up(current_direction)
def get_angle_to_star(self, star):
x1, y1 = self.get_box_by_name('box_minimap').center()
x2, y2 = star.center()
target_angle = calculate_angle_clockwise(x1, y1, x2, y2)
my_angle = self.get_angle()[0]
if my_angle >= target_angle:
turn_angle = -(my_angle - target_angle)
else:
turn_angle = target_angle - my_angle
if turn_angle > 180:
turn_angle = 360 - turn_angle
if turn_angle < -180:
turn_angle = 360 + turn_angle
logger.debug(f'go to turn_angle {my_angle} {target_angle} {turn_angle}')
return turn_angle
def find_stars(self):
box_minimap = self.get_box_by_name('box_minimap')
stars = self.find_feature('star', threshold=0.65, box=box_minimap)
sorted_stars = sorted(stars, key=lambda star: - box_minimap.center_distance(star))
# if self.debug:
# self.screenshot('starts', show_box=True)
# self.screenshot('stars_mask', frame=mask_star(self.frame), show_box=True)
return sorted_stars
def calculate_angle_clockwise(x1, y1, x2, y2):
"""
Calculates angle (radians) from horizontal right to line (x1,y1)->(x2,y2).
Positive clockwise, negative counter-clockwise.
"""
dx = x2 - x1
dy = y2 - y1
# math.atan2(dy, dx) gives angle from positive x-axis, positive CCW.
# Negate for positive CW convention.
return math.degrees(math.atan2(dy, dx))
star_color = {
'r': (190, 220), # Red range
'g': (190, 220), # Green range
'b': (190, 220) # Blue range
}
def mask_star(image):
# return image
return create_color_mask(image, star_color)
def create_color_mask(image, color_ranges):
mask = cv2.inRange(image, (color_ranges['b'][0], color_ranges['g'][0], color_ranges['r'][0]), (color_ranges['b'][1], color_ranges['g'][1], color_ranges['r'][1]))
return mask

View File

@ -49,7 +49,7 @@ class FarmWorldBossTask(WWOneTimeTask, BaseCombatTask):
# not current in use because not stable, right now using one click to scroll down
def run(self):
super().run()
WWOneTimeTask.run(self)
self.ensure_main(time_out=180)
self.set_check_monthly_card()
count = 0
@ -90,7 +90,7 @@ class FarmWorldBossTask(WWOneTimeTask, BaseCombatTask):
self.sleep(5)
logger.info(f'farm echo move forward walk_until_f to find echo')
dropped = self.yolo_find_echo()
dropped = self.yolo_find_echo()[0]
self.incr_drop(dropped)
if count < 2:

View File

@ -5,4 +5,6 @@ class WWOneTimeTask:
def run(self):
mouse_reset_task = self.executor.get_task_by_class(MouseResetTask)
mouse_reset_task.run()
mouse_reset_task.run()
self.executor.interaction.activate()
self.sleep(0.5)

33
tests/TestMap.py Normal file
View File

@ -0,0 +1,33 @@
import unittest
from config import config
from ok.test.TaskTestCase import TaskTestCase
from src.task.FarmMapTask import FarmMapTask
config['debug'] = True
class TestTacet(TaskTestCase):
task_class = FarmMapTask
config = config
def test_find_treasure_icon(self):
self.set_image('tests/images/angle_130.png')
angle, box = self.task.get_angle()
self.logger.info(f'test_find_treasure_icon {angle, box}')
self.assertTrue(100 <= angle <= 200)
def test_find_stars(self):
self.set_image('tests/images/stars.png')
stars = self.task.find_stars()
self.logger.info(f'find_stars {stars}')
self.assertEqual(3, len(stars))
angle1 = self.task.get_angle_to_star(stars[0])
angle2 = self.task.get_angle_to_star(stars[1])
angle3 = self.task.get_angle_to_star(stars[2])
self.assertTrue(100 <= angle1 <= 180)
self.assertTrue(0 <= angle2 <= 90)
self.assertTrue(-45 <= angle3 <= 0)
if __name__ == '__main__':
unittest.main()

BIN
tests/images/angle_130.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 MiB

BIN
tests/images/stars.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB