import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import subprocess
import os
import sys
import time
import psutil
import logging
from datetime import datetime
import ctypes
import webbrowser
import json
def get_base_path():
""" Get the base path for the application """
if getattr(sys, 'frozen', False):
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
return os.path.dirname(sys.executable)
else:
return os.path.dirname(os.path.abspath(__file__))
def get_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
base_path = get_base_path()
return os.path.join(base_path, relative_path)
class LevelChangeApp:
def __init__(self, master):
self.master = master
master.title("Marvel Powers United VR Level Select Tool v1.0.2")
master.geometry("800x850")
# Set up dark theme
self.style = ttk.Style()
self.style.theme_use('clam')
self.style.configure('.', background='#2E2E2E', foreground='white')
self.style.configure('TButton', background='#3E3E3E', foreground='white')
self.style.map('TButton', background=[('active', '#4E4E4E')])
master.configure(bg='#2E2E2E')
# Configure styles for better readability
self.style.configure('TEntry', fieldbackground='#4E4E4E', foreground='white')
self.style.configure('TCombobox', fieldbackground='#4E4E4E', foreground='black', selectbackground='#6E6E6E', selectforeground='white')
self.style.map('TCombobox', fieldbackground=[('readonly', '#4E4E4E')], selectbackground=[('readonly', '#6E6E6E')])
self.setup_logging()
# Initialize attributes
self.game_pid = None
self.uuuclient_process = None
self.console_key = '`' # Default to backtick
self.dll_injected = False
self.load_settings()
self.create_widgets()
self.create_menu()
def load_settings(self):
self.settings_file = get_path("settings.json")
if os.path.exists(self.settings_file):
with open(self.settings_file, "r") as f:
self.settings = json.load(f)
else:
self.settings = {
"console_key": "`",
"dont_show_nick": False,
"dont_show_danger": False
}
def save_settings(self):
with open(self.settings_file, "w") as f:
json.dump(self.settings, f)
def create_menu(self):
menubar = tk.Menu(self.master)
self.master.config(menu=menubar)
main_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Menu", menu=main_menu)
main_menu.add_command(label="About", command=self.show_about)
main_menu.add_command(label="Contact", command=self.show_contact)
main_menu.add_command(label="Options", command=self.show_options)
main_menu.add_separator()
main_menu.add_command(label="Exit", command=self.exit_application)
def show_options(self):
options_window = tk.Toplevel(self.master)
options_window.title("Options")
options_window.geometry("300x200")
options_window.configure(bg='#2E2E2E')
ttk.Button(options_window, text="Reset All Settings", command=self.reset_settings).pack(pady=10)
def reset_settings(self):
self.settings = {
"console_key": "`",
"dont_show_nick": False,
"dont_show_danger": False
}
self.save_settings()
self.key_var.set(self.settings["console_key"])
messagebox.showinfo("Settings Reset", "All settings have been reset to default.")
def update_console_key(self):
new_key = self.key_var.get()
if len(new_key) != 1:
messagebox.showerror("Invalid Key", "Please enter a single character.")
return
self.settings["console_key"] = new_key
self.save_settings()
self.log_to_console(f"Console key updated to: {new_key}")
messagebox.showwarning("UUUClient Settings", "Remember to update the console key in UUUClient as well!")
def load_level(self):
if not self.game_pid:
messagebox.showerror("Error", "Game is not running. Please start the game first.")
return
if not self.dll_injected:
messagebox.showerror("Error", "DLL is not injected. Please inject the DLL first.")
return
level = self.level_var.get()
if level == "Choose a level":
self.log_to_console("Warning: Please select a level first")
return
if level in ["Nick Test Arena (Wolverine)", "Danger Room - Room for Training (Wolverine)"]:
if not self.show_warning_popup(level):
return
command = self.get_command_for_level(level)
self.send_command(command)
def show_warning_popup(self, level):
if (level == "Nick Test Arena (Wolverine)" and self.settings["dont_show_nick"]) or (level == "Danger Room - Room for Training (Wolverine)" and self.settings["dont_show_danger"]):
return True
result = messagebox.askokcancel(
"Warning",
"NOTE: This level requires you to go to the Hub first and choose Wolverine before loading this level! "
"Once the level loads you can switch characters.",
icon='warning'
)
if result:
dont_show_var = tk.BooleanVar()
dont_show_window = tk.Toplevel(self.master)
dont_show_window.title("Don't show again")
dont_show_window.geometry("300x100")
dont_show_window.configure(bg='#2E2E2E')
ttk.Checkbutton(dont_show_window, text="Don't show this warning again for this level",
variable=dont_show_var).pack(pady=10)
ttk.Button(dont_show_window, text="OK", command=dont_show_window.destroy).pack()
self.master.wait_window(dont_show_window)
if dont_show_var.get():
if level == "Nick Test Arena (Wolverine)":
self.settings["dont_show_nick"] = True
else:
self.settings["dont_show_danger"] = True
self.save_settings()
return result
def setup_logging(self):
log_file = get_path("levelselectscript.log")
logging.basicConfig(filename=log_file, level=logging.INFO,
format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
logging.info("----------------------------------------")
logging.info("Script started")
def create_widgets(self):
# Title
title_label = tk.Label(self.master, text="Marvel Powers United VR Level Select Tool v1.0.2", font=("Arial", 16, "bold"), bg='#2E2E2E', fg='blue')
title_label.pack(pady=10)
# Modify Start Game button to Start/Restart Game
game_frame = ttk.Frame(self.master)
game_frame.pack(pady=10, fill=tk.X)
ttk.Button(game_frame, text="Start/Restart Game", command=self.start_restart_game).pack(side=tk.LEFT, expand=True, padx=(0, 5))
ttk.Button(game_frame, text="Inject DLL", command=self.inject_dll).pack(side=tk.LEFT, expand=True, padx=(5, 0))
# UUUClient control frame
uuu_frame = ttk.Frame(self.master)
uuu_frame.pack(pady=10, fill=tk.X)
ttk.Button(uuu_frame, text="Launch UUUClient", command=self.launch_uuuclient).pack(side=tk.LEFT, expand=True, padx=(0, 5))
ttk.Button(uuu_frame, text="Close UUUClient", command=self.close_uuuclient).pack(side=tk.LEFT, expand=True, padx=(5, 0))
# Console key frame
key_frame = ttk.Frame(self.master)
key_frame.pack(pady=10, fill=tk.X)
# Left side: Label and Entry
left_frame = ttk.Frame(key_frame)
left_frame.pack(side=tk.LEFT, expand=True, padx=(0, 5))
ttk.Label(left_frame, text="Console Key:", foreground='white', background='#2E2E2E').pack(side=tk.LEFT, padx=(0, 5))
self.key_var = tk.StringVar(value=self.console_key)
self.key_entry = ttk.Entry(left_frame, textvariable=self.key_var, width=5, style='TEntry')
self.key_entry.pack(side=tk.LEFT)
# Right side: Update button
ttk.Button(key_frame, text="Update Key", command=self.update_console_key).pack(side=tk.LEFT, expand=True, padx=(5, 0))
# Level selection frame
level_frame = ttk.Frame(self.master)
level_frame.pack(pady=10, fill=tk.X)
self.level_var = tk.StringVar()
self.level_dropdown = ttk.Combobox(level_frame, textvariable=self.level_var, state="readonly", style='TCombobox', width=50)
self.level_dropdown['values'] = (
"Game Menu", "Ops - Hub", "Tutorial on Moving",
"Stark Tower - Tutorial Intro Sequence", "Jotunheim", "Throne Room - Asgard",
"Palace - Attilan", "Void - Dark Dimension", "Forest - Halfworld",
"Marketplace - Knowhere Marketplace", "Downtown - Downtown New York",
"Arena - Sakaar Arena", "Research Lab - Wakanda", "Hangar - X-Mansion Hangar",
"Danger Room - Room for Training (Wolverine)", "Sanctuary II - Thanos Boss Battle",
"Nick Test Arena (Wolverine)"
)
self.level_dropdown.set("Choose a level")
self.level_dropdown.pack(side=tk.LEFT, expand=True, padx=(0, 5))
ttk.Button(level_frame, text="Load Selected Level", command=self.load_level).pack(side=tk.LEFT, expand=True, padx=(5, 0))
# Console output
self.console = scrolledtext.ScrolledText(self.master, width=90, height=20, bg='#1E1E1E', fg='white')
self.console.pack(pady=10)
# Add Exit Game button above Exit button
ttk.Button(self.master, text="Exit Game", command=self.exit_game).pack(pady=5)
ttk.Button(self.master, text="Exit Tool", command=self.exit_application).pack(pady=5)
def show_about(self):
about_window = tk.Toplevel(self.master)
about_window.title("About")
about_window.geometry("400x200")
about_window.configure(bg='#2E2E2E')
about_text = ("DeliciousMeatPop from ARMGDDN Games made this tool for the\n"
"Marvel Powers United VR Revival Discord")
ttk.Label(about_window, text=about_text, background='#2E2E2E', foreground='white', wraplength=380).pack(pady=10)
def create_hyperlink(parent, text, url):
link = ttk.Label(parent, text=text, foreground="light blue", cursor="hand2", background='#2E2E2E')
link.pack()
link.bind("<Button-1>", lambda e: webbrowser.open_new(url))
return link
create_hyperlink(about_window, "ARMGDDN Games", "https://t.me/ARMGDDNGames")
create_hyperlink(about_window, "Marvel Powers United VR Revival Discord", "https://discord.com/invite/28fRTaTSd9")
def show_contact(self):
contact_window = tk.Toplevel(self.master)
contact_window.title("Contact")
contact_window.geometry("300x200")
contact_window.configure(bg='#2E2E2E')
ttk.Button(contact_window, text="DMP on Reddit", command=lambda: webbrowser.open("https://www.reddit.com/user/DeliciousMeatPop/")).pack(pady=5)
ttk.Button(contact_window, text="DMP on Telegram", command=lambda: webbrowser.open("https://t.me/SickSoThr33")).pack(pady=5)
ttk.Button(contact_window, text="DMP on Discord", command=lambda: webbrowser.open("https://discordapp.com/users/191105213808115712")).pack(pady=5)
ttk.Button(contact_window, text="DMP on Github", command=lambda: webbrowser.open("https://github.com/KaladinDMP")).pack(pady=5)
def exit_application(self):
if messagebox.askokcancel("Exit", "Are you sure you want to exit?"):
self.master.quit()
def log_to_console(self, message):
self.console.insert(tk.END, f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}\n")
self.console.see(tk.END)
logging.info(message)
def exit_game(self):
if self.game_pid:
try:
process = psutil.Process(self.game_pid)
process.terminate()
self.log_to_console("Game process terminated")
self.game_pid = None
self.dll_injected = False
except psutil.NoSuchProcess:
self.log_to_console("Game process not found")
else:
self.log_to_console("No game process to terminate")
def wait_for_process(self, process_name):
while True:
for proc in psutil.process_iter(['name', 'pid']):
if proc.name() == process_name:
self.game_pid = proc.pid
self.log_to_console(f"{process_name} process found with PID: {self.game_pid}")
return
time.sleep(1)
def start_restart_game(self):
if self.game_pid:
self.exit_game()
time.sleep(2) # Wait for the game to close
self.log_to_console("Starting game")
game_path = get_path(os.path.join("WindowsNoEditor", "MarvelVR", "Binaries", "Win64", "MarvelVR-Win64-Shipping.exe"))
subprocess.Popen(game_path)
self.log_to_console("Game process started. Please wait...")
self.wait_for_process("MarvelVR-Win64-Shipping.exe")
def inject_dll(self):
if not self.game_pid:
self.log_to_console("Error: Game process not found. Please start the game first.")
return
self.log_to_console("Launching minimized UUUClient and injecting DLL")
uuuclient_path = get_path(os.path.join("InjectUUU", "UUUClient.exe"))
if not os.path.exists(uuuclient_path):
self.log_to_console(f"Error: UUUClient.exe not found at {uuuclient_path}")
return
try:
# Constants from the win32 API for minimizing the window
SW_SHOWMINIMIZED = 2
# Launch minimized UUUClient
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = SW_SHOWMINIMIZED
self.uuuclient_process = subprocess.Popen(uuuclient_path, startupinfo=startupinfo)
# Wait for UUUClient to initialize
time.sleep(2)
injector_path = get_path(os.path.join("InjectUUU", "Injector.exe"))
uuu_dll = get_path(os.path.join("InjectUUU", "UniversalUE4Unlocker.dll"))
if not os.path.exists(injector_path):
self.log_to_console(f"Error: Injector.exe not found at {injector_path}")
return
if not os.path.exists(uuu_dll):
self.log_to_console(f"Error: UniversalUE4Unlocker.dll not found at {uuu_dll}")
return
# Run the injector
subprocess.run([injector_path, "--process-id", str(self.game_pid), "-inject", uuu_dll],
check=True,
startupinfo=startupinfo)
self.log_to_console("UUU DLL injected successfully")
self.dll_injected = True
# Wait for 10 seconds
self.log_to_console("Waiting 10 seconds before closing UUUClient")
time.sleep(10)
# Close UUUClient
if self.uuuclient_process:
self.uuuclient_process.terminate()
self.uuuclient_process = None
self.log_to_console("UUUClient closed")
except subprocess.CalledProcessError as e:
self.log_to_console(f"Error injecting DLL: {e}")
self.dll_injected = False
except Exception as e:
self.log_to_console(f"Unexpected error: {e}")
finally:
if self.uuuclient_process:
self.uuuclient_process.terminate()
self.uuuclient_process = None
def launch_uuuclient(self):
# Close UUUClient if it's running
if self.uuuclient_process:
self.uuuclient_process.terminate()
self.uuuclient_process = None
self.log_to_console("UUUClient closed")
# Launch visible UUUClient
self.log_to_console("Launching visible UUUClient")
uuuclient_path = get_path(os.path.join("InjectUUU", "UUUClient.exe"))
self.uuuclient_process = subprocess.Popen(uuuclient_path)
self.log_to_console("Visible UUUClient launched")
def close_uuuclient(self):
if self.uuuclient_process:
self.log_to_console("Closing UUUClient")
self.uuuclient_process.terminate()
self.uuuclient_process = None
self.log_to_console("UUUClient closed")
else:
self.log_to_console("No UUUClient is running")
def get_command_for_level(self, level):
commands = {
"Game Menu": "open menu",
"Ops - Hub": "open Ops",
"Tutorial on Moving": "open MoveTutorial",
"Stark Tower - Tutorial Intro Sequence": "open StarkTower",
"Jotunheim": "open Jotunheim",
"Throne Room - Asgard": "open ThroneRoom",
"Palace - Attilan": "open Palace",
"Void - Dark Dimension": "open Void",
"Forest - Halfworld": "open Forest",
"Marketplace - Knowhere Marketplace": "open Marketplace",
"Downtown - Downtown New York": "open DownTown",
"Arena - Sakaar Arena": "open Arena",
"Research Lab - Wakanda": "open ResearchLab",
"Hangar - X-Mansion Hangar": "open Hangar",
"Danger Room - Room for Training (Wolverine)": "open DangerRoom03",
"Sanctuary II - Thanos Boss Battle": "open SanctuaryII",
"Nick Test Arena (Wolverine)": "open Nick_TestArena"
}
return commands.get(level, "")
def send_command(self, command):
self.log_to_console(f"Sending command: {command}")
try:
subprocess.run(['powershell', '-Command', f"""
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate({self.game_pid});
Start-Sleep -Milliseconds 500;
$wshell.SendKeys('{self.console_key}');
Start-Sleep -Milliseconds 500;
$wshell.SendKeys('{command}');
Start-Sleep -Milliseconds 500;
$wshell.SendKeys('{{ENTER}}');
"""])
self.log_to_console(f"Command sent: {command}")
except Exception as e:
self.log_to_console(f"Error: Failed to send command: {str(e)}")
if __name__ == "__main__":
root = tk.Tk()
app = LevelChangeApp(root)
root.mainloop()