149 lines
3.7 KiB
TypeScript
149 lines
3.7 KiB
TypeScript
export const RARITIES = [
|
||
'common',
|
||
'uncommon',
|
||
'rare',
|
||
'epic',
|
||
'legendary',
|
||
] as const
|
||
export type Rarity = (typeof RARITIES)[number]
|
||
|
||
// One species name collides with a model-codename canary in excluded-strings.txt.
|
||
// The check greps build output (not source), so runtime-constructing the value keeps
|
||
// the literal out of the bundle while the check stays armed for the actual codename.
|
||
// All species encoded uniformly; `as` casts are type-position only (erased pre-bundle).
|
||
const c = String.fromCharCode
|
||
// biome-ignore format: keep the species list compact
|
||
|
||
export const duck = c(0x64,0x75,0x63,0x6b) as 'duck'
|
||
export const goose = c(0x67, 0x6f, 0x6f, 0x73, 0x65) as 'goose'
|
||
export const blob = c(0x62, 0x6c, 0x6f, 0x62) as 'blob'
|
||
export const cat = c(0x63, 0x61, 0x74) as 'cat'
|
||
export const dragon = c(0x64, 0x72, 0x61, 0x67, 0x6f, 0x6e) as 'dragon'
|
||
export const octopus = c(0x6f, 0x63, 0x74, 0x6f, 0x70, 0x75, 0x73) as 'octopus'
|
||
export const owl = c(0x6f, 0x77, 0x6c) as 'owl'
|
||
export const penguin = c(0x70, 0x65, 0x6e, 0x67, 0x75, 0x69, 0x6e) as 'penguin'
|
||
export const turtle = c(0x74, 0x75, 0x72, 0x74, 0x6c, 0x65) as 'turtle'
|
||
export const snail = c(0x73, 0x6e, 0x61, 0x69, 0x6c) as 'snail'
|
||
export const ghost = c(0x67, 0x68, 0x6f, 0x73, 0x74) as 'ghost'
|
||
export const axolotl = c(0x61, 0x78, 0x6f, 0x6c, 0x6f, 0x74, 0x6c) as 'axolotl'
|
||
export const capybara = c(
|
||
0x63,
|
||
0x61,
|
||
0x70,
|
||
0x79,
|
||
0x62,
|
||
0x61,
|
||
0x72,
|
||
0x61,
|
||
) as 'capybara'
|
||
export const cactus = c(0x63, 0x61, 0x63, 0x74, 0x75, 0x73) as 'cactus'
|
||
export const robot = c(0x72, 0x6f, 0x62, 0x6f, 0x74) as 'robot'
|
||
export const rabbit = c(0x72, 0x61, 0x62, 0x62, 0x69, 0x74) as 'rabbit'
|
||
export const mushroom = c(
|
||
0x6d,
|
||
0x75,
|
||
0x73,
|
||
0x68,
|
||
0x72,
|
||
0x6f,
|
||
0x6f,
|
||
0x6d,
|
||
) as 'mushroom'
|
||
export const chonk = c(0x63, 0x68, 0x6f, 0x6e, 0x6b) as 'chonk'
|
||
|
||
export const SPECIES = [
|
||
duck,
|
||
goose,
|
||
blob,
|
||
cat,
|
||
dragon,
|
||
octopus,
|
||
owl,
|
||
penguin,
|
||
turtle,
|
||
snail,
|
||
ghost,
|
||
axolotl,
|
||
capybara,
|
||
cactus,
|
||
robot,
|
||
rabbit,
|
||
mushroom,
|
||
chonk,
|
||
] as const
|
||
export type Species = (typeof SPECIES)[number] // biome-ignore format: keep compact
|
||
|
||
export const EYES = ['·', '✦', '×', '◉', '@', '°'] as const
|
||
export type Eye = (typeof EYES)[number]
|
||
|
||
export const HATS = [
|
||
'none',
|
||
'crown',
|
||
'tophat',
|
||
'propeller',
|
||
'halo',
|
||
'wizard',
|
||
'beanie',
|
||
'tinyduck',
|
||
] as const
|
||
export type Hat = (typeof HATS)[number]
|
||
|
||
export const STAT_NAMES = [
|
||
'DEBUGGING',
|
||
'PATIENCE',
|
||
'CHAOS',
|
||
'WISDOM',
|
||
'SNARK',
|
||
] as const
|
||
export type StatName = (typeof STAT_NAMES)[number]
|
||
|
||
// Deterministic parts — derived from hash(userId)
|
||
export type CompanionBones = {
|
||
rarity: Rarity
|
||
species: Species
|
||
eye: Eye
|
||
hat: Hat
|
||
shiny: boolean
|
||
stats: Record<StatName, number>
|
||
}
|
||
|
||
// Model-generated soul — stored in config after first hatch
|
||
export type CompanionSoul = {
|
||
name: string
|
||
personality: string
|
||
}
|
||
|
||
export type Companion = CompanionBones &
|
||
CompanionSoul & {
|
||
hatchedAt: number
|
||
}
|
||
|
||
// What actually persists in config. Bones are regenerated from hash(userId)
|
||
// on every read so species renames don't break stored companions and users
|
||
// can't edit their way to a legendary.
|
||
export type StoredCompanion = CompanionSoul & { hatchedAt: number }
|
||
|
||
export const RARITY_WEIGHTS = {
|
||
common: 60,
|
||
uncommon: 25,
|
||
rare: 10,
|
||
epic: 4,
|
||
legendary: 1,
|
||
} as const satisfies Record<Rarity, number>
|
||
|
||
export const RARITY_STARS = {
|
||
common: '★',
|
||
uncommon: '★★',
|
||
rare: '★★★',
|
||
epic: '★★★★',
|
||
legendary: '★★★★★',
|
||
} as const satisfies Record<Rarity, string>
|
||
|
||
export const RARITY_COLORS = {
|
||
common: 'inactive',
|
||
uncommon: 'success',
|
||
rare: 'permission',
|
||
epic: 'autoAccept',
|
||
legendary: 'warning',
|
||
} as const satisfies Record<Rarity, keyof import('../utils/theme.js').Theme>
|