mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-09-28 06:45:03 +00:00
Fix: Retry when joystick contact lost
This commit is contained in:
parent
d314bab51c
commit
a800bdb214
@ -152,20 +152,21 @@ class ModuleBase:
|
|||||||
prev_image = image
|
prev_image = image
|
||||||
timer.reset()
|
timer.reset()
|
||||||
|
|
||||||
def image_crop(self, button):
|
def image_crop(self, button, copy=True):
|
||||||
"""Extract the area from image.
|
"""Extract the area from image.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
button(Button, tuple): Button instance or area tuple.
|
button(Button, tuple): Button instance or area tuple.
|
||||||
|
copy:
|
||||||
"""
|
"""
|
||||||
if isinstance(button, Button):
|
if isinstance(button, Button):
|
||||||
return crop(self.device.image, button.area)
|
return crop(self.device.image, button.area, copy=copy)
|
||||||
elif isinstance(button, ButtonWrapper):
|
elif isinstance(button, ButtonWrapper):
|
||||||
return crop(self.device.image, button.area)
|
return crop(self.device.image, button.area, copy=copy)
|
||||||
elif hasattr(button, 'area'):
|
elif hasattr(button, 'area'):
|
||||||
return crop(self.device.image, button.area)
|
return crop(self.device.image, button.area, copy=copy)
|
||||||
else:
|
else:
|
||||||
return crop(self.device.image, button)
|
return crop(self.device.image, button, copy=copy)
|
||||||
|
|
||||||
def image_color_count(self, button, color, threshold=221, count=50):
|
def image_color_count(self, button, color, threshold=221, count=50):
|
||||||
"""
|
"""
|
||||||
@ -178,9 +179,14 @@ class ModuleBase:
|
|||||||
Returns:
|
Returns:
|
||||||
bool:
|
bool:
|
||||||
"""
|
"""
|
||||||
image = self.image_crop(button)
|
if isinstance(button, np.ndarray):
|
||||||
mask = color_similarity_2d(image, color=color) > threshold
|
image = button
|
||||||
return np.sum(mask) > count
|
else:
|
||||||
|
image = self.image_crop(button, copy=False)
|
||||||
|
mask = color_similarity_2d(image, color=color)
|
||||||
|
cv2.inRange(mask, threshold, 255, dst=mask)
|
||||||
|
sum_ = cv2.countNonZero(mask)
|
||||||
|
return sum_ > count
|
||||||
|
|
||||||
def image_color_button(self, area, color, color_threshold=250, encourage=5, name='COLOR_BUTTON'):
|
def image_color_button(self, area, color, color_threshold=250, encourage=5, name='COLOR_BUTTON'):
|
||||||
"""
|
"""
|
||||||
|
@ -106,7 +106,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo
|
|||||||
continue
|
continue
|
||||||
if self.appear(COMBAT_TEAM_PREPARE):
|
if self.appear(COMBAT_TEAM_PREPARE):
|
||||||
self.interval_reset(COMBAT_PREPARE)
|
self.interval_reset(COMBAT_PREPARE)
|
||||||
self._map_A_timer.reset()
|
self.map_A_timer.reset()
|
||||||
if self.appear(COMBAT_PREPARE, interval=2):
|
if self.appear(COMBAT_PREPARE, interval=2):
|
||||||
if not self.handle_combat_prepare():
|
if not self.handle_combat_prepare():
|
||||||
return False
|
return False
|
||||||
|
@ -248,7 +248,7 @@ class Dungeon(DungeonUI, DungeonEvent, Combat):
|
|||||||
area = area_offset((-50, -150, 0, 0), offset=self.config.ASSETS_RESOLUTION)
|
area = area_offset((-50, -150, 0, 0), offset=self.config.ASSETS_RESOLUTION)
|
||||||
|
|
||||||
skip_first_screenshot = True
|
skip_first_screenshot = True
|
||||||
self._map_A_timer.reset()
|
self.map_A_timer.reset()
|
||||||
handled = False
|
handled = False
|
||||||
while 1:
|
while 1:
|
||||||
if skip_first_screenshot:
|
if skip_first_screenshot:
|
||||||
@ -265,7 +265,7 @@ class Dungeon(DungeonUI, DungeonEvent, Combat):
|
|||||||
logger.info(f'No destructible object')
|
logger.info(f'No destructible object')
|
||||||
if not handled:
|
if not handled:
|
||||||
break
|
break
|
||||||
if self._map_A_timer.reached():
|
if self.map_A_timer.reached():
|
||||||
break
|
break
|
||||||
|
|
||||||
return handled
|
return handled
|
||||||
|
@ -130,7 +130,7 @@ class MapControl(MapControlJoystick):
|
|||||||
if allow_straight_run and diff < 15:
|
if allow_straight_run and diff < 15:
|
||||||
logger.info(f'Approaching target, diff={round(diff, 1)}, disallow straight_run')
|
logger.info(f'Approaching target, diff={round(diff, 1)}, disallow straight_run')
|
||||||
direction_interval = Timer(0.2)
|
direction_interval = Timer(0.2)
|
||||||
self._map_2x_run_timer.reset()
|
self.map_2x_run_timer.reset()
|
||||||
allow_straight_run = False
|
allow_straight_run = False
|
||||||
if allow_run and diff < 7:
|
if allow_run and diff < 7:
|
||||||
logger.info(f'Approaching target, diff={round(diff, 1)}, disallow run')
|
logger.info(f'Approaching target, diff={round(diff, 1)}, disallow run')
|
||||||
@ -244,7 +244,7 @@ class MapControl(MapControlJoystick):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Control test in Himeko trail
|
# Control test in Himeko trail
|
||||||
# Must manually enter Himeko trail first and dismiss popup
|
# Must manually enter Himeko trail first and dismiss popup
|
||||||
self = MapControl('alas')
|
self = MapControl('src')
|
||||||
self.minimap.set_plane('Jarilo_BackwaterPass', floor='F1')
|
self.minimap.set_plane('Jarilo_BackwaterPass', floor='F1')
|
||||||
self.device.screenshot()
|
self.device.screenshot()
|
||||||
self.minimap.init_position((519, 359))
|
self.minimap.init_position((519, 359))
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import math
|
import math
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from module.base.timer import Timer
|
from module.base.timer import Timer
|
||||||
from module.device.method.maatouch import MaatouchBuilder
|
from module.device.method.maatouch import MaatouchBuilder
|
||||||
from module.device.method.minitouch import CommandBuilder, insert_swipe, random_normal_distribution
|
from module.device.method.minitouch import CommandBuilder, insert_swipe, random_normal_distribution
|
||||||
@ -36,10 +39,8 @@ class JoystickContact:
|
|||||||
Can not lift finger when:
|
Can not lift finger when:
|
||||||
- Process is force terminated
|
- Process is force terminated
|
||||||
"""
|
"""
|
||||||
builder = self.builder
|
|
||||||
if self.is_downed:
|
if self.is_downed:
|
||||||
builder.up().commit()
|
self.up()
|
||||||
builder.send()
|
|
||||||
logger.info('JoystickContact ends')
|
logger.info('JoystickContact ends')
|
||||||
else:
|
else:
|
||||||
logger.info('JoystickContact ends but it was never downed')
|
logger.info('JoystickContact ends but it was never downed')
|
||||||
@ -98,6 +99,13 @@ class JoystickContact:
|
|||||||
point = (int(round(point[0])), int(round(point[1])))
|
point = (int(round(point[0])), int(round(point[1])))
|
||||||
return point
|
return point
|
||||||
|
|
||||||
|
def up(self):
|
||||||
|
builder = self.builder
|
||||||
|
if self.is_downed:
|
||||||
|
builder.up().commit()
|
||||||
|
builder.send()
|
||||||
|
self.prev_point = None
|
||||||
|
|
||||||
def set(self, direction, run=True):
|
def set(self, direction, run=True):
|
||||||
"""
|
"""
|
||||||
Set joystick to given position
|
Set joystick to given position
|
||||||
@ -110,6 +118,13 @@ class JoystickContact:
|
|||||||
point = JoystickContact.direction2screen(direction, run=run)
|
point = JoystickContact.direction2screen(direction, run=run)
|
||||||
builder = self.builder
|
builder = self.builder
|
||||||
|
|
||||||
|
if self.is_downed and not self.main.joystick_speed():
|
||||||
|
if self.main.joystick_lost_timer.reached():
|
||||||
|
logger.warning(f'Joystick contact lost: {self.main.joystick_lost_timer}, re-down')
|
||||||
|
self.up()
|
||||||
|
else:
|
||||||
|
self.main.joystick_lost_timer.reset()
|
||||||
|
|
||||||
if self.is_downed:
|
if self.is_downed:
|
||||||
points = insert_swipe(p0=self.prev_point, p3=point, speed=20)
|
points = insert_swipe(p0=self.prev_point, p3=point, speed=20)
|
||||||
for point in points[1:]:
|
for point in points[1:]:
|
||||||
@ -121,20 +136,56 @@ class JoystickContact:
|
|||||||
# Character starts moving, RUN button is still unavailable in a short time.
|
# Character starts moving, RUN button is still unavailable in a short time.
|
||||||
# Assume available in 0.3s
|
# Assume available in 0.3s
|
||||||
# We still have reties if 0.3s is incorrect.
|
# We still have reties if 0.3s is incorrect.
|
||||||
self.main._map_2x_run_timer.set_current(0.7)
|
self.main.map_2x_run_timer.set_current(0.7)
|
||||||
|
self.main.joystick_lost_timer.reset()
|
||||||
|
|
||||||
self.prev_point = point
|
self.prev_point = point
|
||||||
|
|
||||||
|
|
||||||
class MapControlJoystick(UI):
|
class MapControlJoystick(UI):
|
||||||
_map_A_timer = Timer(1)
|
map_A_timer = Timer(1)
|
||||||
_map_E_timer = Timer(1)
|
map_E_timer = Timer(1)
|
||||||
_map_2x_run_timer = Timer(1)
|
map_2x_run_timer = Timer(1)
|
||||||
|
|
||||||
|
joystick_lost_timer = Timer(1, count=2)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def joystick_center(self) -> tuple[float, float]:
|
def joystick_center(self) -> tuple[int, int]:
|
||||||
x1, y1, x2, y2 = JOYSTICK.area
|
x1, y1, x2, y2 = JOYSTICK.area
|
||||||
return (x1 + x2) / 2, (y1 + y2) / 2
|
return int((x1 + x2) // 2), int((y1 + y2) // 2)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def DirectionRemapData(self):
|
||||||
|
d = JoystickContact.RADIUS_RUN[1] * 2
|
||||||
|
mx = np.zeros((d, d), dtype=np.float32)
|
||||||
|
my = np.zeros((d, d), dtype=np.float32)
|
||||||
|
for i in range(d):
|
||||||
|
for j in range(d):
|
||||||
|
mx[i, j] = d / 2 + i / 2 * np.cos(2 * np.pi * j / d)
|
||||||
|
my[i, j] = d / 2 + i / 2 * np.sin(2 * np.pi * j / d)
|
||||||
|
return mx, my
|
||||||
|
|
||||||
|
def joystick_speed(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
str: 'run', 'walk', ''
|
||||||
|
"""
|
||||||
|
# About 1.5ms
|
||||||
|
x, y = self.joystick_center
|
||||||
|
radius = JoystickContact.RADIUS_RUN[1]
|
||||||
|
image = self.image_crop((x - radius, y - radius, x + radius, y + radius), copy=False)
|
||||||
|
image = cv2.remap(image, *self.DirectionRemapData, cv2.INTER_CUBIC)
|
||||||
|
|
||||||
|
# 190~205
|
||||||
|
run = image[185:210, :]
|
||||||
|
if self.image_color_count(run, color=(223, 199, 145), threshold=221, count=100):
|
||||||
|
return 'run'
|
||||||
|
# 90~100
|
||||||
|
walk = image[85:105, :]
|
||||||
|
if self.image_color_count(walk, color=(235, 235, 235), threshold=221, count=50):
|
||||||
|
return 'walk'
|
||||||
|
|
||||||
|
return ''
|
||||||
|
|
||||||
def map_get_technique_points(self):
|
def map_get_technique_points(self):
|
||||||
"""
|
"""
|
||||||
@ -162,9 +213,9 @@ class MapControlJoystick(UI):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: If clicked.
|
bool: If clicked.
|
||||||
"""
|
"""
|
||||||
if self._map_A_timer.reached():
|
if self.map_A_timer.reached():
|
||||||
self.device.click(A_BUTTON)
|
self.device.click(A_BUTTON)
|
||||||
self._map_A_timer.reset()
|
self.map_A_timer.reset()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -177,9 +228,9 @@ class MapControlJoystick(UI):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: If clicked.
|
bool: If clicked.
|
||||||
"""
|
"""
|
||||||
if self._map_E_timer.reached():
|
if self.map_E_timer.reached():
|
||||||
self.device.click(E_BUTTON)
|
self.device.click(E_BUTTON)
|
||||||
self._map_E_timer.reset()
|
self.map_E_timer.reset()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -194,13 +245,13 @@ class MapControlJoystick(UI):
|
|||||||
"""
|
"""
|
||||||
is_running = self.image_color_count(RUN_BUTTON, color=(208, 183, 138), threshold=221, count=100)
|
is_running = self.image_color_count(RUN_BUTTON, color=(208, 183, 138), threshold=221, count=100)
|
||||||
|
|
||||||
if run and not is_running and self._map_2x_run_timer.reached():
|
if run and not is_running and self.map_2x_run_timer.reached():
|
||||||
self.device.click(RUN_BUTTON)
|
self.device.click(RUN_BUTTON)
|
||||||
self._map_2x_run_timer.reset()
|
self.map_2x_run_timer.reset()
|
||||||
return True
|
return True
|
||||||
if not run and is_running and self._map_2x_run_timer.reached():
|
if not run and is_running and self.map_2x_run_timer.reached():
|
||||||
self.device.click(RUN_BUTTON)
|
self.device.click(RUN_BUTTON)
|
||||||
self._map_2x_run_timer.reset()
|
self.map_2x_run_timer.reset()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
Loading…
Reference in New Issue
Block a user