feat: achievement system with 10 unlockable achievements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
72
store/achievements.go
Normal file
72
store/achievements.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
var bucketAchievements = []byte("achievements")
|
||||
|
||||
type Achievement struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Unlocked bool `json:"unlocked"`
|
||||
}
|
||||
|
||||
var AchievementDefs = []Achievement{
|
||||
{ID: "first_clear", Name: "Dungeon Delver", Description: "Clear floor 5 for the first time"},
|
||||
{ID: "boss_slayer", Name: "Boss Slayer", Description: "Defeat any boss"},
|
||||
{ID: "floor10", Name: "Deep Explorer", Description: "Reach floor 10"},
|
||||
{ID: "floor20", Name: "Conqueror", Description: "Conquer the Catacombs (floor 20)"},
|
||||
{ID: "solo_clear", Name: "Lone Wolf", Description: "Clear floor 5 solo"},
|
||||
{ID: "gold_hoarder", Name: "Gold Hoarder", Description: "Accumulate 200+ gold in one run"},
|
||||
{ID: "no_death", Name: "Untouchable", Description: "Complete a floor without anyone dying"},
|
||||
{ID: "full_party", Name: "Fellowship", Description: "Start a game with 4 players"},
|
||||
{ID: "relic_collector", Name: "Relic Collector", Description: "Collect 3+ relics in one run"},
|
||||
{ID: "flee_master", Name: "Tactical Retreat", Description: "Successfully flee from combat"},
|
||||
}
|
||||
|
||||
func (d *DB) initAchievements() error {
|
||||
return d.db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists(bucketAchievements)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DB) UnlockAchievement(player, achievementID string) (bool, error) {
|
||||
key := []byte(player + ":" + achievementID)
|
||||
alreadyUnlocked := false
|
||||
err := d.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(bucketAchievements)
|
||||
if b.Get(key) != nil {
|
||||
alreadyUnlocked = true
|
||||
return nil
|
||||
}
|
||||
return b.Put(key, []byte("1"))
|
||||
})
|
||||
// Returns true if newly unlocked (not already had it)
|
||||
return !alreadyUnlocked, err
|
||||
}
|
||||
|
||||
func (d *DB) GetAchievements(player string) ([]Achievement, error) {
|
||||
unlocked := make(map[string]bool)
|
||||
err := d.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(bucketAchievements)
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
for _, a := range AchievementDefs {
|
||||
key := []byte(player + ":" + a.ID)
|
||||
if b.Get(key) != nil {
|
||||
unlocked[a.ID] = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
result := make([]Achievement, len(AchievementDefs))
|
||||
for i, a := range AchievementDefs {
|
||||
result[i] = a
|
||||
result[i].Unlocked = unlocked[a.ID]
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
Reference in New Issue
Block a user