-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py37
148 lines (112 loc) · 7.02 KB
/
main.py37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import cv2
from frameStuffs import Frame
from pynput.keyboard import Listener
import numpy as np
from win32api import mouse_event
from win32con import MOUSEEVENTF_MOVE, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_ABSOLUTE
import time
from math import atan
# a little countdown so you can run the code and open the game at the same time
countdownStart = 3
for i in range(countdownStart, 0, -1):
print("countdown:", i)
time.sleep(1)
print('code starting')
# allows the user to kill the code by clicking `q`
running = True
def on_press(key):
global running
try: running = (key.char != 'q')
except: pass
keyboard_listener = Listener(on_press=on_press)
keyboard_listener.start()
# initalize the frame class to know what rectangle of the screen to screenshot
# frame = Frame(248, 1940, 1215, 704)
# frame = Frame() # NOTE: this line of code will allow you to click where you want the screenshot to be at the beginning of the code
frame = Frame(310, 1767, 1193, 811)
# gets the location of the center of the frame
centerX, centerY = ((frame.right - frame.left) // 2), ((frame.bottom - frame.top) // 2)
while running:
# take a screenshot and copy it for drawing
const_frame = frame.grab_frame()
temp_frame = const_frame.copy()
# these are the colours of the item we're trying to click (the `image color range finder.py` code finds these from a .png of just the item (just finds the minimal and maximal color in an image in RGB values))
# BOX GAME
# lowerColor = np.array([23, 137, 255])
# upperColor = np.array([30, 212, 255])
# FLICKING GAME
# lowerColor = np.array([0, 203, 140])
# upperColor = np.array([0, 205, 245])
# BURST GAME
# lowerColor = np.array([11, 163, 255])
# upperColor = np.array([16, 183, 255])
# COMPETITIVE BURST GAME
# lowerColor = np.array([21, 202, 241])
# upperColor = np.array([24, 243, 255])
# GRAVITY DROP GAME
# lowerColor = np.array([22, 205, 238])
# upperColor = np.array([23, 241, 255])
# TILE FRENZY GAME (OLD)
# lowerColor = np.array([22, 140, 254])
# upperColor = np.array([30, 214, 255])
# TILE FRENZY (NEW)
lowerColor = np.array([6, 187, 96])
upperColor = np.array([18, 255, 255])
# we convert to an hsv
hsv = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2HSV)
# find the pixels in our photo that are in our color range
mask = cv2.inRange(hsv, lowerColor, upperColor)
# find all the 'contours' of only these selected pixels
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# sort the contours by size (largest to smallest)
contours = sorted(contours, key=cv2.contourArea, reverse=True)
# for debugging this will draw the contours on our temp_frame
# cv2.drawContours(temp_frame, contours, -1, (255, 0, 0), 3)
# we make a list of all the bounding rectangles for each of our contours (sorted by size)
rect_list = [cv2.boundingRect(contour) for contour in contours]
# and now we iterate through these rectangles (largest to smallest) for as many points we're expecting to fire at
# NOTE: sometimes we will detect more rectangles inside our other rectangles (causing repeat shots at one point). this is why we limit the `numberOfThingsToFireAt`
# this value should be changed depending on how many we're expecting to see, this is when there we're 30 balls on the screen at any point
numberOfThingsToFireAt = 3
for rect in rect_list[:numberOfThingsToFireAt]:
# rect is the rectangle of the item we're trying to shoot at
# we first calculate the center of the rect we're trying to shoot at
centerRectX, centerRectY = rect[0] + rect[2] // 2, rect[1] + rect[3] // 2
# we then need the distance from where we're aiming (the center of the screen) to what we're trying to shoot at
diffX, diffY = int(-(centerX - centerRectX)), int(-(centerY - centerRectY))
r = 500 # the distance from our player to the wall the dots spawn at
# we then calculate the arclength based on the distance we must travel. The angle is `atan(diff / r)`, the arclength is thus `r * atan(diff / r)`
# this calculation is not perfectly accurate and depending on the game it does change, so these two scalers fine tune the amount we move
# usually our calculation moves us too far, so if we're overshooting increase the number, if we're undershooting decrease the number (1.5 is a good value for most games)
# NOTE: these values work for me but I believe they are system dependant so you may need to tweak these quite a bit
sx = 1.5
# sy = 1.45
# sx = 1.5
sy = 1.447
# note: for small `diff` we have `r * atan(diff / r) // s` to be approximately equal to `diff // s`.
# thus if all targets are close together the following approximation may be used for increased speed
# diffX = int(diffX// sx)
# diffY = int(diffY // sy)
# otherwise we must use the more accurate calculation
diffX = int(r * atan(diffX / r) // sx)
diffY = int(r * atan(diffY / r) // sy)
# we now click on the target. We move to the target at (diffX, diffY) [relative from our current mouse location], then put the left mouse button down and up
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, diffX, diffY, 0, 0) # the final two zeros are flags https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
# after we've moved and clicked on the item we cannot move back right away or the click will not register at (diffX, diffY) so we wait for a period of time
# the amount of time can be tweaked to increase speed
time.sleep(0.1) # works for box frenzy
# now that we've waited we move back to the center of the screen
# we have to do this as our calculations only work if our starting point is the center of the screen (where our r = 500 sphere touches the target wall)
mouse_event(MOUSEEVENTF_MOVE, -diffX, -diffY, 0, 0)
# for debugging sometimes we want to display where we're aiming, this draws the rectangle around and a circle at the point we're trying to aim
# cv2.circle(temp_frame, (centerRectX, centerRectY), 5, (0, 255, 0), 3)
# temp_frame = cv2.rectangle(temp_frame, rect, (0, 255, 0), 3)
# by clicking `q` we can kill the code at any point, this just makes sure we don't get stuck in our loop
if not running: break
# after firing at all of the points we return to the center of the screen and take another screenshot. If we do this too quickly the screenshot will not be accurate and we will detect "phantom points"
# so we wait some amount of time (this amount can be tweaked for increased speed)
time.sleep(0.1) # works for box frenzy
# for debugging this will display the image we processed and what we were aiming at (so long as you uncomment that code above)
# cv2.imwrite(f"./debugging/where to shoot {time.time()}.jpg", temp_frame)
# cv2.waitKey(1500)
# running = False