#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Display functionality for Storm Games. Provides functionality for: - GUI initialization - Text display with navigation - Message boxes """ import pygame import time import os import pyperclip import random from xdg import BaseDirectory from setproctitle import setproctitle from .speech import Speech from .services import PathService, VolumeService # Keep track of the instructions for navigating display_text has been shown displayTextUsageInstructions = False def initialize_gui(gameTitle): """Initialize the game GUI and sound system. Args: gameTitle (str): Title of the game Returns: dict: Dictionary of loaded sound objects """ # Initialize path service with game title pathService = PathService.get_instance().initialize(gameTitle) # Seed the random generator to the clock random.seed() # Set game's name setproctitle(str.lower(str.replace(gameTitle, " ", "-"))) # Initialize pygame pygame.init() pygame.display.set_mode((800, 600)) pygame.display.set_caption(gameTitle) # Set up audio system pygame.mixer.pre_init(44100, -16, 2, 1024) pygame.mixer.init() pygame.mixer.set_num_channels(32) pygame.mixer.set_reserved(0) # Reserve channel for cut scenes # Enable key repeat for volume controls pygame.key.set_repeat(500, 100) # Load sound files try: from os import listdir from os.path import isfile, join soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg", "wav"])] except Exception as e: print("No sounds found.") Speech.get_instance().speak("No sounds found.", False) soundFiles = [] # Create dictionary of sound objects soundData = {} for f in soundFiles: soundData[f.split('.')[0]] = pygame.mixer.Sound("sounds/" + f) # Play intro sound if available from .sound import cut_scene if 'game-intro' in soundData: cut_scene(soundData, 'game-intro') return soundData def display_text(text): """Display and speak text with navigation controls. Allows users to: - Navigate text line by line with arrow keys (skipping blank lines) - Listen to full text with space - Copy current line or full text (preserving blank lines) - Exit with enter/escape - Volume controls (with Alt modifier): - Alt+PageUp/PageDown: Master volume up/down - Alt+Home/End: Background music volume up/down - Alt+Insert/Delete: Sound effects volume up/down Args: text (list): List of text lines to display """ # Get service instances speech = Speech.get_instance() volumeService = VolumeService.get_instance() # Store original text with blank lines for copying originalText = text.copy() # Create navigation text by filtering out blank lines navText = [line for line in text if line.strip()] # Add instructions at the start on the first display global displayTextUsageInstructions if not displayTextUsageInstructions: instructions = ("Press space to read the whole text. Use up and down arrows to navigate " "the text line by line. Press c to copy the current line to the clipboard " "or t to copy the entire text. Press enter or escape when you are done reading.") navText.insert(0, instructions) displayTextUsageInstructions = True # Add end marker navText.append("End of text.") currentIndex = 0 speech.speak(navText[currentIndex]) while True: event = pygame.event.wait() if event.type == pygame.KEYDOWN: # Check for Alt modifier mods = pygame.key.get_mods() altPressed = mods & pygame.KMOD_ALT # Volume controls (require Alt) if altPressed: if event.key == pygame.K_PAGEUP: volumeService.adjust_master_volume(0.1, pygame.mixer) elif event.key == pygame.K_PAGEDOWN: volumeService.adjust_master_volume(-0.1, pygame.mixer) elif event.key == pygame.K_HOME: volumeService.adjust_bgm_volume(0.1, pygame.mixer) elif event.key == pygame.K_END: volumeService.adjust_bgm_volume(-0.1, pygame.mixer) elif event.key == pygame.K_INSERT: volumeService.adjust_sfx_volume(0.1, pygame.mixer) elif event.key == pygame.K_DELETE: volumeService.adjust_sfx_volume(-0.1, pygame.mixer) else: if event.key in (pygame.K_ESCAPE, pygame.K_RETURN): return if event.key in [pygame.K_DOWN, pygame.K_s] and currentIndex < len(navText) - 1: currentIndex += 1 speech.speak(navText[currentIndex]) if event.key in [pygame.K_UP, pygame.K_w] and currentIndex > 0: currentIndex -= 1 speech.speak(navText[currentIndex]) if event.key == pygame.K_SPACE: # Join with newlines to preserve spacing in speech speech.speak('\n'.join(originalText[1:-1])) if event.key == pygame.K_c: try: pyperclip.copy(navText[currentIndex]) speech.speak("Copied " + navText[currentIndex] + " to the clipboard.") except: speech.speak("Failed to copy the text to the clipboard.") if event.key == pygame.K_t: try: # Join with newlines to preserve blank lines in full text pyperclip.copy(''.join(originalText[2:-1])) speech.speak("Copied entire message to the clipboard.") except: speech.speak("Failed to copy the text to the clipboard.") event = pygame.event.clear() time.sleep(0.001)