Files
bd2/go/internal/server/missions/achievement_claim_state.go
T
Flechazo d04791dd31 feat(server): add colored logs, level rewards, hunting dispatch and fiend hunt
Persist claims, AP, teams and presets with session receipts and atomic request rollback. Read rewards and growth from GameData; replace fixed Fiend Hunt user state and preserve the account initialization contract.
2026-10-04 21:27:19 +08:00

53 lines
1.5 KiB
Go

package missions
import (
"bd2server/internal/server/gamedata"
"fmt"
"math"
"strings"
)
// ClaimedAchievementIDs exposes committed claims to the counter query adapter.
// The mission domain remains the sole owner of reward claims.
func (s *Service) ClaimedAchievementIDs() map[gamedata.AchievementKey]bool {
s.mu.Lock()
defer s.mu.Unlock()
out := map[gamedata.AchievementKey]bool{}
for key := range s.design.Achievements {
if contains(s.state.Claimed, "achievement:"+achievementName(key)) {
out[key] = true
}
}
return out
}
// AchievementExperience derives exact earned experience from durable claimed
// identities and versioned table rewards. It does not manufacture levels.
func (s *Service) AchievementExperience() (uint64, error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.achievementExperienceLocked()
}
func (s *Service) achievementExperienceLocked() (uint64, error) {
var total uint64
known := map[string]bool{}
for key := range s.design.Achievements {
known["achievement:"+achievementName(key)] = true
}
for _, identity := range s.state.Claimed {
if strings.HasPrefix(identity, "achievement:") && !known[identity] {
return 0, fmt.Errorf("missions: incompatible claimed achievement %q missing from current GameData", identity)
}
}
for key, d := range s.design.Achievements {
if !contains(s.state.Claimed, "achievement:"+achievementName(key)) {
continue
}
if total > math.MaxUint64-d.AddExp {
return 0, fmt.Errorf("missions: achievement experience overflow")
}
total += d.AddExp
}
return total, nil
}