diff --git a/.env b/.env new file mode 100644 index 0000000..048dc30 --- /dev/null +++ b/.env @@ -0,0 +1,34 @@ +# '++s': start, '++p': pause, '++q': die +#MANA_SPENT +#https://wiki.mediviastats.info/Medivia_Vocations_Introduction +#Quantos segundos para recuperar 1 de mana +#blank.png, food.png, hand.png, inside_backpack.png, trash_container.png +#RECONNECT caso caia ou deslogue o amiguinho irá tentar relogar + +DRAG_TIME=0 +SLEEP_ACTION=1 +MEDIVIA_WINDOW=Medivia +IMAGES_PATH=C:\Users\XXX\Pictures\bot +MEDIVIA_PATH=C:\Users\XXX\medivia\Medivia_D3D.exe +RECONNECT=true + +AGENT_NUMBER=3 + + +WINDOW_1=Player One +SPELL_NAME_1=adori gran +MANA_SPENT_1=50 +SECONDS_TO_ONE_MANA_1=3 +MANA_TRAIN_1=false + +WINDOW_2=Player Two +SPELL_NAME_2=adori vita vis +MANA_SPENT_2=180 +SECONDS_TO_ONE_MANA_2=1 +MANA_TRAIN_2=false + +WINDOW_3=Player Three +SPELL_NAME_3=adori vita vis +MANA_SPENT_3=180 +SECONDS_TO_ONE_MANA_3=3 +MANA_TRAIN_3=false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c0ddb1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +build/ +dist/ +main.spec \ No newline at end of file diff --git a/action.py b/action.py new file mode 100644 index 0000000..51ccda3 --- /dev/null +++ b/action.py @@ -0,0 +1,67 @@ +import pyautogui +import keyboard +import os +import time + +pyautogui.PAUSE = 1.4 +pyautogui.FAILSAFE = False + + +class Action: + + DRAG_DURATION = float(os.getenv('DRAG_TIME')) + TIME_SLEEP = float(os.getenv('SLEEP_ACTION')) + + @staticmethod + def dragRuneToHand(blank, hand): + if not blank: + return False + + pyautogui.moveTo(blank) + if Action.DRAG_DURATION > 0: + pyautogui.dragTo(hand, button='left', duration=Action.DRAG_DURATION) + time.sleep(Action.DRAG_DURATION) + else: + pyautogui.mouseDown(button='left') + pyautogui.moveTo(hand) + pyautogui.mouseUp(button='left') + return True + + @staticmethod + def openInsideBackpack(inside_bp): + if not inside_bp: + return False + + pyautogui.rightClick(inside_bp) + return True + + + @staticmethod + def conjureSpell(spell_name): + pyautogui.typewrite(spell_name) + keyboard.press_and_release('enter') + + + @staticmethod + def dragBackRune(blank, hand): + pyautogui.moveTo(hand) + if Action.DRAG_DURATION > 0: + pyautogui.dragTo(blank, button='left', duration=Action.DRAG_DURATION) + time.sleep(Action.DRAG_DURATION) + else: + pyautogui.mouseDown(button='left') + pyautogui.moveTo(blank) + pyautogui.mouseUp(button='left') + + @staticmethod + def eatFood(food): + pyautogui.moveTo(food) + pyautogui.click(button='right', clicks=6, interval=0.30) + + @staticmethod + def logout(): + keyboard.press_and_release('ctrl+q') + + @staticmethod + def login(): + keyboard.press_and_release('enter') \ No newline at end of file diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..8fdf4d3 --- /dev/null +++ b/agent.py @@ -0,0 +1,213 @@ +import os +import datetime as dt +from action import Action +from item import Item +import pywinauto +import pyautogui +import time +pyautogui.FAILSAFE = False + + +class Agent: + + STOPPED = 0 + RUNNING = 1 + WAITING = 2 + FINISHED = 3 + hand = None + food = None + inside_backpack = None + last_blank_pos = None + #if client delay and rune not back to bp, try to remove from hand + last_located_hand = None + trash_container = None + + def __init__(self, index, game_window): + self.index = str(index) + self.game_window = game_window + if self.isValidConfig(): + self.char_name = os.getenv('WINDOW_'+self.index) + self.spell = os.getenv('SPELL_NAME_'+self.index) + self.mana_spent = float(os.getenv('MANA_SPENT_'+self.index)) + self.seconds_to_one_mana = float(os.getenv('SECONDS_TO_ONE_MANA_'+self.index)) + self.mana_train = os.getenv('MANA_TRAIN_'+self.index) + self.time_to_spell = float(self.seconds_to_one_mana * self.mana_spent) + self.last_running_time = dt.datetime.now() + self.state = self.STOPPED + self.has_blank = True + self.total_mana_spent = 0 + self.total_rune_made = 0 + self.reconnect = os.getenv('RECONNECT') + print([self.char_name, self.spell, self.mana_spent, self.mana_train, self.seconds_to_one_mana, + self.last_running_time, self.state, self.reconnect]) + else: + pyautogui.alert('Burrão! \nConfigurações icorretas ['+str(index)+'].\nRefaça e comece novamente por favor', 'Atenção') + self.game_window.kill() + + + def isValidConfig(self): + + if not isinstance(self.game_window, pywinauto.Application): + return False + + if not self.index: + return False + + if not (os.getenv('WINDOW_'+self.index) or os.getenv('SPELL_NAME_'+self.index) + or os.getenv('MANA_SPENT_'+self.index) or os.getenv('SECONDS_TO_ONE_MANA_'+self.index) + or os.getenv('MANA_TRAIN_'+self.index)): + return False + + if not (self.representsInt(os.getenv('MANA_SPENT_'+self.index)) + or self.representsInt(os.getenv('SECONDS_TO_ONE_MANA_'+self.index))): + return False + + + return True + + + def isValidItems(self): + try: + if not self.getBlank(): + self.sendAlert('suas BLANKS') + return False + if not self.getHand(): + self.sendAlert('a MÃO vazia') + return False + if not self.getFood(): + self.sendAlert('suas FOODS') + return False + if not self.getTrashContainer(): + self.sendAlert('seu container de RETENÇÃO') + return False + + return True + except FileNotFoundError: + pyautogui.alert('Existem arquivos de imagem faltante / nome errado, refaça e reinicie a configuração', 'Atenção') + return False + + def sendAlert(self, obj): + pyautogui.alert('Não consegui encontrar '+obj+' , refaça e reinicie a configuração', 'Atenção') + + def representsInt(self, s): + try: + int(s) + return True + except ValueError: + return False + + def getHand(self): + return pyautogui.locateOnScreen(Item.image_path + '/hand.png', grayscale=True) + + def getFood(self): + return pyautogui.locateOnScreen(Item.image_path + '/food.png', grayscale=True) + + def getInsideBackpack(self): + return pyautogui.locateOnScreen(Item.image_path + '/inside_backpack.png', grayscale=True) + + def getTrashContainer(self): + return pyautogui.locateOnScreen(Item.image_path + '/trash_container.png', grayscale=True) + + def getMediviaLoginScreen(self): + return pyautogui.locateOnScreen(Item.image_path + '/medivia.png', grayscale=True) + + + def getBlank(self): + blank = pyautogui.locateOnScreen(Item.image_path + '/blank.png', grayscale=True) + #to back rune + self.last_blank_pos = blank + return blank + + def focus(self): + #app_dialog = self.game_window.top_window() + #app_dialog.wrapper_object().set_focus() + #pyautogui.moveTo(x,y) + #pywinauto.application.Application().connect(process=self.game_window.process).top_window().set_focus() + #time.sleep(0.5) + app_dialog = self.game_window.top_window() + app_dialog.set_focus() + + + def run(self): + self.state = self.RUNNING + if self.canSpell(): + self.focus() + self.throws() + + def die(self): + self.game_window.kill() + + + def canSpell(self): + now = dt.datetime.now() + spentTime = (now-self.last_running_time).total_seconds() + return self.state != self.FINISHED and spentTime >= self.time_to_spell + + def wasDisconneted(self): + if self.reconnect == 'true' and self.state != self.FINISHED: + loginScreen = self.getMediviaLoginScreen() + if loginScreen: + print('Char '+self.char_name+' foi desconectado por algum motivo, reconectando...') + Action.login() + time.sleep(2) + return True + return False + + def throws(self): + self.last_running_time = dt.datetime.now() + if self.mana_train == 'true': + Action.conjureSpell(os.getenv('SPELL_NAME_'+self.index)) + self.total_mana_spent += self.mana_spent + food = self.getFood() + if not food and self.wasDisconneted() == False: + print('Char '+self.char_name+' sem food e conectado, deslogando') + self.state = self.FINISHED + Action.logout() + self.game_window.kill() + return 0 + else: + Action.eatFood(self.getFood()) + self.state = self.WAITING + print(self.char_name+': Trabalho feito, aguardando mana') + else: + hand = self.getHand() + if not hand: + #If rune stack on hand + Action.dragBackRune(self.getTrashContainer(), self.last_located_hand) + else: + self.last_located_hand = hand + + # try to drag blank to hand + if not Action.dragRuneToHand(self.getBlank(), hand): + self.has_blank = False + # if blank not found try open inside blank backpack + if not Action.openInsideBackpack(self.getInsideBackpack()): + if self.wasDisconneted() == False: + print('Char '+self.char_name+' sem blank e conectado, deslogando') + # logout if not found inside blank backpack + self.state = self.FINISHED + Action.logout() + self.game_window.kill() + return 0 + else: + #if found blank backpack dragToHand again + Action.dragRuneToHand(self.getBlank(), hand) + self.has_blank = True + + Action.conjureSpell(os.getenv('SPELL_NAME_'+self.index)) + Action.dragBackRune(self.last_blank_pos, hand) + self.total_rune_made += 1 + self.total_mana_spent += self.mana_spent + food = self.getFood() + if not food and self.wasDisconneted() == False: + print('Char '+self.char_name+' sem food e conectado, deslogando') + self.state = self.FINISHED + Action.logout() + self.game_window.kill() + return 0 + else: + Action.eatFood(self.getFood()) + self.state = self.WAITING + print(self.char_name+': Trabalho feito, aguardando mana') + + \ No newline at end of file diff --git a/computer.ico b/computer.ico new file mode 100644 index 0000000..b351d3b Binary files /dev/null and b/computer.ico differ diff --git a/image_name_examples/blank.PNG b/image_name_examples/blank.PNG new file mode 100644 index 0000000..477182a Binary files /dev/null and b/image_name_examples/blank.PNG differ diff --git a/image_name_examples/food.PNG b/image_name_examples/food.PNG new file mode 100644 index 0000000..19dde00 Binary files /dev/null and b/image_name_examples/food.PNG differ diff --git a/image_name_examples/hand.PNG b/image_name_examples/hand.PNG new file mode 100644 index 0000000..65b1c36 Binary files /dev/null and b/image_name_examples/hand.PNG differ diff --git a/image_name_examples/inside_backpack.PNG b/image_name_examples/inside_backpack.PNG new file mode 100644 index 0000000..36542a2 Binary files /dev/null and b/image_name_examples/inside_backpack.PNG differ diff --git a/image_name_examples/medivia.PNG b/image_name_examples/medivia.PNG new file mode 100644 index 0000000..038ebe5 Binary files /dev/null and b/image_name_examples/medivia.PNG differ diff --git a/image_name_examples/trash_container.PNG b/image_name_examples/trash_container.PNG new file mode 100644 index 0000000..fb58f0a Binary files /dev/null and b/image_name_examples/trash_container.PNG differ diff --git a/item.py b/item.py new file mode 100644 index 0000000..93b07eb --- /dev/null +++ b/item.py @@ -0,0 +1,8 @@ +import os +import pyautogui +pyautogui.FAILSAFE = False + + +class Item: + + image_path = os.getenv('IMAGES_PATH') \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f534af5 --- /dev/null +++ b/main.py @@ -0,0 +1,148 @@ +import settings +from agent import Agent +import keyboard +import time +import os +import pywinauto +from pynput import keyboard +import threading +from PIL import Image +import sys +import pyautogui +pyautogui.FAILSAFE = False + + +agents = [] +run = 0 + + +def generateAgents(): + agent_number = int(os.getenv('AGENT_NUMBER')) + medivia_path = os.getenv('MEDIVIA_PATH') + + + i = 1 + while i <= agent_number: + app = pywinauto.Application().start(r'{}'.format(medivia_path)) + print(app.process) + agents.append(Agent(i, app)) + i += 1 + +def hasAgentRunning(): + global run + for agent in agents: + if agent.state == Agent.RUNNING: + return True + if len(agents) == 0: + run = 0 + return False + + +def pause(): + print('Vou dar uma pausadinha amigão') + global run + run = 0 + + +def die(icon = None): + pyautogui.alert('Adeus, até a proxima', 'Tchau') + print('Adeus amigão, espero que eu tenha lhe ajudado') + global run + run = 0 + change_state.stop() + #For icon tray + if icon: + icon.visible = False + icon.stop() + sys.exit() + +def isValidItems(): + for agent in agents: + if not agent.isValidItems(): + print('Configuração de imagens / itens errados ['+str(agent.char_name)+']') + return False + + return True + + +def start(): + global run + print('Vamos la!') + run = 1 + threading.Thread(target = init).start() + +def init(): + global run + lock = threading.Lock() + while run: + total_mana_spent = 0 + total_rune_made = 0 + changed = False + with lock: + for agent in agents: + if not hasAgentRunning() and agent.canSpell(): + print('Agent '+agent.char_name+' preparado para fazer') + agent.run() + changed = True + total_mana_spent += agent.total_mana_spent + total_rune_made += agent.total_rune_made + if agent.state == Agent.FINISHED: + print('Agent '+agent.char_name+' removido por falta de blank ou food') + agents.remove(agent) + if changed: + print('Total de runas feitas: '+str(total_rune_made)) + print('Total de mana gasta: '+str(total_mana_spent)) + + time.sleep(1) + +def reboot(): + generateAgents() + start() + +def startDraw(): + threading.Thread(target = draw).start() + +def draw(): + global run + run = not run + time.sleep(5) + pyautogui.click() + distance = 50 + lock = threading.Lock() + initialPosition = pyautogui.position() + while distance > 0 and run: + with lock: + pyautogui.dragRel(distance, 0) + distance = distance - 5 # ❹ + pyautogui.dragRel(0, distance, duration=0.0) + pyautogui.dragRel(-distance, 0, duration=0.1) + distance = distance - 5 + pyautogui.dragRel(0, -distance, duration=0.2) + if run: + pyautogui.dragTo(initialPosition) + time.sleep(5) + pyautogui.dragTo(initialPosition, duration=0.1) + time.sleep(5) + pyautogui.dragTo(initialPosition, duration=0.2) + time.sleep(5) + pyautogui.dragTo(initialPosition, button='left', duration=0.2) + time.sleep(5) + pyautogui.dragTo(initialPosition, button='left', duration=1) + + pyautogui.moveTo(initialPosition) + pyautogui.mouseDown(button='left') + pyautogui.move(0, 30) + pyautogui.mouseUp(button='left') + +if __name__ == "__main__": + generateAgents() + with keyboard.GlobalHotKeys({ + '++s': start, + '++p': pause, + '++q': die, + '++t': startDraw}) as change_state: + change_state.join() + + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..823e9a9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,34 @@ +altgraph==0.17 +astroid==2.4.1 +autopep8==1.5.2 +colorama==0.4.3 +comtypes==1.1.7 +future==0.18.2 +isort==4.3.21 +keyboard==0.13.5 +lazy-object-proxy==1.4.3 +mccabe==0.6.1 +MouseInfo==0.1.3 +numpy==1.18.4 +pefile==2019.4.18 +Pillow==7.1.2 +PyAutoGUI==0.9.50 +pycodestyle==2.6.0 +PyGetWindow==0.0.8 +PyInstaller==3.6 +pylint==2.5.2 +PyMsgBox==1.0.8 +pynput==1.6.8 +pyperclip==1.8.0 +PyRect==0.1.4 +PyScreeze==0.1.26 +pystray==0.15.0 +python-dotenv==0.13.0 +PyTweening==1.0.3 +pywin32==227 +pywin32-ctypes==0.2.0 +pywinauto==0.6.8 +six==1.14.0 +toml==0.10.1 +wrapt==1.12.1 +wxPython==4.1.0 diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..8ea31fb --- /dev/null +++ b/settings.py @@ -0,0 +1,9 @@ +from dotenv import load_dotenv +import sys +import os + +if getattr(sys, 'frozen', False): + os.chdir(sys._MEIPASS) +load_dotenv() + +load_dotenv(verbose=True) diff --git a/tray.png b/tray.png new file mode 100644 index 0000000..a0eb391 Binary files /dev/null and b/tray.png differ