Huge refactor of the libstormgames library. It is hopefully mostly backwards compatible. Still lots of testing to do, and probably some fixes needed, but this is a good start.
This commit is contained in:
174
display.py
Normal file
174
display.py
Normal file
@ -0,0 +1,174 @@
|
||||
#!/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
|
||||
path_service = 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()
|
||||
volume_service = 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()
|
||||
alt_pressed = mods & pygame.KMOD_ALT
|
||||
|
||||
# Volume controls (require Alt)
|
||||
if alt_pressed:
|
||||
if event.key == pygame.K_PAGEUP:
|
||||
volume_service.adjust_master_volume(0.1, pygame.mixer)
|
||||
elif event.key == pygame.K_PAGEDOWN:
|
||||
volume_service.adjust_master_volume(-0.1, pygame.mixer)
|
||||
elif event.key == pygame.K_HOME:
|
||||
volume_service.adjust_bgm_volume(0.1, pygame.mixer)
|
||||
elif event.key == pygame.K_END:
|
||||
volume_service.adjust_bgm_volume(-0.1, pygame.mixer)
|
||||
elif event.key == pygame.K_INSERT:
|
||||
volume_service.adjust_sfx_volume(0.1, pygame.mixer)
|
||||
elif event.key == pygame.K_DELETE:
|
||||
volume_service.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)
|
Reference in New Issue
Block a user