Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- Autoclicker
- Module workflow:
- 1. Setup regions using the HERO_SETTINGS and BOTTOM_LEFT_COORDS settings.
- 2. Create a MatchController object, which later acts as the "main trigger"
- that kicks off the autoclicking behaviour.
- 3. Iterate over each Region in SLOTS_REGION, assigning each region to
- a hero by checking against 'facepng' as found in HERO_SETTINGS.
- 4. Create a suitable Hero object using the data in HERO_SETTINGS. Then,
- register that object to the controller.
- 5. Call start() on the controller.
- """
- from sikuli import *
- COOLDOWN_PNG = Pattern("COOLDOWN_PNG.png").similar(0.61)
- ## SETTINGS
- ###########
- HERO_SETTINGS = {
- 'Maria': {
- 'facepng': "1550770324810.png",
- 'opener': [3, 2, 1],
- 'rotation': []
- },
- 'Annette': {
- 'facepng': "1550770347658.png",
- 'opener': [3, 1, 2],
- 'rotation': []
- },
- 'Viska': {
- 'facepng': "1550770359778.png",
- 'opener': [2, 3, 1],
- 'rotation': []
- },
- 'Aselica': {
- 'facepng': "1550770375611.png",
- 'opener': [2, 1, 2],
- 'rotation': []
- },
- }
- # Coordinates of bottom left corner of slot 1
- BOTTOM_LEFT_COORDS = (75, 757)#########made 45 from 75
- # Unused
- #lowCD = Pattern("CDimageLowDetection-1.png").similar(0.57)
- ###########
- # Derive dimensions of UI using settings
- # Use the horizontal distance from edge as unit of measure
- UNIT = BOTTOM_LEFT_COORDS[0] + 10
- # Infer skill dimensions
- SKILL_WIDTH = int(1 * UNIT)
- SKILL_HEIGHT = int(1.18 * UNIT)
- # Slot dimensions
- SLOT_HEIGHT = int(3.1 * UNIT)
- SLOT_WIDTH = int(SKILL_WIDTH * 3.2)
- # Infer upper bound of slots; this cuts off just above the eyes of the face
- SLOTS_Y_COORD = BOTTOM_LEFT_COORDS[1] - SLOT_HEIGHT
- # The distance between slots
- PADDING_BETWEEN_SLOTS = int(0.57 * UNIT)
- # Rough approximation of the area where the face usually is
- FACE_WIDTH, FACE_HEIGHT = SKILL_WIDTH, SKILL_HEIGHT
- # Compile the measurements above into a simple dict of {slotnum: Region obj}
- SLOTS_REGION = {
- 1: Region(
- UNIT - 9,
- SLOTS_Y_COORD,
- SLOT_WIDTH, SLOT_HEIGHT
- ),
- 2: Region(
- UNIT - 9 + PADDING_BETWEEN_SLOTS + SLOT_WIDTH,
- SLOTS_Y_COORD,
- SLOT_WIDTH, SLOT_HEIGHT
- ),
- 3: Region(
- UNIT - 9 + PADDING_BETWEEN_SLOTS * 2 + SLOT_WIDTH * 2,
- SLOTS_Y_COORD,
- SLOT_WIDTH, SLOT_HEIGHT
- ),
- 4: Region(
- UNIT - 9 + PADDING_BETWEEN_SLOTS * 3 + SLOT_WIDTH * 3,
- SLOTS_Y_COORD,
- SLOT_WIDTH, SLOT_HEIGHT
- ),
- }
- class MatchController:
- """Starts/stops matches and triggers skillcasts during matches"""
- def __init__(self, label):
- self.label = label # For display purposes
- self.heroes = []
- def register_hero(self, hero):
- if len(self.heroes) > 4:
- raise ValueError('cannot register more than 4 heroes')
- self.heroes.append(hero)
- def start(self):
- if not self.heroes:
- self.report('No heroes registered. Failed to start.')
- return
- if len(self.heroes) < 4:
- self.report('WARNING: Starting with <4 heroes registered. This '
- 'will probably not work well.')
- self.report('Starting MatchController ' + self.label)
- # Wait for heros face to appear
- hero = self.heroes[0]
- self.report('Waiting for {} to appear in {} face subregion...'.format(
- hero.facepng, hero))
- # Use the face subregion to check if the heros face appears
- # FOREVER means script will not continue unless it shows up
- hero.subregions['face'].wait(hero.facepng, FOREVER)
- self.report(' Found, starting clicks.')
- # Continuously fetch next skill and try to click
- while True:
- try:
- for hero in self.heroes:
- skillnum = hero.get_current_skillnum()
- if hero.is_on_cooldown(skillnum):
- # If previous skill on CD, means cast successfully
- # Only rotate and get new skillnum if successful cast
- skillnum = hero.get_new_skillnum()
- m = "Detected cooldown, now trying to cast s{}"
- self.report(m.format(skillnum))
- hero.click_skill(skillnum)
- except StopIteration:
- self.report('Stopping macro.')
- break
- except Exception as e:
- self.report('Unexpected error occurred.')
- raise e
- def report(self, msg):
- """Convenience method to show which Hero is producing the message."""
- print('[MatchController-{}] {}'.format(self.label, msg))
- def __repr__(self):
- """Define output when object is printed"""
- return "<MatchController '{}'>".format(self.label)
- class Hero:
- """The interface between MatchController and Sikuli."""
- def __init__(self, heroname, slotnum, facepng, opener=[], rotation=None):
- if slotnum not in SLOTS_REGION:
- raise ValueError('slotnum must be 1, 2, 3 or 4')
- self.name = heroname
- self.region = SLOTS_REGION[slotnum]#######sub s
- self.slotnum = slotnum
- self.facepng = facepng
- # Extract some data from the region to store as shortcuts
- self.topleft = self.region.getTopLeft()
- self.botleft = self.region.getBottomLeft()
- # Define subregions
- self.subregions = {
- 'skill1': Region(
- self.botleft.x,
- self.botleft.y - SKILL_HEIGHT,
- SKILL_WIDTH, SKILL_HEIGHT
- ),
- 'skill2': Region(
- self.botleft.x + 8 + SKILL_WIDTH,
- self.botleft.y - SKILL_HEIGHT,
- SKILL_WIDTH, SKILL_HEIGHT
- ),
- 'skill3': Region(
- self.botleft.x + 15 + SKILL_WIDTH * 2,
- self.botleft.y - SKILL_HEIGHT,
- SKILL_WIDTH, SKILL_HEIGHT
- ),
- 'face': Region(1,1,1,1)
- }
- # Typecheck and store opener and rotation args
- self.set_opener(opener)
- self.set_rotation(rotation)
- self.report('Initialized to slot {}'.format(self.slotnum))
- def assert_skillnum_list(self, skill_num_list):
- """Helper function to typecheck lists of skillnums"""
- try:
- assert list(filter(lambda i: 3 >= int(i) >= 1, skill_num_list))
- except (AssertionError, ValueError):
- # catches non-list-like stuff, empty lists and bad list elements
- msg = 'must provide a list containing 1, 2 or 3 only'
- raise ValueError(msg)
- def set_opener(self, skill_num_list=[]):
- if skill_num_list != []:
- self.assert_skillnum_list(skill_num_list)
- # Since we will be popping off values from the given list of numbers,
- # we store a copy of the list as ._opener and store the original list
- # as .opener
- self.opener = skill_num_list
- self._opener = [i for i in self.opener]
- def set_rotation(self, skill_num_list):
- skill_num_list = skill_num_list or [1, 2, 3]
- self.assert_skillnum_list(skill_num_list)
- # Since we want to reorder the numbers in the given list later,
- # we store a copy of the list as ._opener and store the original list
- # as .opener
- self.rotation = skill_num_list
- self._rotation = [i for i in self.rotation]
- def face_is_visible(self):
- """Shorthand func for checking if this heros face is still visible"""
- facereg = self.subregions['face']
- face = self.facepng
- return facereg.exists(face, 1) # 1 sec timeout
- def click_skill(self, skillnum):
- self.report('Clicking skill {}.'.format(skillnum))
- # Fetch and click region
- skillreg = self.subregions['skill'+str(skillnum)]
- skillreg.highlight(1) #############################
- click(skillreg)
- def get_current_skillnum(self):
- """Finds next skill to cast either from opener or from rotation.
- Raises StopIteration if face not found.
- Raises RuntimeError if theres no skills to cast for any reason
- """
- skillnum = 0
- if self._opener:
- skillnum = self._opener[0]
- else:
- # itertools.cycle.__next__() doesn't work due to Sikuli's py2
- # interpreter. Instead, cycle by popping initial value and
- # appending to end of list
- skillnum = self._rotation[0]
- if not skillnum:
- raise RuntimeError('Unexpectedly ran out of skills to cast')
- if not self.face_is_visible():
- self.report('Face no longer visible, sending stop signal!')
- raise StopIteration
- return skillnum
- def get_new_skillnum(self):
- if self._opener:
- self._opener.pop(0)
- else:
- self._rotation.pop(0)
- self._rotation.append(skillnum)
- return self.get_current_skillnum()
- def is_on_cooldown(self, skillnum):
- reg = self.subregions['skill' + str(skillnum)]
- return reg.exists(COOLDOWN_PNG, 0)
- def report(self, msg):
- """Convenience method to show which Hero is producing the message."""
- n = '[Hero-{}]'.format(self.name)
- print('{:>20} {}'.format(n, msg))
- def __repr__(self):
- """Define output when object is printed"""
- return "<Hero {} @slot{}>".format(self.name, self.slotnum)
- if __name__ == '__main__':
- # Create a MatchController
- controller = MatchController('LoV')
- # Assign each slot to a hero
- for slotnum, reg in SLOTS_REGION.items():
- for heroname, herodata in HERO_SETTINGS.items():
- print(reg)
- #reg.highlight(2)
- if reg.exists(herodata['facepng'], 0):
- reg.highlight(0.1)
- print('test2')
- # Use the ** 'splat' operator to unpack herodata as args:
- h = Hero(heroname, slotnum, **herodata)
- controller.register_hero(h)
- break
- controller.start()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement