The Lazy Fisher

Can it really be this easy?

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.

Hitbox visualized

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.