Documentation
Script Template
Below is the basic template of a python script that can be loaded by Copycat.
from ScriptCore import Template, PrintColors
class Script(Template):
def __init__(self, controller, report):
super().__init__(controller, report)
def run(self, frame):
self.print_log("Hello World!", PrintColors.COLOR_RED)
return frame
You must call your class Script, and it must extend the Template class from ScriptCore. Take notice of the import at the top of the script.
The init function is where you can initialize any variables you may need. The run function is where you will do your processing on the frame.
Main Loop
Copycat continuously runs the run method, passing in the frame from the capture card / desktop.
def run(self, frame):
self.print_log("Hello World!", PrintColors.COLOR_RED)
return frame
The frame from the capture method enters the run function as a parameter, and must always be returned back.
The frame is a NumPy array, and can be manipulated using OpenCV. You can find the documentation for OpenCV here.
Printing
You can print messages to the output log in Copycat by using the self.print function. Alternatively you can print to the terminal using the standard print function.
# Make sure to import the PrintColors enum
from ScriptCore import PrintColors
# Prints to output log, with new line.
self.print_log("I'm black!", PrintColors.COLOR_BLACK)
self.print_log("I'm red!", PrintColors.COLOR_RED)
self.print_log("I'm green!", PrintColors.COLOR_GREEN)
self.print_log("I'm blue!", PrintColors.COLOR_BLUE)
# Prints to output log as bold text, with new line.
self.print_log("I'm black and bold!", PrintColors.COLOR_BOLD_BLACK)
self.print_log("I'm red and bold!", PrintColors.COLOR_BOLD_RED)
self.print_log("I'm green and bold!", PrintColors.COLOR_BOLD_GREEN)
self.print_log("I'm blue and bold!", PrintColors.COLOR_BOLD_BLUE)
# Prints to output log without a new line.
self.print_log("[Error] ", PrintColors.COLOR_BOLD_RED, false)
self.print_log("Some content!", PrintColors.COLOR_BLACK)
# Prints to terminal instead
print("The terminal is boring!")
Macros
You can easily create macros, to perform a series of time based actions. Macros must be declared in the init method, to be later run inside the run method.
To use macros you must first import the module:
from Macro import Macro
Here is an example of a simple bunny hop macro:
self.bunny_hop = Macro(controller, [
[self.release_button, Buttons.BTN_SOUTH],
["wait", 50],
[self.press_button, Buttons.BTN_SOUTH],
["wait", 50],
[self.release_button, Buttons.BTN_SOUTH]
])
The above macro will release the jump button, wait 50 milliseconds, press the jump button again, wait another 50 milliseconds, then finally release the jump button.
You can include any number of actions inside a macro. The macro accepts a list of actions, which are also arrays. The first element of the array is the action to perform, and the second element is the value to use.
Remember you must initiate combos inside the init method and not the run method, otherwise you will overwrite your macro every loop.
Once initialised, the macro can be used in your run method, like so:
def run(self, frame):
# Start Bunny hop macro when the X/A button is pressed
if (self.is_actual_button_pressed(Buttons.BTN_SOUTH)):
self.print_log("Bunny Hop Macro running", PrintColors.COLOR_GREEN)
self.bunny_hop.run()
# Cycle the macro, this iterate through he macro actions loop at a time
self.bunny_hop.cycle()
return frame
It's important that the cycle function runs at the end of the run method. If you have multiple combos, which trigger off the same buttons, the order of which cycle is run for each combo will make a difference to the output.
You can also randomise your wait times by using "wait_random", like this:
self.bunny_hop = Macro(controller, [
[self.release_button, Buttons.BTN_SOUTH],
["wait_random", 50, 100],
[self.press_button, Buttons.BTN_SOUTH],
["wait_random", 50, 100],
[self.release_button, Buttons.BTN_SOUTH]
])
Speak
You can make the PC "speak" any text that you pass through the following function:
self.say("Hello, I am your PC speaking!")
Screen Reader
You can utilise the EasyOCR screen reader, by doing the following:
class Script(Template):
def __init__(self, controller, report):
super().__init__(controller, report)
# Setup a OCR using GPU
self.init_ocr(['en'], True)
def run(self, frame):
# Read text from the frame
result = self.read_text(frame, 100, 100, 200, 100)
# Print the result
if (len(result) > 0):
self.print_log("Text Found: " + str(result[0]), PrintColors.COLOR_GREEN)
return frame
The above code will read the screen, and return a list of all the words it found. Remember to first utilise init_ocr in your init method. The first parameter of this function determines the language pack to use, and the second parameter determines if it will use GPU or CPU (True = GPU, False = CPU).
You can find the supported language list here: https://www.jaided.ai/easyocr/.
Image Search (Template Matching)
Using the power of OpenCV, you can use template matching to locate an image inside any other image:
class Script(Template):
def __init__(self, controller, report):
super().__init__(controller, report)
# Load the template image
self.leaf = cv2.imread('scripts/TestScript/leaf.png')
def run(self, frame):
# Search for the template image
result = self.search_image(frame, self.leaf, 0.95)
# Draw a rectangle around the result
if (result != None):
cv2.rectangle(frame, result[0], result[1], (0,0,255), 5)
return frame
In the above script we are searching our frame for a leaf image. The leaf image was extracted from a base image. You can set the threshold to a higher vale to make the search more strict. The threshold is between 0 and 1.
Settings Window
When creating your script you may want to provide toggles to specific features. You can utilise the settings window to accomplish this.
To create a setting window, create a file inside your script folder called settings.ini, here is an example of the available field types:
[bunny_hop]
type = onoff
label = Bunny Hop
default = False
value = False
[slider_1]
type = slider
label = Cool Slider
default = 100
value = 50
min = 0
max = 100
step = 1
[dropdown_1]
type = dropdown
label = Cool Dropdown
default = Option 1
options = Option 1, Option 2, Option 3
value = 50
[toggle_label]
type = label
label = Hello world!
color = red
[weights]
type = filelister
label = Weights
default = GENERIC
value = GENERIC
path = /weights
ext = .weights
You can receive any of the values by calling the following function:
self.get_setting('ads_strafe')
You can set any of the values by calling the following function:
self.set_setting('ads_strafe', False)
Importing Modules
You may want to import your own modules. Since you need to load in the ScriptCore template, by default the directory path is inside Copycat's root directory.
To import your own modules, that exist inside your scripts folder, you can use the following code:
from ControllerMapping import Buttons
from Macro import Macro
from ScriptCore import Template, PrintColors
# Import any class from within this script's folder
import sys
sys.path.append('scripts/ImportExample')
from MyClass import MyClass
class Script(Template):
def __init__(self, controller, report):
super().__init__(controller, report)
self.myClass = MyClass()
def run(self, frame):
print(self.myClass.message)
return frame
Its important to notice that the import of MyClass is below the other imports. Since those modules exist inside Copycat's root directory, and the MyClass exists inside the script directory. We can swap directories using the sys module.
You can access the script functions inside your additional module, by importing the Copycat module. Like so:
import Copycat
class MyClass:
def get_left_trigger_example(self):
return Copycat.script.get_actual_left_trigger()
Example - Bunny Hop
Here is an example of a bunny hop macro:
from ScriptCore import Template, PrintColors
from ControllerMapping import Buttons
from Macro import Macro
# This is an example script that will be run by the main program.
class Script(Template):
def __init__(self, controller, report):
super().__init__(controller, report)
# Setup a macro
self.bunny_hop = Macro(controller, [
[self.release_button, Buttons.BTN_SOUTH],
["wait", 50],
[self.press_button, Buttons.BTN_SOUTH],
["wait", 50],
[self.release_button, Buttons.BTN_SOUTH]
])
def run(self, frame):
# Start Bunny hop macro when the X/A button is pressed
if (self.is_actual_button_pressed(Buttons.BTN_SOUTH)):
self.print_log("Bunny Hop Macro running", PrintColors.COLOR_GREEN)
self.bunny_hop.run()
# Cycle the macro, this iterate through he macro actions loop at a time
self.bunny_hop.cycle()
return frame
Licence - Creation or Update
If you have a sellers account, you can create licences using your api key on the fly, via a HTTP request. To do this in python it would look something like this:
import requests
response = requests.post('https://copycat.stickassist.com/licence-api/create-or-update', {
'api_key': 'YOUR_API_KEY',
'info[script_name]' : 'YOUR_SCRIPT_NAME',
'info[expire_date]' : '2024-01-01', # Optional (Year)-(month)-(day)
'info[active]' : true, # Optional
'info[hwid]' : 'USER_HARDWARE_ID', # Optional
'info[licence]' : 'USER_LICENCE_KEY' # Optional
})
print(response.json())
If you provide the licence parameter, it will update an existing licence.
Licence - Validation
If you have a sellers account, you can validate a licence using your api key on the fly, via a HTTP request. To do this in python it would look something like this:
import requests
response = requests.post('https://copycat.stickassist.com/licence-api/validate', {
'api_key': 'YOUR_API_KEY',
'script_name' : 'YOUR_SCRIPT_NAME',
'hwid' : 'USER_HARDWARE_ID',
'licence' : 'USER_LICENCE_KEY'
})
print(response.json())
Decrypt File
If you need decrypt a file on the fly, you can use the following function:
self.decrypt_file('example.weights', 'password123')
Encrypt File
If you need encrypt a file on the fly, you can use the following function:
self.encrypt_file('example.weights', 'password123')
Get HWID
If you need to get the users HWID for some kind of verification, or other use you can use the following function:
self.get_hwid()
Map Range
If you need to map a number from one range to another, you can use the following function:
self.map_range(input_value, from_min, from_max, to_min, to_max)