275 lines
10 KiB
Python
275 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Service classes for Storm Games.
|
|
|
|
Provides centralized services to replace global variables:
|
|
- ConfigService: Manages game configuration
|
|
- VolumeService: Handles volume settings
|
|
- PathService: Manages file paths
|
|
"""
|
|
|
|
import configparser
|
|
import os
|
|
from xdg import BaseDirectory
|
|
|
|
# For backward compatibility
|
|
from .config import gamePath, globalPath, write_config, read_config
|
|
|
|
class ConfigService:
|
|
"""Configuration management service."""
|
|
|
|
_instance = None
|
|
|
|
@classmethod
|
|
def get_instance(cls):
|
|
"""Get or create the singleton instance."""
|
|
if cls._instance is None:
|
|
cls._instance = ConfigService()
|
|
return cls._instance
|
|
|
|
def __init__(self):
|
|
"""Initialize configuration parsers."""
|
|
self.localConfig = configparser.ConfigParser()
|
|
self.globalConfig = configparser.ConfigParser()
|
|
self.gameTitle = None
|
|
self.pathService = None
|
|
|
|
def set_game_info(self, gameTitle, pathService):
|
|
"""Set game information and initialize configs.
|
|
|
|
Args:
|
|
gameTitle (str): Title of the game
|
|
pathService (PathService): Path service instance
|
|
"""
|
|
self.gameTitle = gameTitle
|
|
self.pathService = pathService
|
|
|
|
# Load existing configurations
|
|
self.read_local_config()
|
|
self.read_global_config()
|
|
|
|
def read_local_config(self):
|
|
"""Read local configuration from file."""
|
|
try:
|
|
# Try to use pathService if available
|
|
if self.pathService and self.pathService.gamePath:
|
|
with open(os.path.join(self.pathService.gamePath, "config.ini"), 'r') as configFile:
|
|
self.localConfig.read_file(configFile)
|
|
# Fallback to global gamePath
|
|
elif gamePath:
|
|
with open(os.path.join(gamePath, "config.ini"), 'r') as configFile:
|
|
self.localConfig.read_file(configFile)
|
|
# Delegate to old function as last resort
|
|
else:
|
|
read_config(False)
|
|
self.localConfig = configparser.ConfigParser()
|
|
self.localConfig.read_dict(globals().get('localConfig', {}))
|
|
except:
|
|
pass
|
|
|
|
def read_global_config(self):
|
|
"""Read global configuration from file."""
|
|
try:
|
|
# Try to use pathService if available
|
|
if self.pathService and self.pathService.globalPath:
|
|
with open(os.path.join(self.pathService.globalPath, "config.ini"), 'r') as configFile:
|
|
self.globalConfig.read_file(configFile)
|
|
# Fallback to global globalPath
|
|
elif globalPath:
|
|
with open(os.path.join(globalPath, "config.ini"), 'r') as configFile:
|
|
self.globalConfig.read_file(configFile)
|
|
# Delegate to old function as last resort
|
|
else:
|
|
read_config(True)
|
|
self.globalConfig = configparser.ConfigParser()
|
|
self.globalConfig.read_dict(globals().get('globalConfig', {}))
|
|
except:
|
|
pass
|
|
|
|
def write_local_config(self):
|
|
"""Write local configuration to file."""
|
|
try:
|
|
# Try to use pathService if available
|
|
if self.pathService and self.pathService.gamePath:
|
|
with open(os.path.join(self.pathService.gamePath, "config.ini"), 'w') as configFile:
|
|
self.localConfig.write(configFile)
|
|
# Fallback to global gamePath
|
|
elif gamePath:
|
|
with open(os.path.join(gamePath, "config.ini"), 'w') as configFile:
|
|
self.localConfig.write(configFile)
|
|
# Delegate to old function as last resort
|
|
else:
|
|
# Update old global config
|
|
globals()['localConfig'] = self.localConfig
|
|
write_config(False)
|
|
except Exception as e:
|
|
print(f"Warning: Failed to write local config: {e}")
|
|
|
|
def write_global_config(self):
|
|
"""Write global configuration to file."""
|
|
try:
|
|
# Try to use pathService if available
|
|
if self.pathService and self.pathService.globalPath:
|
|
with open(os.path.join(self.pathService.globalPath, "config.ini"), 'w') as configFile:
|
|
self.globalConfig.write(configFile)
|
|
# Fallback to global globalPath
|
|
elif globalPath:
|
|
with open(os.path.join(globalPath, "config.ini"), 'w') as configFile:
|
|
self.globalConfig.write(configFile)
|
|
# Delegate to old function as last resort
|
|
else:
|
|
# Update old global config
|
|
globals()['globalConfig'] = self.globalConfig
|
|
write_config(True)
|
|
except Exception as e:
|
|
print(f"Warning: Failed to write global config: {e}")
|
|
|
|
|
|
class VolumeService:
|
|
"""Volume management service."""
|
|
|
|
_instance = None
|
|
|
|
@classmethod
|
|
def get_instance(cls):
|
|
"""Get or create the singleton instance."""
|
|
if cls._instance is None:
|
|
cls._instance = VolumeService()
|
|
return cls._instance
|
|
|
|
def __init__(self):
|
|
"""Initialize volume settings."""
|
|
self.bgmVolume = 0.75 # Default background music volume
|
|
self.sfxVolume = 1.0 # Default sound effects volume
|
|
self.masterVolume = 1.0 # Default master volume
|
|
|
|
def adjust_master_volume(self, change, pygameMixer=None):
|
|
"""Adjust the master volume for all sounds.
|
|
|
|
Args:
|
|
change (float): Amount to change volume by (positive or negative)
|
|
pygameMixer: Optional pygame.mixer module for real-time updates
|
|
"""
|
|
self.masterVolume = max(0.0, min(1.0, self.masterVolume + change))
|
|
|
|
# Update real-time audio if pygame mixer is provided
|
|
if pygameMixer:
|
|
# Update music volume
|
|
if pygameMixer.music.get_busy():
|
|
pygameMixer.music.set_volume(self.bgmVolume * self.masterVolume)
|
|
|
|
# Update all sound channels
|
|
for i in range(pygameMixer.get_num_channels()):
|
|
channel = pygameMixer.Channel(i)
|
|
if channel.get_busy():
|
|
currentVolume = channel.get_volume()
|
|
if isinstance(currentVolume, (int, float)):
|
|
# Mono audio
|
|
channel.set_volume(currentVolume * self.masterVolume)
|
|
else:
|
|
# Stereo audio
|
|
left, right = currentVolume
|
|
channel.set_volume(left * self.masterVolume, right * self.masterVolume)
|
|
|
|
def adjust_bgm_volume(self, change, pygameMixer=None):
|
|
"""Adjust only the background music volume.
|
|
|
|
Args:
|
|
change (float): Amount to change volume by (positive or negative)
|
|
pygameMixer: Optional pygame.mixer module for real-time updates
|
|
"""
|
|
self.bgmVolume = max(0.0, min(1.0, self.bgmVolume + change))
|
|
|
|
# Update real-time audio if pygame mixer is provided
|
|
if pygameMixer and pygameMixer.music.get_busy():
|
|
pygameMixer.music.set_volume(self.bgmVolume * self.masterVolume)
|
|
|
|
def adjust_sfx_volume(self, change, pygameMixer=None):
|
|
"""Adjust volume for sound effects only.
|
|
|
|
Args:
|
|
change (float): Amount to change volume by (positive or negative)
|
|
pygameMixer: Optional pygame.mixer module for real-time updates
|
|
"""
|
|
self.sfxVolume = max(0.0, min(1.0, self.sfxVolume + change))
|
|
|
|
# Update real-time audio if pygame mixer is provided
|
|
if pygameMixer:
|
|
# Update all sound channels except reserved ones
|
|
for i in range(pygameMixer.get_num_channels()):
|
|
channel = pygameMixer.Channel(i)
|
|
if channel.get_busy():
|
|
currentVolume = channel.get_volume()
|
|
if isinstance(currentVolume, (int, float)):
|
|
# Mono audio
|
|
channel.set_volume(currentVolume * self.sfxVolume * self.masterVolume)
|
|
else:
|
|
# Stereo audio
|
|
left, right = currentVolume
|
|
channel.set_volume(left * self.sfxVolume * self.masterVolume,
|
|
right * self.sfxVolume * self.masterVolume)
|
|
|
|
def get_bgm_volume(self):
|
|
"""Get the current BGM volume with master adjustment.
|
|
|
|
Returns:
|
|
float: Current adjusted BGM volume
|
|
"""
|
|
return self.bgmVolume * self.masterVolume
|
|
|
|
def get_sfx_volume(self):
|
|
"""Get the current SFX volume with master adjustment.
|
|
|
|
Returns:
|
|
float: Current adjusted SFX volume
|
|
"""
|
|
return self.sfxVolume * self.masterVolume
|
|
|
|
|
|
class PathService:
|
|
"""Path management service."""
|
|
|
|
_instance = None
|
|
|
|
@classmethod
|
|
def get_instance(cls):
|
|
"""Get or create the singleton instance."""
|
|
if cls._instance is None:
|
|
cls._instance = PathService()
|
|
return cls._instance
|
|
|
|
def __init__(self):
|
|
"""Initialize path variables."""
|
|
self.globalPath = None
|
|
self.gamePath = None
|
|
self.gameName = None
|
|
|
|
# Try to initialize from global variables for backward compatibility
|
|
global gamePath, globalPath
|
|
if gamePath:
|
|
self.gamePath = gamePath
|
|
if globalPath:
|
|
self.globalPath = globalPath
|
|
|
|
def initialize(self, gameTitle):
|
|
"""Initialize paths for a game.
|
|
|
|
Args:
|
|
gameTitle (str): Title of the game
|
|
"""
|
|
self.gameName = gameTitle
|
|
self.globalPath = os.path.join(BaseDirectory.xdg_config_home, "storm-games")
|
|
self.gamePath = os.path.join(self.globalPath,
|
|
str.lower(str.replace(gameTitle, " ", "-")))
|
|
|
|
# Create game directory if it doesn't exist
|
|
if not os.path.exists(self.gamePath):
|
|
os.makedirs(self.gamePath)
|
|
|
|
# Update global variables for backward compatibility
|
|
global gamePath, globalPath
|
|
gamePath = self.gamePath
|
|
globalPath = self.globalPath
|
|
|
|
return self
|