Files
termdoku/internal/achievements/achievements.go

163 lines
3.4 KiB
Go

package achievements
import (
"encoding/json"
"errors"
"io/fs"
"os"
"path/filepath"
)
type Achievement struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Icon string `json:"icon"`
Unlocked bool `json:"unlocked"`
Progress int `json:"progress"`
Target int `json:"target"`
}
type Manager struct {
Achievements map[string]*Achievement `json:"achievements"`
NewUnlocks []string `json:"-"`
}
func New() *Manager {
return &Manager{
Achievements: map[string]*Achievement{
"first_win": {
ID: "first_win",
Name: "First Victory",
Description: "Complete your first puzzle",
Icon: "🏆",
Target: 1,
},
"speed_demon": {
ID: "speed_demon",
Name: "Speed Demon",
Description: "Complete an Easy puzzle in under 3 minutes",
Icon: "⚡",
Target: 1,
},
"perfectionist": {
ID: "perfectionist",
Name: "Perfectionist",
Description: "Complete a puzzle without using hints",
Icon: "💎",
Target: 1,
},
"streak_master": {
ID: "streak_master",
Name: "Streak Master",
Description: "Achieve a 5-day streak",
Icon: "🔥",
Target: 5,
},
"century": {
ID: "century",
Name: "Century Club",
Description: "Complete 100 puzzles",
Icon: "💯",
Target: 100,
},
"lunatic_legend": {
ID: "lunatic_legend",
Name: "Lunatic Legend",
Description: "Complete 10 Lunatic puzzles",
Icon: "🌙",
Target: 10,
},
"daily_devotee": {
ID: "daily_devotee",
Name: "Daily Devotee",
Description: "Complete 30 daily puzzles",
Icon: "📅",
Target: 30,
},
"no_mistakes": {
ID: "no_mistakes",
Name: "Flawless",
Description: "Complete a Hard puzzle without auto-check",
Icon: "✨",
Target: 1,
},
},
NewUnlocks: []string{},
}
}
func path() (string, error) {
h, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(h, ".termdoku", "achievements.json"), nil
}
func Load() (*Manager, error) {
m := New()
p, err := path()
if err != nil {
return m, err
}
b, err := os.ReadFile(p)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return m, nil
}
return m, err
}
if err := json.Unmarshal(b, m); err != nil {
return m, err
}
return m, nil
}
func Save(m *Manager) error {
p, err := path()
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
return err
}
data, err := json.MarshalIndent(m, "", " ")
if err != nil {
return err
}
return os.WriteFile(p, data, 0o644)
}
func (m *Manager) CheckAndUnlock(id string, progress int) bool {
if ach, ok := m.Achievements[id]; ok {
if !ach.Unlocked {
ach.Progress = progress
if ach.Progress >= ach.Target {
ach.Unlocked = true
m.NewUnlocks = append(m.NewUnlocks, id)
return true
}
}
}
return false
}
func (m *Manager) GetUnlockedCount() int {
count := 0
for _, ach := range m.Achievements {
if ach.Unlocked {
count++
}
}
return count
}
func (m *Manager) GetTotalCount() int {
return len(m.Achievements)
}
func (m *Manager) ClearNewUnlocks() {
m.NewUnlocks = []string{}
}