diff --git a/MANIFEST.in b/MANIFEST.in index 1893aa9..d1c987c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,7 @@ include fishy/ProvisionsChalutier.zip include fishy/FooAddon.zip include fishy/fishybot_logo.png include fishy/sound.mp3 +include fishy/beep.wav recursive-include tests * recursive-exclude * __pycache__ diff --git a/fishy/beep.wav b/fishy/beep.wav new file mode 100644 index 0000000..f837519 Binary files /dev/null and b/fishy/beep.wav differ diff --git a/fishy/engine/fullautofisher/controls.py b/fishy/engine/fullautofisher/controls.py index 82e4419..03f479d 100644 --- a/fishy/engine/fullautofisher/controls.py +++ b/fishy/engine/fullautofisher/controls.py @@ -1,36 +1,46 @@ import logging -from fishy.helper import hotkey +from fishy.helper import hotkey, helper from fishy.engine.fullautofisher.engine import FullAuto -from fishy.helper.config import config from fishy.helper.hotkey import Key def get_controls(engine: FullAuto): - from fishy.engine.fullautofisher.calibrate import Calibrate from fishy.engine.fullautofisher.recorder import Recorder from fishy.engine.fullautofisher.player import Player controls = [ - # ("MAIN", { - # Key.RIGHT: Player(engine).start_route, - # Key.UP: Calibrate(engine).callibrate, - # Key.LEFT: Recorder(engine).start_recording, - # Key.DOWN: change_state - # }), - # ("COODS", { - # Key.RIGHT: print_coods, - # Key.UP: engine.update_crop, - # Key.LEFT: toggle_show, - # Key.DOWN: change_state - # }), - # ("TEST1", { - # Key.RIGHT: set_target, - # Key.UP: rotate_to_90, - # Key.LEFT: move_to_target, - # Key.DOWN: change_state - # }) + ("MODE_SELECT", { + Key.RIGHT: (lambda: engine.controls.select_mode("CALIBRATE"), "calibrate mode"), + Key.UP: (lambda: engine.controls.select_mode("PLAY"), "play mode"), + Key.LEFT: (lambda: engine.controls.select_mode("RECORD"), "record mode"), + Key.DOWN: (lambda: engine.controls.select_mode("TEST"), "test mode") + }), + ("CALIBRATE", { + Key.RIGHT: (engine.calibrate.update_crop, "cropping"), + Key.UP: (engine.calibrate.walk_calibrate, "walking"), + Key.LEFT: (engine.calibrate.rotate_calibrate, "rotation"), + Key.DOWN: (engine.calibrate.time_to_reach_bottom_callibrate, "look up down") + }), + ("PLAY/RECORD", { + Key.RIGHT: (Player(engine).toggle_move, "start/stop play"), + Key.UP: (Recorder(engine).toggle_recording, "start/stop record"), + Key.LEFT: (None, "not implemented"), + Key.DOWN: (None, "not implemented") + }), + ("TEST1", { + Key.RIGHT: (engine.test.print_coods, "print coordinates"), + Key.UP: (engine.test.look_for_hole, "look for hole up down"), + Key.LEFT: (None, "not implemented"), + Key.DOWN: (lambda: engine.controls.select_mode("TEST2"), "show next") + }), + ("TEST2", { + Key.RIGHT: (engine.test.set_target, "set target"), + Key.UP: (engine.test.move_to_target, "move to target"), + Key.LEFT: (engine.test.rotate_to_target, "rotate to target"), + Key.DOWN: (lambda: engine.controls.select_mode("TEST1"), "show previous") + }) ] return controls @@ -41,15 +51,20 @@ class Controls: self.current_menu = first - 1 self.controls = controls - def change_state(self): - self.current_menu += 1 - if self.current_menu == len(self.controls): - self.current_menu = 0 + def initialize(self): + self.select_mode("MODE_SELECT") + + def select_mode(self, mode): + self.current_menu = 0 + for i, control in enumerate(self.controls): + if mode == control[0]: + self.current_menu = i help_str = F"CONTROLS: {self.controls[self.current_menu][0]}" - for key, func in self.controls[self.current_menu][1].items(): + for key, meta in self.controls[self.current_menu][1].items(): + func, name = meta hotkey.set_hotkey(key, func) - help_str += f"\n{key.value}: {func.__name__}" + help_str += f"\n{key.value}: {name}" logging.info(help_str) def unassign_keys(self): @@ -57,4 +72,4 @@ class Controls: for c in self.controls: for k in c[1].keys(): if k not in keys: - hotkey.free_key(k) \ No newline at end of file + hotkey.free_key(k) diff --git a/fishy/engine/fullautofisher/engine.py b/fishy/engine/fullautofisher/engine.py index 521ce95..0aefd5d 100644 --- a/fishy/engine/fullautofisher/engine.py +++ b/fishy/engine/fullautofisher/engine.py @@ -1,7 +1,9 @@ import math import os import tempfile +import traceback import uuid +from enum import Enum from zipfile import ZipFile import cv2 @@ -42,8 +44,16 @@ def image_pre_process(img): return img +class State(Enum): + NONE = 0 + PLAYING = 1 + RECORDING = 2 + OTHER = 3 + + class FullAuto(IEngine): rotate_by = 30 + state = State.NONE def __init__(self, gui_ref): from fishy.engine.fullautofisher.controls import Controls @@ -56,9 +66,9 @@ class FullAuto(IEngine): self._curr_rotate_y = 0 self.fisher = SemiFisherEngine(None) - self.controls = Controls(controls.get_controls(self)) self.calibrate = Calibrate(self) self.test = Test(self) + self.controls = Controls(controls.get_controls(self)) @property def show_crop(self): @@ -75,24 +85,31 @@ class FullAuto(IEngine): self.fisher.toggle_start() self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="Full auto debug") - if self.calibrate.crop is None: - self.calibrate.update_crop(enable_crop=False) - self.window.crop = self.calibrate.crop - if not is_tesseract_installed(): - logging.info("tesseract not found") - downlaoad_and_extract_tesseract() + try: + if self.calibrate.crop is None: + self.calibrate.update_crop(enable_crop=False) + self.window.crop = self.calibrate.crop - self.controls.change_state() - while self.start and WindowClient.running(): - self.window.show(self.show_crop, func=image_pre_process) - if not self.show_crop: - time.sleep(0.1) + if not is_tesseract_installed(): + logging.info("tesseract not found") + downlaoad_and_extract_tesseract() + + self.controls.initialize() + while self.start and WindowClient.running(): + self.window.show(self.show_crop, func=image_pre_process) + if not self.show_crop: + time.sleep(0.1) + except: + traceback.print_exc() + + if not self.window.get_capture(): + logging.error("Game window not found") self.gui.bot_started(False) self.controls.unassign_keys() self.window.show(False) - logging.info("Quit") + logging.info("Quitting") self.window.destory() self.fisher.toggle_start() diff --git a/fishy/engine/fullautofisher/player.py b/fishy/engine/fullautofisher/player.py index 6288e0c..63f2edb 100644 --- a/fishy/engine/fullautofisher/player.py +++ b/fishy/engine/fullautofisher/player.py @@ -4,63 +4,59 @@ from pprint import pprint from fishy.engine.semifisher import fishing_event, fishing_mode -from fishy.engine.fullautofisher.engine import FullAuto +from fishy.engine.fullautofisher.engine import FullAuto, State -from fishy.helper import hotkey, helper +from fishy.helper import helper from fishy.helper.config import config -from fishy.helper.hotkey import Key + + +def _get_rec_file(): + file = config.get("full_auto_rec_file") + + if not file: + logging.error("Please select a fishy file first from config") + return None + + file = open(file, 'rb') + data = pickle.load(file) + file.close() + pprint(data) + if "full_auto_path" not in data: + logging.error("invalid file") + return None + return data["full_auto_path"] class Player: def __init__(self, engine: 'FullAuto'): self.recording = False self.engine = engine - self.timeline = [] self.hole_complete_flag = False self.start_moving_flag = False - def _mark_hole(self): - coods = self.engine.get_coods() - self.timeline.append(("check_fish", coods)) + def toggle_move(self): + if FullAuto.state != State.PLAYING and FullAuto.state != State.NONE: + return - def _start_moving(self): self.start_moving_flag = not self.start_moving_flag - - def _stop_recording(self): - self.recording = False + if self.start_moving_flag: + self._start_route() def _hole_complete_callback(self, e): if e == fishing_event.State.IDLE: self.hole_complete_flag = True - def start_route(self): - file = config.get("full_auto_rec_file") - - if not file: - logging.error("Please select a fishy file first from config") + def _start_route(self): + FullAuto.state = State.PLAYING + timeline = _get_rec_file() + if not timeline: return - file = open(file, 'rb') - data = pickle.load(file) - file.close() - pprint(data) - if "full_auto_path" not in data: - logging.error("invalid file") - return - self.timeline = data["full_auto_path"] - - # wait until f8 is pressed - logging.info("press f8 to start") - - self.start_moving_flag = False - hotkey.set_hotkey(Key.F8, self._start_moving) - helper.wait_until(lambda: self.start_moving_flag) - logging.info("starting, press f8 to stop") forward = True i = 0 while self.start_moving_flag: - action = self.timeline[i] + action = timeline[i] if action[0] == "move_to": self.engine.move_to(action[1]) @@ -84,11 +80,12 @@ class Player: fishing_mode.subscribers.remove(self._hole_complete_callback) i += 1 if forward else -1 - if i >= len(self.timeline): + if i >= len(timeline): forward = False - i = len(self.timeline) - 1 + i = len(timeline) - 1 elif i < 0: forward = True i = 0 logging.info("stopped") + FullAuto.state = State.NONE diff --git a/fishy/engine/fullautofisher/recorder.py b/fishy/engine/fullautofisher/recorder.py index 084892b..1ba7310 100644 --- a/fishy/engine/fullautofisher/recorder.py +++ b/fishy/engine/fullautofisher/recorder.py @@ -4,7 +4,7 @@ import time from pprint import pprint from tkinter.filedialog import asksaveasfile -from fishy.engine.fullautofisher.engine import FullAuto +from fishy.engine.fullautofisher.engine import FullAuto, State from fishy.helper import hotkey from fishy.helper.hotkey import Key @@ -12,6 +12,7 @@ from fishy.helper.hotkey import Key class Recorder: recording_fps = 1 + mark_hole_key = Key.F8 def __init__(self, engine: FullAuto): self.recording = False @@ -23,15 +24,19 @@ class Recorder: self.timeline.append(("check_fish", coods)) logging.info("check_fish") - def _stop_recording(self): - self.recording = False + def toggle_recording(self): + if FullAuto.state != State.RECORDING and FullAuto.state != State.NONE: + return - def start_recording(self): - logging.info("f7 for marking hole, f8 to stop recording") - hotkey.set_hotkey(Key.F7, self._mark_hole) - hotkey.set_hotkey(Key.F8, self._stop_recording) + self.recording = not self.recording + if self.recording: + self._start_recording() + + def _start_recording(self): + FullAuto.state = State.RECORDING + logging.info("f8 to stop recording") + hotkey.set_hotkey(Recorder.mark_hole_key, self._mark_hole) - self.recording = True self.timeline = [] while self.recording: @@ -47,6 +52,8 @@ class Recorder: else: logging.warning("Took too much time to record") + hotkey.free_key(Recorder.mark_hole_key) + files = [('Fishy File', '*.fishy')] file = None while not file: @@ -55,4 +62,5 @@ class Recorder: pprint(data) pickle.dump(data, file) file.close() + FullAuto.state = State.NONE diff --git a/fishy/helper/hotkey.py b/fishy/helper/hotkey.py index da24f49..74c57c3 100644 --- a/fishy/helper/hotkey.py +++ b/fishy/helper/hotkey.py @@ -1,8 +1,9 @@ from enum import Enum from threading import Thread -from typing import Dict, Callable +from typing import Dict, Callable, Optional import keyboard +from playsound import playsound from fishy.helper import helper @@ -18,22 +19,28 @@ class Key(Enum): RIGHT = "right" -_hotkeys: Dict[Key, Callable] = {} +_hotkeys: Dict[Key, Optional[Callable]] = {} -def _run_callback(k): - return lambda: Thread(target=_hotkeys[k]).start() +def _get_callback(k): + def callback(): + if not _hotkeys[k]: + return + + playsound(helper.manifest_file("beep.wav"), False) + Thread(target=_hotkeys[k]).start() + return callback def initalize(): for k in Key: - _hotkeys[k] = helper.empty_function - keyboard.add_hotkey(k.value, _run_callback(k)) + _hotkeys[k] = None + keyboard.add_hotkey(k.value, _get_callback(k)) -def set_hotkey(key: Key, func: Callable): +def set_hotkey(key: Key, func: Optional[Callable]): _hotkeys[key] = func def free_key(k: Key): - set_hotkey(k, helper.empty_function) + set_hotkey(k, None)