104 lines
3.8 KiB
Go
104 lines
3.8 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"os"
|
||
"path/filepath"
|
||
)
|
||
|
||
// ПРИМЕЧАНИЕ: сохранение реализовано только для этой игры — она
|
||
// единственная в коллекции без чёткого конца партии, поэтому жалко
|
||
// сбрасывать прогресс при каждом выходе в меню. Остальные 8 игр
|
||
// коллекции по-прежнему не сохраняются между запусками — обычно в
|
||
// них и не нужно (партия проходится за один присест).
|
||
|
||
// ErrCorpseStarchNoSave возвращается LoadCorpseStarchGame, если
|
||
// файл сохранения не найден (новая партия должна начаться с нуля).
|
||
var ErrCorpseStarchNoSave = errors.New("файл сохранения не найден")
|
||
|
||
// corpseStarchSavePath возвращает путь к файлу сохранения в
|
||
// пользовательской конфигурационной директории (например,
|
||
// ~/.config/go-games-collection/corpsestarch_save.json на Linux).
|
||
func corpseStarchSavePath() (string, error) {
|
||
dir, err := os.UserConfigDir()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return filepath.Join(dir, "go-games-collection", "corpsestarch_save.json"), nil
|
||
}
|
||
|
||
// SaveCorpseStarchGame сохраняет текущее состояние партии на диск,
|
||
// создавая директорию сохранения при необходимости.
|
||
func SaveCorpseStarchGame(g *CorpseStarchGameState) error {
|
||
path, err := corpseStarchSavePath()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||
return err
|
||
}
|
||
data, err := json.MarshalIndent(g, "", " ")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return os.WriteFile(path, data, 0o644)
|
||
}
|
||
|
||
// LoadCorpseStarchGame загружает сохранённую партию с диска.
|
||
// Возвращает ErrCorpseStarchNoSave, если файла сохранения нет —
|
||
// вызывающий код должен в этом случае начать новую партию.
|
||
func LoadCorpseStarchGame() (*CorpseStarchGameState, error) {
|
||
path, err := corpseStarchSavePath()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
data, err := os.ReadFile(path)
|
||
if err != nil {
|
||
if os.IsNotExist(err) {
|
||
return nil, ErrCorpseStarchNoSave
|
||
}
|
||
return nil, err
|
||
}
|
||
var g CorpseStarchGameState
|
||
if err := json.Unmarshal(data, &g); err != nil {
|
||
return nil, err
|
||
}
|
||
if len(g.ServitorCounts) < len(corpseStarchServitorDefs) {
|
||
grown := make([]int, len(corpseStarchServitorDefs))
|
||
copy(grown, g.ServitorCounts)
|
||
g.ServitorCounts = grown
|
||
}
|
||
if len(g.ServitorPurchases) < len(corpseStarchServitorDefs) {
|
||
grown := make([]int, len(corpseStarchServitorDefs))
|
||
copy(grown, g.ServitorPurchases)
|
||
// у старых сохранений без этого поля используем текущие
|
||
// счётчики как разумное начальное приближение — иначе цена
|
||
// следующей покупки внезапно откатилась бы к базовой
|
||
for i := range grown {
|
||
if grown[i] < g.ServitorCounts[i] {
|
||
grown[i] = g.ServitorCounts[i]
|
||
}
|
||
}
|
||
g.ServitorPurchases = grown
|
||
}
|
||
if g.BonusMultiplier == 0 {
|
||
g.BonusMultiplier = 1
|
||
}
|
||
return &g, nil
|
||
}
|
||
|
||
// DeleteCorpseStarchSave удаляет файл сохранения, если он есть
|
||
// (используется для явного сброса прогресса). Отсутствие файла не
|
||
// считается ошибкой.
|
||
func DeleteCorpseStarchSave() error {
|
||
path, err := corpseStarchSavePath()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
err = os.Remove(path)
|
||
if err != nil && !os.IsNotExist(err) {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|