Source code for unipress.core.settings

"""
Settings management for Unipress games.

Handles hierarchical settings loading with priority:
1. Constructor parameters (highest)
2. Game-specific settings
3. Global settings
4. Default values (lowest)
"""

from pathlib import Path
from typing import Any

import tomli


[docs] def get_default_settings() -> dict[str, Any]: """Get hardcoded default settings.""" return { "game": { "difficulty": 5, "lives": 3, "fullscreen": True, }, "ui": { "blink_duration": 1.0, "language": "pl_PL", }, }
[docs] def load_toml_file(file_path: str | Path) -> dict[str, Any]: """Load TOML file safely, return empty dict if file doesn't exist.""" try: with open(file_path, "rb") as f: return tomli.load(f) except (FileNotFoundError, tomli.TOMLDecodeError): return {}
[docs] def merge_settings(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]: """Merge two settings dictionaries recursively.""" result = base.copy() for key, value in override.items(): if key in result and isinstance(result[key], dict) and isinstance(value, dict): result[key] = merge_settings(result[key], value) else: result[key] = value return result
[docs] def load_settings(game_name: str, **constructor_overrides) -> dict[str, Any]: """ Load settings with hierarchical priority. Args: game_name: Name of the game (e.g., "demo_jump") **constructor_overrides: Settings passed to BaseGame constructor Returns: Merged settings dictionary """ # 1. Start with defaults settings = get_default_settings() # 2. Load global settings global_settings_path = Path("unipress") / "settings.toml" global_settings = load_toml_file(global_settings_path) if global_settings: settings = merge_settings(settings, global_settings) # 3. Load game-specific settings game_settings_path = Path("unipress") / "games" / game_name / "settings.toml" game_settings = load_toml_file(game_settings_path) if game_settings: settings = merge_settings(settings, game_settings) # 4. Apply constructor overrides if constructor_overrides: # Convert flat constructor params to nested structure game_overrides = {} for key, value in constructor_overrides.items(): if value is not None: if key in ["difficulty", "lives", "fullscreen"]: if "game" not in game_overrides: game_overrides["game"] = {} game_overrides["game"][key] = value elif key in ["blink_duration", "language"]: if "ui" not in game_overrides: game_overrides["ui"] = {} game_overrides["ui"][key] = value if game_overrides: settings = merge_settings(settings, game_overrides) return settings
[docs] def get_setting(settings: dict[str, Any], key_path: str, default: Any = None) -> Any: """ Get setting value using dot notation. Args: settings: Settings dictionary key_path: Dot-separated path (e.g., "game.difficulty") default: Default value if key not found Returns: Setting value or default """ keys = key_path.split(".") value = settings for key in keys: if isinstance(value, dict) and key in value: value = value[key] else: return default return value