package save import ( "fmt" "os" "path/filepath" "time" "github.com/BurntSushi/toml" ) const ( dataFile = "data.toml" ) // Save file structure type Data struct { Settings Settings `toml:"settings"` GameState GameState `toml:"game_state"` } type Settings struct { FPSMonitor bool `toml:"fps_monitor"` FPSCap string `toml:"fps_cap"` PortalVisibility bool `toml:"portal_visibility"` } type GameState struct { HasSave bool `toml:"has_save"` SavedAt time.Time `toml:"saved_at"` HeroX float64 `toml:"hero_x"` HeroY float64 `toml:"hero_y"` HeroStamina float64 `toml:"hero_stamina"` PlayTimeMS int64 `toml:"play_time_ms"` } // Save manager type Manager struct { dataPath string } func NewManager() (*Manager, error) { exePath, err := os.Executable() if err != nil { return nil, fmt.Errorf("failed to get executable path: %w", err) } exeDir := filepath.Dir(exePath) return &Manager{ dataPath: filepath.Join(exeDir, dataFile), }, nil } // Data operations func (m *Manager) LoadData() (*Data, error) { if _, err := os.Stat(m.dataPath); os.IsNotExist(err) { return &Data{ Settings: Settings{ FPSMonitor: false, FPSCap: "60", PortalVisibility: false, }, GameState: GameState{ HasSave: false, }, }, nil } var data Data if _, err := toml.DecodeFile(m.dataPath, &data); err != nil { return nil, fmt.Errorf("failed to parse data file: %w", err) } return &data, nil } func (m *Manager) SaveData(data *Data) error { file, err := os.Create(m.dataPath) if err != nil { return fmt.Errorf("failed to create data file: %w", err) } defer file.Close() encoder := toml.NewEncoder(file) if err := encoder.Encode(data); err != nil { return fmt.Errorf("failed to write data file: %w", err) } return nil } // Settings operations func (m *Manager) LoadSettings() (*Settings, error) { data, err := m.LoadData() if err != nil { return nil, err } return &data.Settings, nil } func (m *Manager) SaveSettings(settings *Settings) error { data, err := m.LoadData() if err != nil { data = &Data{ Settings: *settings, GameState: GameState{ HasSave: false, }, } } else { data.Settings = *settings } return m.SaveData(data) } // Game state operations func (m *Manager) LoadGameState() (*GameState, error) { data, err := m.LoadData() if err != nil { return nil, err } if !data.GameState.HasSave { return nil, nil } return &data.GameState, nil } func (m *Manager) SaveGameState(state *GameState) error { data, err := m.LoadData() if err != nil { data = &Data{ Settings: Settings{ FPSMonitor: false, FPSCap: "60", PortalVisibility: false, }, GameState: *state, } } else { data.GameState = *state } data.GameState.HasSave = true data.GameState.SavedAt = time.Now() return m.SaveData(data) } func (m *Manager) HasSavedGame() bool { data, err := m.LoadData() if err != nil { return false } return data.GameState.HasSave } func (m *Manager) DeleteGameState() error { data, err := m.LoadData() if err != nil { return err } data.GameState = GameState{ HasSave: false, } return m.SaveData(data) } // Utilities func (m *Manager) GetSaveFilePath() string { return m.dataPath }