Why
For a short period of time I played World of Warcraft, more specifically the Wrath of the Lich King expansion. I got bored after a while and initially resorted to playing Wolf of Action House. I found myself in Dalaran and looking at a fisher and thought — this has to be easy to automate.
Thoughts prior to implementation
My initial thought was using either Haar Cascades or a NN-implementation of image recognition, to find and see live changes in position of the bait. My second thought was using the level of sound as the sound of catching a fish was loud, to then press like a maniac in some pattern. I did not try this approach as other sounds were also generated in popular areas. Thirdly I tried fishing, to see if I had missed something which was exploitable – and I did.
When getting a fish, the animation was not just an animation, but the actual hit-box of the bait also moved, so why not identify the bait, predict where the bait will move and spam-click there? In other words, press within the green box but – but not the red.
Building the bot
So, to be able to press above the bait, all I have to do is locate it. Being afraid of the manual work of labeling I started Googling like a fanatic for something which could help me and found PyAutoGUI. The library allows me to not only take a screenshot, but also to locate it.
To make my life easier, I wanted to be able to control the bot, when to start and stop fishing basically.
To begin with Ineeded a listener.
from pynput.keyboard import Key, Listener, Controller
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
keyboard = Controller()
With my listener I defined actions for pressing and releasing a button. The reason why I start a new thread is so that the main thread is still open for input — I can still give my program input while fishing.
import _thread
def on_press(key):
if key == Key.shift:
_thread.start_new_thread(start_fishing(), ())
def on_release(key):
if key == Key.esc:
_thread.exit()
return False
So, how do we fish?!
- By hovering over the bait when entering our fishingstate a local screenshot is taken on the bait.
- PyAutoGUI is used to locate the bait.
- If that fails, the system skips the next step.
- The system begins to spam right-click (collect) above the located bait for 10 seconds.
- The system puts out a new bait (bound to “1”) and waits for it to settle in the water
- The system goes back to the state of locating the bait.
import pyautogui
import time
def start_fishing():
x, y = pyautogui.position()
pyautogui.screenshot('bait_screenshot.png', region=(x-14, y-14, 28, 28))
while True:
found = False
res = None
count = 0
try:
res = pyautogui.locateOnScreen('bait_screenshot.png', confidence=0.95)
found = True
except:
pyautogui.press('1')
time.sleep(3)
while found:
pyautogui.rightClick(res.left, res.top + 60)
time.sleep(0.1)
count += 1
if count > 100:
found = False
count = 0
pyautogui.press('1')
time.sleep(3)
Results and Thoughts
The mini project was a success. I could leave it for hours without breaking. It is somewhat consistent and performs well in all environments I tried it in, as a new picture is taken in that environment before every fishing session. An improvement would be to generalize it for different resolutions as it’s currently pixel-based. As I only have one monitor, one could simply not ask me to do so. What did I learn from this project? Spending a lot of time and effort for the best possible solution is sometimes a worthwhile approach. For a quick hack like this though, I have to say that I usually go by some wise words from Gino D’Acampo — minimum effort, maximum satisfaction.