82 lines
2.1 KiB
TypeScript
82 lines
2.1 KiB
TypeScript
import type { AchievementDef } from '../types'
|
|
|
|
type UnlockCallback = (achievement: AchievementDef) => void
|
|
|
|
export class AchievementSystem {
|
|
private definitions: AchievementDef[] = []
|
|
private unlockedIds: Set<string> = new Set()
|
|
private onUnlock: UnlockCallback | null = null
|
|
private toastQueue: string[] = []
|
|
private toastActive = false
|
|
|
|
init(defs: AchievementDef[], alreadyUnlocked: string[]) {
|
|
this.definitions = defs
|
|
this.unlockedIds = new Set(alreadyUnlocked)
|
|
this.toastQueue = []
|
|
this.toastActive = false
|
|
}
|
|
|
|
setUnlockCallback(cb: UnlockCallback) {
|
|
this.onUnlock = cb
|
|
}
|
|
|
|
check(variables: Record<string, number>) {
|
|
for (const def of this.definitions) {
|
|
if (this.unlockedIds.has(def.id)) continue
|
|
|
|
const cond = def.condition
|
|
const val = variables[cond.variable] ?? 0
|
|
let matched = false
|
|
|
|
switch (cond.op) {
|
|
case '==': matched = val === cond.value; break
|
|
case '!=': matched = val !== cond.value; break
|
|
case '>': matched = val > (cond.value as number); break
|
|
case '<': matched = val < (cond.value as number); break
|
|
case '>=': matched = val >= (cond.value as number); break
|
|
case '<=': matched = val <= (cond.value as number); break
|
|
}
|
|
|
|
if (matched) {
|
|
this.unlockedIds.add(def.id)
|
|
this.onUnlock?.(def)
|
|
this.enqueueToast(def.id)
|
|
}
|
|
}
|
|
}
|
|
|
|
private enqueueToast(id: string) {
|
|
this.toastQueue.push(id)
|
|
if (!this.toastActive) {
|
|
this.showNextToast()
|
|
}
|
|
}
|
|
|
|
private showNextToast() {
|
|
if (this.toastQueue.length === 0) {
|
|
this.toastActive = false
|
|
return
|
|
}
|
|
this.toastActive = true
|
|
// toast is shown by the UI layer watching toastQueue
|
|
// UI calls toastDismissed() when the toast animation finishes
|
|
}
|
|
|
|
getToastQueue(): string[] {
|
|
return [...this.toastQueue]
|
|
}
|
|
|
|
toastDismissed(id: string) {
|
|
this.toastQueue = this.toastQueue.filter((i) => i !== id)
|
|
this.showNextToast()
|
|
}
|
|
|
|
getUnlockedIds(): string[] {
|
|
return [...this.unlockedIds]
|
|
}
|
|
|
|
getDefinitions(): AchievementDef[] {
|
|
return this.definitions
|
|
}
|
|
}
|