diff --git a/Souce code/Snakey.py b/Souce code/Snakey.py new file mode 100644 index 0000000..dca4fbd --- /dev/null +++ b/Souce code/Snakey.py @@ -0,0 +1,312 @@ +import tkinter as tk +from tkinter import filedialog, simpledialog, messagebox +import pyttsx3 +import json +import os +import pygame +import webbrowser +import random +import logging + +# Setup logging +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") + +# Initialize settings file +SETTINGS_FILE = "SnakeyData.json" +DEFAULT_SETTINGS = { + "image": None, + "image_moving": None, + "speech_enabled": True, + "user_data": {}, + "PLAYED_BEFORE": "no", +} + +def load_settings(): + if not os.path.exists(SETTINGS_FILE): + save_settings(DEFAULT_SETTINGS) + try: + with open(SETTINGS_FILE, "r") as f: + data = json.load(f) + except json.JSONDecodeError as e: + logging.error(f"Failed to load settings: {e}") + data = DEFAULT_SETTINGS + + for key, value in DEFAULT_SETTINGS.items(): + if key not in data: + data[key] = value + + return data + +def save_settings(data): + try: + with open(SETTINGS_FILE, "w") as f: + json.dump(data, f, indent=4) + except Exception as e: + logging.error(f"Failed to save settings: {e}") + +# Load initial settings +settings = load_settings() + +# Speech Engine +engine = pyttsx3.init() + +def speak(text): + if settings.get("speech_enabled", True): + try: + engine.say(text) + engine.runAndWait() + except Exception as e: + logging.error(f"Speech engine error: {e}") + +# Initialize pygame for sound +try: + pygame.mixer.init() +except pygame.error as e: + logging.warning(f"Pygame mixer initialization failed: {e}") + +def play_intro_music(): + try: + music_path = os.path.join(os.path.dirname(__file__), "Snakey_intro.wav") + if not os.path.exists(music_path): + raise FileNotFoundError(f"No file '{music_path}' found.") + pygame.mixer.music.load(music_path) + pygame.mixer.music.play(-1) # Loop the intro music + except Exception as e: + logging.error(f"Failed to play intro music: {e}") + +def stop_intro_music(): + try: + pygame.mixer.music.stop() + except Exception as e: + logging.error(f"Failed to stop music: {e}") + +class SnakeyApp: + def __init__(self, root): + self.root = root + self.root.overrideredirect(True) + self.root.geometry("200x200+500+300") + self.root.attributes("-topmost", True) # Always on top + + try: + self.root.attributes("-transparentcolor", "white") + except tk.TclError: + logging.warning("Transparent color not supported on this system.") + + self.canvas = tk.Canvas(root, width=200, height=200, bg='white', highlightthickness=0) + self.canvas.pack(expand=True) + + self.snake_id = None + self.update_image(settings.get("image")) + + self.schedule_random_movement() + self.schedule_talk() + + self.canvas.bind("", self.start_drag) + self.canvas.bind("", self.drag) + + self.menu = tk.Menu(root, tearoff=0) + self.menu.add_command(label="Settings", command=self.open_settings) + self.menu.add_command(label="Surf the Web!", command=self.surf_web) + self.menu.add_command(label="Quit", command=root.destroy) + self.menu.add_command(label="Tell me a joke!", command=self.tell_joke) + self.menu.add_command(label="Talk!", command=self.open_tts_window) # TTS button + self.canvas.bind("", self.show_menu) + + self.greet_user() + + def schedule_random_movement(self): + # Move more often (every 5-15 seconds) + delay = random.randint(5000, 15000) + self.root.after(delay, self.random_move) + + def random_move(self): + # Move up to 1000 pixels away from current position + current_x = self.root.winfo_x() + current_y = self.root.winfo_y() + + move_x = random.randint(-1000, 1000) + move_y = random.randint(-1000, 1000) + + screen_width = self.root.winfo_screenwidth() + screen_height = self.root.winfo_screenheight() + + new_x = min(max(0, current_x + move_x), screen_width - 200) + new_y = min(max(0, current_y + move_y), screen_height - 200) + + self.glide_to_new_position(current_x, current_y, new_x, new_y) + + # Show the moving image for 5 seconds + self.update_image(settings.get("image_moving")) + + # Switch back to "not moving" image after 5 seconds + self.root.after(5000, self.update_image, settings.get("image")) + + self.schedule_random_movement() + + def glide_to_new_position(self, start_x, start_y, end_x, end_y, duration=5000, steps=100): + # Calculate the total number of steps based on the duration + step_delay = duration // steps + step_x = (end_x - start_x) / steps + step_y = (end_y - start_y) / steps + + def move_step(i): + if i < steps: + current_x = start_x + i * step_x + current_y = start_y + i * step_y + self.root.geometry(f"+{int(current_x)}+{int(current_y)}") + self.root.after(step_delay, move_step, i + 1) + + move_step(0) + + def schedule_talk(self): + # Talk every 10 seconds to 1 minute + delay = random.randint(10000, 60000) + self.root.after(delay, self.speak_random_phrase) + + def speak_random_phrase(self): + phrases = [ + "nice computer you got here! can i have it?", + "haha im digging into your files!", + "An SSD? let me see!", + "La la la la la", + "haha im a little snakey guy" + ] + phrase = random.choice(phrases) + speak(phrase) + self.schedule_talk() + + def start_drag(self, event): + self.start_x = event.x + self.start_y = event.y + + def drag(self, event): + x_offset = event.x - self.start_x + y_offset = event.y - self.start_y + new_x = self.root.winfo_x() + x_offset + new_y = self.root.winfo_y() + y_offset + self.root.geometry(f"+{new_x}+{new_y}") + + def show_menu(self, event): + self.menu.post(event.x_root, event.y_root) + + def greet_user(self): + if settings["PLAYED_BEFORE"] == "no": + speak("Thanks for waking me up! My name's Snakey, and how about yours?") + self.show_name_input() + play_intro_music() # Play intro music on first run + else: + name = settings["user_data"].get("name", "there") + greeting = f"Hello there, {name}. It's nice to see you!" + self.show_notification(greeting) + speak(greeting) + stop_intro_music() # Stop the intro music if user has played before + + def show_name_input(self): + name_input_window = tk.Toplevel(self.root) + name_input_window.title("BFDC - Best Friend Data Collector") + name_input_window.geometry("300x150") + + label = tk.Label(name_input_window, text="What's your name?") + label.pack(pady=10) + + name_entry = tk.Entry(name_input_window) + name_entry.pack(pady=5) + + def save_name(): + name = name_entry.get() + if name: + settings["user_data"]["name"] = name + settings["PLAYED_BEFORE"] = "yes" + save_settings(settings) + speak(f"Nice to meet you, {name}!") + name_input_window.after(2000, name_input_window.destroy) + stop_intro_music() # Stop the music once the name is entered + + tk.Button(name_input_window, text="Submit", command=save_name).pack(pady=10) + + def show_notification(self, text): + notif = tk.Toplevel(self.root) + notif.geometry("200x100+500+300") + tk.Label(notif, text=text, wraplength=180).pack(expand=True) + self.root.after(2000, notif.destroy) + + def open_settings(self): + settings_window = tk.Toplevel(self.root) + settings_window.title("Settings") + settings_window.geometry("300x200") + + def select_image(): + file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")]) + if file_path: + settings["image"] = file_path + save_settings(settings) + self.update_image(file_path) + + tk.Button(settings_window, text="Select Image When Not Moving", command=select_image).pack(pady=5) + + def select_moving_image(): + file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")]) + if file_path: + settings["image_moving"] = file_path + save_settings(settings) + + tk.Button(settings_window, text="Select Image When Moving", command=select_moving_image).pack(pady=5) + + def clear_data(): + response = messagebox.askyesno("Clear Data", "Are you sure you want to clear all collected data?") + if response: + save_settings(DEFAULT_SETTINGS) + self.root.quit() # Close the app + + tk.Button(settings_window, text="Clear Collected Data", command=clear_data).pack(pady=5) + + def update_image(self, image_path): + self.canvas.delete("all") + if image_path: + try: + self.image = tk.PhotoImage(file=image_path) + self.canvas.create_image(100, 100, image=self.image) + except Exception as e: + logging.error(f"Failed to load image: {e}") + else: + self.snake_id = self.canvas.create_oval(40, 40, 160, 160, fill="green") + + def surf_web(self): + webbrowser.open("https://www.google.com") + + def tell_joke(self): + jokes = [ + "Why don't skeletons fight each other? They don't have the guts.", + "Why can't your nose be 12 inches long? Because then it would be a foot!", + "I'm reading a book on anti-gravity. It's impossible to put down!" + ] + joke = random.choice(jokes) + speak(joke) + self.show_notification(joke) + + def open_tts_window(self): + tts_window = tk.Toplevel(self.root) + tts_window.title("Text-to-Speech") + + label = tk.Label(tts_window, text="Type something to say:") + label.pack(pady=10) + + text_entry = tk.Entry(tts_window) + text_entry.pack(pady=5) + + def speak_text(): + text = text_entry.get() + if text: + speak(text) + + tk.Button(tts_window, text="Speak", command=speak_text).pack(pady=10) + + +def run(): + root = tk.Tk() + app = SnakeyApp(root) + root.mainloop() + + +if __name__ == "__main__": + run()