163 lines
3.4 KiB
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{}
|
|
}
|