Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
DQconfig.ini
PARconfig.ini
PyAutoRaid.log
110 changes: 93 additions & 17 deletions Modules/PyAutoRaid.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@
import threading
from screeninfo import get_monitors
from tkinter import messagebox
from tkinter import scrolledtext
from tkinter import ttk
from tkinter import *
from ttkthemes import ThemedTk
import pyscreeze
from faction_wars.FactionWars import FactionWarsCommand
from dungeons.IronTwins import IronTwinsCommand
from doom_tower.DoomTower import DoomTowerCommand

pyscreeze.USE_IMAGE_NOT_FOUND_EXCEPTION = False
# Configure logging
logging.basicConfig(
Expand Down Expand Up @@ -773,11 +778,14 @@ def __init__(self, master):
self.AS_bought = 0
self.MS_bought = 0
self.manual_run_triggered = False

self.command_registry = {
'rewards': RewardsCommand(self),
'daily_ten_classic_arena': DailyTenClassicArenaCommand(self),
'clanboss': ClanBossCommand(self),
'faction_wars': FactionWarsCommand(self, logger),
'iron_twins': IronTwinsCommand(self, logger),
'doom_tower': DoomTowerCommand(self, logger)
# Add other commands as needed
}

Expand Down Expand Up @@ -865,20 +873,43 @@ def Check_os(self):

def find_raid_path(self):
try:
logger.info("Starting focused search for Raid.exe...")
appdata_local = os.path.join(os.environ['LOCALAPPDATA'])
raid_feature = "Raid.exe"
for root, dirs, files in os.walk(appdata_local):
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this change made my start up time go from 1min to 2s

if raid_feature in dirs or raid_feature in files:

# Most specific likely path
current_path = os.path.join(appdata_local, "PlariumPlay", "StandAloneApps", "raid-shadow-legends")

# Step 1: Check if the likely path exists; move backward if it doesn't
while not os.path.exists(current_path) and current_path != appdata_local:
logger.debug(f"Path not found: {current_path}. Moving to parent directory.")
current_path = os.path.dirname(current_path)

if not os.path.exists(current_path):
logger.error("No valid path found starting from likely paths.")
self.steps["Raid_path"] = "False"
sys.exit(1)

logger.debug(f"Valid path found: {current_path}. Starting recursive search here.")

# Step 2: Perform a recursive search starting from the valid path
for root, dirs, files in os.walk(current_path, topdown=True):
# Check for the target file
if raid_feature in files:
raidloc = os.path.join(root, raid_feature)
logging.debug(f"Found Raid.exe installed at {raidloc}")
logger.debug(f"Found Raid.exe at {raidloc}")
self.steps["Raid_path"] = "True"
return raidloc

# Step 3: If not found, log an error
self.steps["Raid_path"] = "False"
logging.error("Raid.exe was not found.")
logger.error("Raid.exe was not found after recursive search.")
sys.exit(1)

except Exception as e:
logger.error(f"Error in find_raid_path: {e}")


def get_asset_path(self):
try:
# Start with the directory of the current script
Expand Down Expand Up @@ -1009,19 +1040,33 @@ def get_screen_info(self):
self.width = m.width
self.height = m.height
main = m.is_primary
if self.width != 1920 or self.height != 1080:
tkinter.messagebox.showerror(
"Warning",
"Your Screen pixel is not 1920 by 1080. This may cause issues",
)
logger.warning("Screen resolution is not 1920x1080.")
if main == True:

# Log screen resolution
logger.info(f"Detected screen resolution: width={self.width}, height={self.height}, primary={main}")

if main: # Only consider the primary monitor
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two different sized monitors and this error kept kicking off for me even though my main is set to 1920x1080.
moving the main check up here fixed it for me.

if self.width != 1920 or self.height != 1080:
logger.warning("Screen resolution is not 1920x1080. This may cause issues.")
tkinter.messagebox.showerror(
"Warning",
"Your screen resolution is not 1920x1080. This may cause issues.",
)
else:
logger.info("Screen resolution is 1920x1080.")

center_width = int((self.width / 2) - 450)
center_height = int((self.height / 2) - 300)
logger.info(f"Screen info obtained: width={self.width}, height={self.height}")
logger.info(f"Screen info obtained: center_width={center_width}, center_height={center_height}")
return (center_width, center_height)

# If no primary monitor was detected
logger.error("No primary monitor detected.")
raise RuntimeError("No primary monitor detected.")

except Exception as e:
logger.error(f"Error in get_screen_info: {e}")
raise


def window_sizing_centering(self):
try:
Expand All @@ -1044,10 +1089,12 @@ def initiate_raid(self, not_open):
try:
self.window_sizing_centering()
exit_add_image = os.path.join(self.asset_path, "exitAdd.png")
home_battle_button = os.path.join(self.asset_path, "battleBTN.png")

while True:
try:
# Attempt to locate the image
if pyautogui.locateOnScreen(exit_add_image, confidence=0.7) is not None:
if pyautogui.locateOnScreen(exit_add_image, confidence=0.7) is not None or pyautogui.locateOnScreen(home_battle_button, confidence=0.7):
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If exit image wasn't on the screen it would loop here without the tk ui popping up. Now I also check for being on the bastion without a pop up by looking for the battle button

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've realised this one might not be the intention you had. Is it supposed to attempt to escape so the exit button comes up and then close to make sure we are in the bastion without popups?

logger.info("Image found. Breaking the loop.")
break
else:
Expand Down Expand Up @@ -1137,7 +1184,6 @@ def run(self):
os.system("taskkill /f /im Raid.exe")
break

# GUI class with error handling and logging
class GUI:
def __init__(self, master):
try:
Expand All @@ -1156,7 +1202,7 @@ def __init__(self, master):
# Creating a ttk Frame which will contain all other widgets
main_frame = ttk.Frame(master)
main_frame.pack(fill=tkinter.BOTH, expand=True)
config_keys = ['rewards', 'daily_ten_classic_arena', 'clanboss']
config_keys = ['rewards', 'daily_ten_classic_arena', 'clanboss', 'faction_wars', 'iron_twins', 'doom_tower']
# Automated Mode Checkbox
self.automated_mode = tkinter.IntVar()
if settings_config.get("automated_mode") == 'True':
Expand All @@ -1177,7 +1223,7 @@ def checkbox_callback(var_name, index, mode, config_key, var):

# Other Checkboxes
self.checkbox_texts = [
"Collect Rewards", "Ten Classic Arena Battles", "Clan Boss",
"Collect Rewards", "Ten Classic Arena Battles", "Clan Boss", "Faction Wars", "Iron Twins", "Doom Tower"
]
self.checkboxes = []
self.vars = []
Expand All @@ -1200,6 +1246,18 @@ def checkbox_callback(var_name, index, mode, config_key, var):

self.btn_quit_all = ttk.Button(main_frame, text="Quit All", command=self.quit_all)
self.btn_quit_all.grid(row=len(self.checkbox_texts) + 3, column=1, padx=10, pady=(5, 5), sticky="E")

# Separator above the log box
self.separator2 = ttk.Separator(main_frame, orient='horizontal')
self.separator2.grid(row=len(self.checkbox_texts) + 4, column=0, columnspan=2, padx=10, pady=5, sticky="EW")

# Log Text Box
self.log_text = scrolledtext.ScrolledText(main_frame, wrap=tkinter.WORD, height=10, state="disabled")
self.log_text.grid(row=len(self.checkbox_texts) + 5, column=0, columnspan=2, padx=10, pady=(5, 10), sticky="EW")

# Redirect logs to the text box
self.redirect_logs()

logger.info("GUI initialized successfully.")
except Exception as e:
logger.error(f"Error initializing GUI: {e}")
Expand Down Expand Up @@ -1237,6 +1295,24 @@ def timer_thread(self):
logger.info("Timer thread started.")
except Exception as e:
logger.error(f"Error in timer_thread: {e}")

def redirect_logs(self):
"""Redirect log output to the text box."""
class TextHandler(logging.Handler):
def __init__(self, widget):
super().__init__()
self.widget = widget

def emit(self, record):
msg = self.format(record)
self.widget.config(state="normal")
self.widget.insert(tkinter.END, msg + "\n")
self.widget.config(state="disabled")
self.widget.see(tkinter.END)

handler = TextHandler(self.log_text)
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(handler)

def on_closing():
try:
Expand Down
118 changes: 118 additions & 0 deletions Modules/doom_tower/DoomTower.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import os
import time
import pyautogui

class Command:
def execute(self):
pass

class DoomTowerCommand(Command):
def __init__(self, app, logger):
self.app = app
self.logger = logger

def execute(self):
try:
self.logger.info("Starting Doom Tower task.")

# Define image paths
battle_btn_image = os.path.join(self.app.asset_path, "battleBTN.png")
doom_tower_image = os.path.join(self.app.asset_path, "doomTower.png")
doom_tower_next_stage = os.path.join(self.app.asset_path, "doomTowerNextStage.png")
doom_tower_start_battle = os.path.join(self.app.asset_path, "doomTowerStartBattle.png")
doom_tower_stages_screen = os.path.join(self.app.asset_path, "doomTowerScreen.png")
in_battle_image = os.path.join(self.app.asset_path, "inBattle.png")
in_mutli_battle_image = os.path.join(self.app.asset_path, "turnOffMultiBattle.png")
mutli_battle_complete_image = os.path.join(self.app.asset_path, "mutliBattleComplete.png")

self.logger.info("Attempting to close any existing pop-ups.")
self.app.delete_popup()

# Navigate to battle screen
time.sleep(2)
self.click_image(battle_btn_image, "Battle button")

# Navigate to Doom Tower
pyautogui.moveTo(960, 540)
pyautogui.dragRel(-600, 0, duration=0.5)
time.sleep(2)
self.click_image(doom_tower_image, "Doom Tower button")
time.sleep(2)

# Loop through this to complete Auto Climb and then Tower - Or replay failed stages
while pyautogui.locateOnScreen(doom_tower_next_stage, confidence=0.8):
self.click_image(doom_tower_next_stage, "Doom Tower Next Stage")

# If Wave stage Start Multi Battle
x, y = pyautogui.locateCenterOnScreen(doom_tower_start_battle, confidence=0.8)
pyautogui.click(x, y)
self.logger.info(f"Clicked on Doom Tower Start Battle at coordinates ({x}, {y}).")
time.sleep(2)

if pyautogui.locateOnScreen(doom_tower_start_battle, confidence=0.8):
self.logger.info("Must not have enough keys. Doom Tower Comlete")
pyautogui.press("esc")
time.sleep(1)
pyautogui.press("esc")
time.sleep(1)


# Wait for battle to complete
while pyautogui.locateOnScreen(in_battle_image, confidence=0.8) or pyautogui.locateOnScreen(in_mutli_battle_image, confidence=0.8):
self.logger.info("Waiting for the battle results.")
time.sleep(10)
while pyautogui.locateOnScreen(mutli_battle_complete_image, confidence=0.8):
# Back to doom tower to check if the boss is ready or failed run
pyautogui.press("esc")
time.sleep(1)
pyautogui.press("esc")
time.sleep(1)

if not pyautogui.locateOnScreen(doom_tower_stages_screen, confidence=0.7):
self.logger.info("Can't find doom tower stages screen.")

# If Boss Stage Click Boss Right Hand Side
if pyautogui.locateOnScreen(doom_tower_stages_screen, confidence=0.7):
self.logger.info("Found doom tower stages screen. Clicking Right Side")
pyautogui.click(1250, 500)
time.sleep(2)

# Click on boss that is stage 120
if pyautogui.locateOnScreen(doom_tower_stages_screen, confidence=0.7):
self.logger.info("Found doom tower stages screen. Clicking Middle (for stage 120)")
pyautogui.click(950, 500)
time.sleep(2)

self.click_image(doom_tower_start_battle, "Doom Tower Start Battle")
time.sleep(5)
# Wait for battle to complete
while pyautogui.locateOnScreen(in_battle_image, confidence=0.8):
self.logger.info("Waiting for the battle results.")
time.sleep(10)

pyautogui.press("esc")
self.app.back_to_bastion()
self.logger.info("Doom Tower task completed successfully.")
except Exception as e:
self.logger.error(f"Error in DoomTowerCommand: {e}", exc_info=True)
self.app.back_to_bastion()

def click_image(self, imagePath, description, retry=True):
"""Helper to click an image on screen."""
if retry:
# Retry until the image is found and clicked
while pyautogui.locateOnScreen(imagePath, confidence=0.8):
x, y = pyautogui.locateCenterOnScreen(imagePath, confidence=0.8)
pyautogui.click(x, y)
self.logger.info(f"Clicked on {description} at coordinates ({x}, {y}).")
time.sleep(2)
else:
# Single attempt to click the image
location = pyautogui.locateOnScreen(imagePath, confidence=0.8)
if location:
x, y = pyautogui.locateCenterOnScreen(imagePath, confidence=0.8)
pyautogui.click(x, y)
time.sleep(2)
self.logger.info(f"Clicked on {description} at coordinates ({x}, {y}).")
else:
self.logger.warning(f"{description} not found for a single click attempt.")
Loading