69 lines
2.3 KiB
TypeScript
69 lines
2.3 KiB
TypeScript
export type PvpContentType = 'dungeon' | 'raid'
|
|
export type CpuDifficulty = 1 | 2 | 3 | 4 | 5
|
|
|
|
export type CpuPvpLeaderboardEntry = {
|
|
characterName: string
|
|
className: string
|
|
contentType: PvpContentType
|
|
encountersCleared: number
|
|
cpuDifficulty: CpuDifficulty
|
|
result: 'victory' | 'defeat'
|
|
completedAt: string
|
|
}
|
|
|
|
const cpuLeaderboardKey = 'chronicle.pvpCpuLeaderboard.v1'
|
|
const checkpointKey = 'chronicle.pvpRoguelikeCheckpoint.v1'
|
|
|
|
export function randomCpuDifficulty(): CpuDifficulty {
|
|
return (Math.floor(Math.random() * 5) + 1) as CpuDifficulty
|
|
}
|
|
|
|
export function loadCpuPvpLeaderboard(contentType?: PvpContentType): CpuPvpLeaderboardEntry[] {
|
|
try {
|
|
const raw = JSON.parse(localStorage.getItem(cpuLeaderboardKey) ?? '[]') as CpuPvpLeaderboardEntry[]
|
|
return raw
|
|
.filter((entry) => !contentType || entry.contentType === contentType)
|
|
.sort((left, right) =>
|
|
right.encountersCleared - left.encountersCleared
|
|
|| right.cpuDifficulty - left.cpuDifficulty
|
|
|| right.completedAt.localeCompare(left.completedAt),
|
|
)
|
|
.slice(0, 12)
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
export function recordCpuPvpLeaderboard(entry: CpuPvpLeaderboardEntry) {
|
|
const current = loadCpuPvpLeaderboard()
|
|
const next = [...current, entry]
|
|
.sort((left, right) =>
|
|
right.encountersCleared - left.encountersCleared
|
|
|| right.cpuDifficulty - left.cpuDifficulty
|
|
|| right.completedAt.localeCompare(left.completedAt),
|
|
)
|
|
.slice(0, 30)
|
|
localStorage.setItem(cpuLeaderboardKey, JSON.stringify(next))
|
|
}
|
|
|
|
function checkpointStorageKey(characterId: number, contentType: PvpContentType) {
|
|
return `${checkpointKey}:${characterId}:${contentType}`
|
|
}
|
|
|
|
export function loadPvpRoguelikeCheckpoint(characterId: number, contentType: PvpContentType) {
|
|
const value = Number(localStorage.getItem(checkpointStorageKey(characterId, contentType)) ?? 1)
|
|
return Number.isInteger(value) && value >= 5 ? value : 1
|
|
}
|
|
|
|
export function recordPvpRoguelikeCheckpoint(
|
|
characterId: number,
|
|
contentType: PvpContentType,
|
|
stage: number,
|
|
) {
|
|
if (stage < 5 || stage % 5 !== 0) return loadPvpRoguelikeCheckpoint(characterId, contentType)
|
|
const current = loadPvpRoguelikeCheckpoint(characterId, contentType)
|
|
const next = Math.max(current, stage)
|
|
localStorage.setItem(checkpointStorageKey(characterId, contentType), String(next))
|
|
return next
|
|
}
|