From 8a0224984572b8c167d5298a35823b6b2daaabe5 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 28 Oct 2025 06:35:14 +0100 Subject: [PATCH] feat: implement persistant configuration --- .gitignore | 1 + game/config.go | 21 ++++++++++++++++ game/config/config.go | 45 ++++++++++++++++++++++++++++++++++ game/state/handlers/options.go | 5 ++++ tetris.go | 10 ++++++++ 5 files changed, 82 insertions(+) create mode 100644 game/config.go create mode 100644 game/config/config.go diff --git a/.gitignore b/.gitignore index e0cc394..d46f6d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /tetris /tetris.exe +/config.json diff --git a/game/config.go b/game/config.go new file mode 100644 index 0000000..710fb0c --- /dev/null +++ b/game/config.go @@ -0,0 +1,21 @@ +package game + +import ( + "io" + + "tetris/game/config" +) + +// Global config variable with default values. +var Config = &config.Config{ + SoundVolume: 255, +} + +func LoadConfig(configPath string) error { + cfg, err := config.LoadFromFile(configPath) + if err != nil && err != io.EOF { + return err + } + Config = cfg + return nil +} diff --git a/game/config/config.go b/game/config/config.go new file mode 100644 index 0000000..82ad3d8 --- /dev/null +++ b/game/config/config.go @@ -0,0 +1,45 @@ +package config + +import ( + "encoding/json" + "io" + "os" +) + +type Config struct { + fd *os.File + SoundVolume byte `json:"sound_volume"` +} + +func DefaultConfig(fd *os.File) *Config { + cfg := Config{ + SoundVolume: 255, + } + cfg.fd = fd + return &cfg +} + +func LoadFromFile(filename string) (*Config, error) { + fd, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0o644) + if err != nil { + return nil, err + } + + cfg := DefaultConfig(fd) + err = json.NewDecoder(fd).Decode(cfg) + return cfg, err +} + +func (c Config) Save() error { + if err := c.fd.Truncate(0); err != nil { + return err + } + if _, err := c.fd.Seek(0, io.SeekStart); err != nil { + return err + } + return json.NewEncoder(c.fd).Encode(c) +} + +func (c Config) Close() error { + return c.fd.Close() +} diff --git a/game/state/handlers/options.go b/game/state/handlers/options.go index 7627964..8fabd6d 100644 --- a/game/state/handlers/options.go +++ b/game/state/handlers/options.go @@ -5,6 +5,7 @@ import ( "tetris/engine/audio" "tetris/engine/core" "tetris/engine/render" + "tetris/game" "tetris/game/state" "tetris/game/ui" "tetris/game/ui/layouts" @@ -24,6 +25,10 @@ func soundVolumeChanged(widget *widgets.Slider) { audio.SetVolume(value) audio.Play(assets.SFX_MENU_SOUND_VOLUME_SELECT) + + // Store in config and save + game.Config.SoundVolume = byte(widget.Value) + game.Config.Save() } func NewOptionsMenu() *OptionsMenu { diff --git a/tetris.go b/tetris.go index 40ae3eb..c5dfcbf 100644 --- a/tetris.go +++ b/tetris.go @@ -1,10 +1,14 @@ package main import ( + "fmt" + "tetris/assets" "tetris/engine/audio" + "tetris/engine/core" "tetris/engine/graphics" "tetris/engine/render" + "tetris/game" "tetris/game/state/handlers" "tetris/game/state/machine" @@ -12,6 +16,10 @@ import ( ) func main() { + if err := game.LoadConfig("./config.json"); err != nil { + fmt.Println("Failed to load config:", err) + } + render.Init(render.Config{ Title: "Tetris", WindowWidth: 685, @@ -34,6 +42,8 @@ func main() { audio.Init() defer audio.Exit() + // Set volume from config + audio.SetVolume(core.ByteToClampedFloat32(game.Config.SoundVolume)) audio.LoadLibrary(assets.LoadSound()) // Load texture