diff --git a/.gitignore b/.gitignore
index 885f5509..d9f79556 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ __pycache__/
assets/models/
.idea/
.settings/
+*.iml
diff --git a/Auto Maple.iml b/Auto Maple.iml
deleted file mode 100644
index ad3c0a36..00000000
--- a/Auto Maple.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/resources b/resources
index af45bb34..92c1c7e3 160000
--- a/resources
+++ b/resources
@@ -1 +1 @@
-Subproject commit af45bb34da12e672b1a230c0de464461ea42729a
+Subproject commit 92c1c7e31b2c0d7033661dc8f131d7f75b732f2d
diff --git a/src/command_book/command_book.py b/src/command_book/command_book.py
new file mode 100644
index 00000000..af721add
--- /dev/null
+++ b/src/command_book/command_book.py
@@ -0,0 +1,125 @@
+import os
+import inspect
+import importlib
+import traceback
+from os.path import basename, splitext
+from src.common import config, utils
+from src.routine import components
+from src.common.interfaces import Configurable
+
+
+CB_KEYBINDING_DIR = os.path.join('resources', 'keybindings')
+
+
+class CommandBook(Configurable):
+ def __init__(self, file):
+ self.name = splitext(basename(file))[0]
+ self.buff = components.Buff()
+ self.DEFAULT_CONFIG = {}
+ result = self.load_commands(file)
+ if result is None:
+ raise ValueError(f"Invalid command book at '{file}'")
+ self.dict, self.module = result
+ super().__init__(self.name, directory=CB_KEYBINDING_DIR)
+
+ def load_commands(self, file):
+ """Prompts the user to select a command module to import. Updates config's command book."""
+
+ utils.print_separator()
+ print(f"[~] Loading command book '{basename(file)}':")
+
+ ext = splitext(file)[1]
+ if ext != '.py':
+ print(f" ! '{ext}' is not a supported file extension.")
+ return
+
+ new_step = components.step
+ new_cb = {}
+ for c in (components.Wait, components.Walk, components.Fall):
+ new_cb[c.__name__.lower()] = c
+
+ # Import the desired command book file
+ target = '.'.join(['resources', 'command_books', self.name])
+ try:
+ module = importlib.import_module(target)
+ module = importlib.reload(module)
+ except ImportError: # Display errors in the target Command Book
+ print(' ! Errors during compilation:\n')
+ for line in traceback.format_exc().split('\n'):
+ line = line.rstrip()
+ if line:
+ print(' ' * 4 + line)
+ print(f"\n ! Command book '{self.name}' was not loaded")
+ return
+
+ # Load key map
+ if hasattr(module, 'Key'):
+ default_config = {}
+ for key, value in module.Key.__dict__.items():
+ if not key.startswith('__') and not key.endswith('__'):
+ default_config[key] = value
+ self.DEFAULT_CONFIG = default_config
+ else:
+ print(f" ! Error loading command book '{self.name}', keymap class 'Key' is missing")
+ return
+
+ # Check if the 'step' function has been implemented
+ step_found = False
+ for name, func in inspect.getmembers(module, inspect.isfunction):
+ if name.lower() == 'step':
+ step_found = True
+ new_step = func
+
+ # Populate the new command book
+ for name, command in inspect.getmembers(module, inspect.isclass):
+ if issubclass(command, components.Command):
+ new_cb[name.lower()] = command
+
+ # Check if required commands have been implemented and overridden
+ required_found = True
+ for command in (components.Buff,):
+ name = command.__name__.lower()
+ if name not in new_cb:
+ required_found = False
+ new_cb[name] = command
+ print(f" ! Error: Must implement required command '{name}'.")
+
+ # Look for overridden movement commands
+ movement_found = True
+ for command in (components.Move, components.Adjust):
+ name = command.__name__.lower()
+ if name not in new_cb:
+ movement_found = False
+ new_cb[name] = command
+
+ if not step_found and not movement_found:
+ print(f" ! Error: Must either implement both 'Move' and 'Adjust' commands, "
+ f"or the function 'step'")
+ if required_found and (step_found or movement_found):
+ self.buff = new_cb['buff']()
+ components.step = new_step
+ config.gui.menu.file.enable_routine_state()
+ config.gui.view.status.set_cb(basename(file))
+ config.routine.clear()
+ print(f" ~ Successfully loaded command book '{self.name}'")
+ return new_cb, module
+ else:
+ print(f" ! Command book '{self.name}' was not loaded")
+
+ def __getitem__(self, item):
+ return self.dict[item]
+
+ def __contains__(self, item):
+ return item in self.dict
+
+ def load_config(self):
+ super().load_config()
+ self._set_keybinds()
+
+ def save_config(self):
+ self._set_keybinds()
+ super().save_config()
+
+ def _set_keybinds(self):
+ for k, v in self.config.items():
+ setattr(self.module.Key, k, v)
diff --git a/src/command_book/resources/keybindings/kanna b/src/command_book/resources/keybindings/kanna
new file mode 100644
index 00000000..c5414f5f
Binary files /dev/null and b/src/command_book/resources/keybindings/kanna differ
diff --git a/src/common/interfaces.py b/src/common/interfaces.py
index ba833aa9..047d6c7d 100644
--- a/src/common/interfaces.py
+++ b/src/common/interfaces.py
@@ -1,30 +1,32 @@
import os
import pickle
-SETTINGS_DIR = '.settings'
-
class Configurable:
TARGET = 'default_configurable'
- DEFAULT_CONFIG = {
+ DIRECTORY = '.settings'
+ DEFAULT_CONFIG = { # Must be overridden by subclass
'Default configuration': 'None'
}
- def __init__(self, target):
+ def __init__(self, target, directory='.settings'):
+ assert self.DEFAULT_CONFIG != Configurable.DEFAULT_CONFIG, 'Must override Configurable.DEFAULT_CONFIG'
self.TARGET = target
- self.config = self.DEFAULT_CONFIG.copy()
+ self.DIRECTORY = directory
+ self.config = self.DEFAULT_CONFIG.copy() # Shallow copy, should only contain primitives
self.load_config()
def load_config(self):
- path = os.path.join(SETTINGS_DIR, self.TARGET)
+ path = os.path.join(self.DIRECTORY, self.TARGET)
if os.path.isfile(path):
with open(path, 'rb') as file:
- self.config = pickle.load(file)
+ loaded = pickle.load(file)
+ self.config = {key: loaded.get(key, '') for key in self.DEFAULT_CONFIG}
else:
self.save_config()
def save_config(self):
- path = os.path.join(SETTINGS_DIR, self.TARGET)
+ path = os.path.join(self.DIRECTORY, self.TARGET)
directory = os.path.dirname(path)
if not os.path.exists(directory):
os.makedirs(directory)
diff --git a/src/common/utils.py b/src/common/utils.py
index 71d9957a..8d01dd10 100644
--- a/src/common/utils.py
+++ b/src/common/utils.py
@@ -97,6 +97,8 @@ def multi_match(frame, template, threshold=0.95):
:return: An array of matches that exceed THRESHOLD.
"""
+ if template.shape[0] > frame.shape[0] or template.shape[1] > frame.shape[1]:
+ return []
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
result = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED)
locations = np.where(result >= threshold)
diff --git a/src/gui/interfaces.py b/src/gui/interfaces.py
index dbbfc152..d52314f2 100644
--- a/src/gui/interfaces.py
+++ b/src/gui/interfaces.py
@@ -1,7 +1,10 @@
"""Interfaces that are used by various GUI pages."""
import tkinter as tk
+import keyboard as kb
from tkinter import ttk
+from src.common import utils
+from src.common.interfaces import Configurable
class Frame(tk.Frame):
@@ -28,3 +31,162 @@ class MenuBarItem(tk.Menu):
def __init__(self, parent, label, **kwargs):
super().__init__(parent, **kwargs)
parent.add_cascade(label=label, menu=self)
+
+
+class KeyBindings(LabelFrame):
+ def __init__(self, parent, label, target, **kwargs):
+ super().__init__(parent, label, **kwargs)
+ if target is not None:
+ assert isinstance(target, Configurable)
+ self.target = target
+ self.long = False
+
+ self.displays = {} # Holds each action's display variable
+ self.forward = {} # Maps actions to keys
+ self.backward = {} # Maps keys to actions
+ self.prev_a = ''
+ self.prev_k = ''
+
+ self.contents = None
+ self.container = None
+ self.canvas = None
+ self.scrollbar = None
+ self.reset = None
+ self.save = None
+ self.create_edit_ui()
+
+ def create_edit_ui(self):
+ self.displays = {}
+ self.forward = {}
+ self.backward = {}
+ self.prev_a = ''
+ self.prev_k = ''
+
+ if self.target is None:
+ self.contents = Frame(self)
+ self.create_disabled_entry()
+ self.contents.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
+ return
+
+ if len(self.target.config) > 27:
+ self.long = True
+ self.container = Frame(self, width=354, height=650)
+ self.container.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=(5, 0))
+ self.container.pack_propagate(False)
+ self.canvas = tk.Canvas(self.container, bd=0, highlightthickness=0)
+ self.scrollbar = tk.Scrollbar(self.container, command=self.canvas.yview)
+ self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
+ self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+ self.contents = Frame(self.canvas)
+ self.contents.bind(
+ '',
+ lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
+ )
+ self.canvas.create_window((0, 0), window=self.contents, anchor=tk.NW)
+ self.canvas.configure(yscrollcommand=self.scrollbar.set)
+ else:
+ self.contents = Frame(self)
+ self.contents.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
+
+ for action, key in self.target.config.items():
+ self.forward[action] = key
+ self.backward[key] = action
+ self.create_entry(action, key)
+ self.focus()
+
+ self.reset = tk.Button(self, text='Reset', command=self.refresh_edit_ui, takefocus=False)
+ self.reset.pack(side=tk.LEFT, padx=5, pady=5)
+ self.save = tk.Button(self, text='Save', command=self.save_keybindings, takefocus=False)
+ self.save.pack(side=tk.RIGHT, padx=5, pady=5)
+
+ def refresh_edit_ui(self):
+ self.destroy_contents()
+ self.create_edit_ui()
+
+ def destroy_contents(self):
+ self.contents.destroy()
+ self.reset.destroy()
+ self.save.destroy()
+ if self.long:
+ self.container.destroy()
+ self.canvas.destroy()
+ self.scrollbar.destroy()
+
+ @utils.run_if_disabled('\n[!] Cannot save key bindings while Auto Maple is enabled')
+ def save_keybindings(self):
+ utils.print_separator()
+ print(f"[~] Saving key bindings to '{self.target.TARGET}':")
+
+ failures = 0
+ for action, key in self.forward.items():
+ if key != '':
+ self.target.config[action] = key
+ else:
+ print(f" ! Action '{action}' was not bound to a key")
+ failures += 1
+
+ self.target.save_config()
+ if failures == 0:
+ print(' ~ Successfully saved all key bindings')
+ else:
+ print(f' ~ Successfully saved all except for {failures} key bindings')
+ self.refresh_edit_ui()
+
+ def create_entry(self, action, key):
+ """
+ Creates an input row for a single key bind. ACTION is assigned to KEY.
+ """
+
+ display_var = tk.StringVar(value=key)
+ self.displays[action] = display_var
+
+ row = Frame(self.contents, highlightthickness=0)
+ row.pack(expand=True, fill='x')
+
+ label = tk.Entry(row)
+ label.grid(row=0, column=0, sticky=tk.EW)
+ label.insert(0, action)
+ label.config(state=tk.DISABLED)
+
+ def on_key_press(_):
+ k = kb.read_key()
+ if action != self.prev_a:
+ self.prev_k = ''
+ self.prev_a = action
+ if k != self.prev_k:
+ prev_key = self.forward[action]
+ self.backward.pop(prev_key, None)
+ if k in self.backward:
+ prev_action = self.backward[k]
+ self.forward[prev_action] = ''
+ self.displays[prev_action].set('')
+ display_var.set(k)
+ self.forward[action] = k
+ self.backward[k] = action
+ self.prev_k = k
+
+ def validate(d):
+ """Blocks user insertion, but allows StringVar set()."""
+
+ if d == '-1':
+ return True
+ return False
+
+ reg = (self.register(validate), '%d')
+ entry = tk.Entry(row, textvariable=display_var,
+ validate='key', validatecommand=reg,
+ takefocus=False)
+ entry.bind('', on_key_press)
+ entry.grid(row=0, column=1, sticky=tk.EW)
+
+ def create_disabled_entry(self):
+ row = Frame(self.contents, highlightthickness=0)
+ row.pack(expand=True, fill='x')
+
+ label = tk.Entry(row)
+ label.grid(row=0, column=0, sticky=tk.EW)
+ label.config(state=tk.DISABLED)
+
+ entry = tk.Entry(row)
+ entry.grid(row=0, column=1, sticky=tk.EW)
+ entry.config(state=tk.DISABLED)
diff --git a/src/gui/menu/file.py b/src/gui/menu/file.py
index d82db545..948181a4 100644
--- a/src/gui/menu/file.py
+++ b/src/gui/menu/file.py
@@ -87,7 +87,7 @@ def _load_commands():
def get_routines_dir():
- target = os.path.join(config.RESOURCES_DIR, 'routines', config.bot.module_name)
+ target = os.path.join(config.RESOURCES_DIR, 'routines', config.bot.command_book.name)
if not os.path.exists(target):
os.makedirs(target)
return target
diff --git a/src/gui/settings/keybindings.py b/src/gui/settings/keybindings.py
deleted file mode 100644
index 1cb27520..00000000
--- a/src/gui/settings/keybindings.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import tkinter as tk
-import keyboard as kb
-from src.gui.interfaces import LabelFrame, Frame
-from src.common import utils
-from src.common.interfaces import Configurable
-
-
-class KeyBindings(LabelFrame):
- def __init__(self, parent, label, target, **kwargs):
- super().__init__(parent, label, **kwargs)
- assert isinstance(target, Configurable)
- self.target = target
-
- self.columnconfigure(0, minsize=300)
-
- self.displays = {} # Holds each action's display variable
- self.forward = {} # Maps actions to keys
- self.backward = {} # Maps keys to actions
- self.prev_a = ''
- self.prev_k = ''
-
- self.contents = None
- self.create_edit_ui()
-
- def create_edit_ui(self):
- self.displays = {}
- self.forward = {}
- self.backward = {}
- self.prev_a = ''
- self.prev_k = ''
-
- self.contents = Frame(self)
- self.contents.grid(row=0, column=0, sticky=tk.NSEW, padx=5, pady=5)
-
- if self.target is not None: # For when running GUI only
- for action, key in self.target.config.items():
- self.forward[action] = key
- self.backward[key] = action
- self.create_entry(action, key)
- self.focus()
- else:
- self.create_disabled_entry()
-
- reset = tk.Button(self.contents, text='Reset', command=self.refresh_edit_ui, takefocus=False)
- reset.pack(side=tk.LEFT, pady=5)
-
- save = tk.Button(self.contents, text='Save', command=self.save, takefocus=False)
- save.pack(side=tk.RIGHT, pady=5)
-
- def refresh_edit_ui(self):
- self.contents.destroy()
- self.create_edit_ui()
-
- @utils.run_if_disabled('\n[!] Cannot save key bindings while Auto Maple is enabled')
- def save(self):
- utils.print_separator()
- print(f"[~] Saving key bindings to '{self.target.TARGET}':")
-
- failures = 0
- for action, key in self.forward.items():
- if key != '':
- self.target.config[action] = key
- else:
- print(f" ! Action '{action}' was not bound to a key")
- failures += 1
-
- self.target.save_config()
- if failures == 0:
- print(' ~ Successfully saved all key bindings')
- else:
- print(f' ~ Successfully saved all except for {failures} key bindings')
- self.create_edit_ui()
-
- def create_entry(self, action, key):
- """
- Creates an input row for a single key bind. ACTION is assigned to KEY.
- """
-
- display_var = tk.StringVar(value=key)
- self.displays[action] = display_var
-
- row = Frame(self.contents, highlightthickness=0)
- row.pack(expand=True, fill='x')
-
- label = tk.Entry(row)
- label.grid(row=0, column=0, sticky=tk.EW)
- label.insert(0, action)
- label.config(state=tk.DISABLED)
-
- def on_key_press(_):
- k = kb.read_key()
- if action != self.prev_a:
- self.prev_k = ''
- self.prev_a = action
- if k != self.prev_k:
- prev_key = self.forward[action]
- self.backward.pop(prev_key, None)
- if k in self.backward:
- prev_action = self.backward[k]
- self.forward[prev_action] = ''
- self.displays[prev_action].set('')
- display_var.set(k)
- self.forward[action] = k
- self.backward[k] = action
- self.prev_k = k
-
- def validate(d):
- """Blocks user insertion, but allows StringVar set()."""
-
- if d == '-1':
- return True
- return False
-
- reg = (self.register(validate), '%d')
- entry = tk.Entry(row, textvariable=display_var,
- validate='key', validatecommand=reg,
- takefocus=False)
- entry.bind('', on_key_press)
- entry.grid(row=0, column=1, sticky=tk.EW)
-
- def create_disabled_entry(self):
- row = Frame(self.contents, highlightthickness=0)
- row.pack(expand=True, fill='x')
-
- label = tk.Entry(row)
- label.grid(row=0, column=0, sticky=tk.EW)
- label.config(state=tk.DISABLED)
-
- entry = tk.Entry(row)
- entry.grid(row=0, column=1, sticky=tk.EW)
- entry.config(state=tk.DISABLED)
diff --git a/src/gui/settings/main.py b/src/gui/settings/main.py
index 2bbd9275..f84aaa3e 100644
--- a/src/gui/settings/main.py
+++ b/src/gui/settings/main.py
@@ -1,7 +1,7 @@
"""Displays Auto Maple's current settings and allows the user to edit them."""
import tkinter as tk
-from src.gui.settings.keybindings import KeyBindings
+from src.gui.interfaces import KeyBindings
from src.gui.settings.pets import Pets
from src.gui.interfaces import Tab, Frame
from src.common import config
@@ -14,14 +14,22 @@ def __init__(self, parent, **kwargs):
self.columnconfigure(0, weight=1)
self.columnconfigure(3, weight=1)
- column1 = Frame(self)
- column1.grid(row=0, column=1, sticky=tk.N, padx=10, pady=10)
- self.controls = KeyBindings(column1, 'Auto Maple Controls', config.listener)
+ self.column1 = Frame(self)
+ self.column1.grid(row=0, column=1, sticky=tk.N, padx=10, pady=10)
+ self.controls = KeyBindings(self.column1, 'Auto Maple Controls', config.listener)
self.controls.pack(side=tk.TOP, fill='x', expand=True)
-
- column2 = Frame(self)
- column2.grid(row=0, column=2, sticky=tk.N, padx=10, pady=10)
- self.key_bindings = KeyBindings(column2, 'In-game Keybindings', config.bot)
- self.key_bindings.pack(side=tk.TOP, fill='x', expand=True)
- self.pets = Pets(column2)
+ self.common_bindings = KeyBindings(self.column1, 'In-game Keybindings', config.bot)
+ self.common_bindings.pack(side=tk.TOP, fill='x', expand=True, pady=(10, 0))
+ self.pets = Pets(self.column1)
self.pets.pack(side=tk.TOP, fill='x', expand=True, pady=(10, 0))
+
+ self.column2 = Frame(self)
+ self.column2.grid(row=0, column=2, sticky=tk.N, padx=10, pady=10)
+ self.class_bindings = KeyBindings(self.column2, f'No Command Book Selected', None)
+ self.class_bindings.pack(side=tk.TOP, fill='x', expand=True)
+
+ def update_class_bindings(self):
+ self.class_bindings.destroy()
+ class_name = config.bot.command_book.name.capitalize()
+ self.class_bindings = KeyBindings(self.column2, f'{class_name} Keybindings', config.bot.command_book)
+ self.class_bindings.pack(side=tk.TOP, fill='x', expand=True)
diff --git a/src/modules/bot.py b/src/modules/bot.py
index dee47e7c..9e3e4bab 100644
--- a/src/modules/bot.py
+++ b/src/modules/bot.py
@@ -12,6 +12,7 @@
from src.detection import detection
from src.routine import components
from src.routine.routine import Routine
+from src.command_book.command_book import CommandBook
from src.routine.components import Point
from src.common.vkeys import press, click
from src.common.interfaces import Configurable
@@ -39,13 +40,14 @@ def __init__(self):
self.rune_pos = (0, 0)
self.rune_closest_pos = (0, 0) # Location of the Point closest to rune
self.submodules = []
- self.module_name = None
- self.buff = components.Buff()
+ self.command_book = None # CommandBook instance
+ # self.module_name = None
+ # self.buff = components.Buff()
- self.command_book = {}
- for c in (components.Wait, components.Walk, components.Fall,
- components.Move, components.Adjust, components.Buff):
- self.command_book[c.__name__.lower()] = c
+ # self.command_book = {}
+ # for c in (components.Wait, components.Walk, components.Fall,
+ # components.Move, components.Adjust, components.Buff):
+ # self.command_book[c.__name__.lower()] = c
config.routine = Routine()
@@ -79,7 +81,7 @@ def _main(self):
while True:
if config.enabled and len(config.routine) > 0:
# Buff and feed pets
- self.buff.main()
+ self.command_book.buff.main()
pet_settings = config.gui.settings.pets
auto_feed = pet_settings.auto_feed.get()
num_pets = pet_settings.num_pets.get()
@@ -149,78 +151,82 @@ def _solve_rune(self, model):
inferences.append(solution)
def load_commands(self, file):
- """Prompts the user to select a command module to import. Updates config's command book."""
-
- utils.print_separator()
- print(f"[~] Loading command book '{basename(file)}':")
-
- ext = splitext(file)[1]
- if ext != '.py':
- print(f" ! '{ext}' is not a supported file extension.")
- return False
-
- new_step = components.step
- new_cb = {}
- for c in (components.Wait, components.Walk, components.Fall):
- new_cb[c.__name__.lower()] = c
-
- # Import the desired command book file
- module_name = splitext(basename(file))[0]
- target = '.'.join(['resources', 'command_books', module_name])
try:
- module = importlib.import_module(target)
- module = importlib.reload(module)
- except ImportError: # Display errors in the target Command Book
- print(' ! Errors during compilation:\n')
- for line in traceback.format_exc().split('\n'):
- line = line.rstrip()
- if line:
- print(' ' * 4 + line)
- print(f"\n ! Command book '{module_name}' was not loaded")
- return
-
- # Check if the 'step' function has been implemented
- step_found = False
- for name, func in inspect.getmembers(module, inspect.isfunction):
- if name.lower() == 'step':
- step_found = True
- new_step = func
-
- # Populate the new command book
- for name, command in inspect.getmembers(module, inspect.isclass):
- new_cb[name.lower()] = command
-
- # Check if required commands have been implemented and overridden
- required_found = True
- for command in [components.Buff]:
- name = command.__name__.lower()
- if name not in new_cb:
- required_found = False
- new_cb[name] = command
- print(f" ! Error: Must implement required command '{name}'.")
-
- # Look for overridden movement commands
- movement_found = True
- for command in (components.Move, components.Adjust):
- name = command.__name__.lower()
- if name not in new_cb:
- movement_found = False
- new_cb[name] = command
-
- if not step_found and not movement_found:
- print(f" ! Error: Must either implement both 'Move' and 'Adjust' commands, "
- f"or the function 'step'")
- if required_found and (step_found or movement_found):
- self.module_name = module_name
- self.command_book = new_cb
- self.buff = new_cb['buff']()
- components.step = new_step
- config.gui.menu.file.enable_routine_state()
- config.gui.view.status.set_cb(basename(file))
- config.routine.clear()
- print(f" ~ Successfully loaded command book '{module_name}'")
- else:
- print(f" ! Command book '{module_name}' was not loaded")
+ self.command_book = CommandBook(file)
+ config.gui.settings.update_class_bindings()
+ except ValueError:
+ pass # TODO: UI warning popup, say check cmd for errors
+ #
+ # utils.print_separator()
+ # print(f"[~] Loading command book '{basename(file)}':")
+ #
+ # ext = splitext(file)[1]
+ # if ext != '.py':
+ # print(f" ! '{ext}' is not a supported file extension.")
+ # return False
+ #
+ # new_step = components.step
+ # new_cb = {}
+ # for c in (components.Wait, components.Walk, components.Fall):
+ # new_cb[c.__name__.lower()] = c
+ #
+ # # Import the desired command book file
+ # module_name = splitext(basename(file))[0]
+ # target = '.'.join(['resources', 'command_books', module_name])
+ # try:
+ # module = importlib.import_module(target)
+ # module = importlib.reload(module)
+ # except ImportError: # Display errors in the target Command Book
+ # print(' ! Errors during compilation:\n')
+ # for line in traceback.format_exc().split('\n'):
+ # line = line.rstrip()
+ # if line:
+ # print(' ' * 4 + line)
+ # print(f"\n ! Command book '{module_name}' was not loaded")
+ # return
+ #
+ # # Check if the 'step' function has been implemented
+ # step_found = False
+ # for name, func in inspect.getmembers(module, inspect.isfunction):
+ # if name.lower() == 'step':
+ # step_found = True
+ # new_step = func
+ #
+ # # Populate the new command book
+ # for name, command in inspect.getmembers(module, inspect.isclass):
+ # new_cb[name.lower()] = command
+ #
+ # # Check if required commands have been implemented and overridden
+ # required_found = True
+ # for command in [components.Buff]:
+ # name = command.__name__.lower()
+ # if name not in new_cb:
+ # required_found = False
+ # new_cb[name] = command
+ # print(f" ! Error: Must implement required command '{name}'.")
+ #
+ # # Look for overridden movement commands
+ # movement_found = True
+ # for command in (components.Move, components.Adjust):
+ # name = command.__name__.lower()
+ # if name not in new_cb:
+ # movement_found = False
+ # new_cb[name] = command
+ #
+ # if not step_found and not movement_found:
+ # print(f" ! Error: Must either implement both 'Move' and 'Adjust' commands, "
+ # f"or the function 'step'")
+ # if required_found and (step_found or movement_found):
+ # self.module_name = module_name
+ # self.command_book = new_cb
+ # self.buff = new_cb['buff']()
+ # components.step = new_step
+ # config.gui.menu.file.enable_routine_state()
+ # config.gui.view.status.set_cb(basename(file))
+ # config.routine.clear()
+ # print(f" ~ Successfully loaded command book '{module_name}'")
+ # else:
+ # print(f" ! Command book '{module_name}' was not loaded")
def update_submodules(self, force=False):
"""
diff --git a/src/modules/capture.py b/src/modules/capture.py
index 0d8bf0cf..284e3c49 100644
--- a/src/modules/capture.py
+++ b/src/modules/capture.py
@@ -87,7 +87,7 @@ def _main(self):
self.window['width'] = max(rect[2] - rect[0], MMT_WIDTH)
self.window['height'] = max(rect[3] - rect[1], MMT_HEIGHT)
- # Calibrate by finding the bottom right corner of the minimap
+ # Calibrate by finding the top-left and bottom-right corners of the minimap
with mss.mss() as self.sct:
self.frame = self.screenshot()
if self.frame is None:
diff --git a/src/routine/components.py b/src/routine/components.py
index 4b4d3456..4bdbb96f 100644
--- a/src/routine/components.py
+++ b/src/routine/components.py
@@ -81,7 +81,7 @@ def main(self):
move = config.bot.command_book['move']
move(*self.location).execute()
if self.adjust:
- adjust = config.bot.command_book.get('adjust') # TODO: adjust using step('up')?
+ adjust = config.bot.command_book['adjust'] # TODO: adjust using step('up')?
adjust(*self.location).execute()
for command in self.commands:
command.execute()
diff --git a/src/routine/layout.py b/src/routine/layout.py
index 8a62c47c..30a794b0 100644
--- a/src/routine/layout.py
+++ b/src/routine/layout.py
@@ -292,4 +292,4 @@ def save(self):
def get_layouts_dir():
- return os.path.join(config.RESOURCES_DIR, 'layouts', config.bot.module_name)
+ return os.path.join(config.RESOURCES_DIR, 'layouts', config.bot.command_book.name)
diff --git a/src/routine/routine.py b/src/routine/routine.py
index fc2232a9..84e598cf 100644
--- a/src/routine/routine.py
+++ b/src/routine/routine.py
@@ -274,7 +274,7 @@ def _eval(self, row, i):
def get_all_components():
"""Returns a dictionary mapping all creatable Components to their names."""
- options = config.bot.command_book.copy()
+ options = config.bot.command_book.dict.copy()
for e in (Point, Label, Jump, Setting):
options[e.__name__.lower()] = e
return options