import { z } from 'zod'; /** XP required to go from level L to L+1. */ export function xpForNextLevel(level: number): number { return 5 * level * level + 50 * level + 100; } /** Total XP needed to reach a given level from 0. */ export function totalXpForLevel(level: number): number { let total = 0; for (let i = 0; i < level; i += 1) { total += xpForNextLevel(i); } return total; } /** Derive level from cumulative XP. */ export function levelFromXp(xp: number): number { let level = 0; let remaining = Math.max(0, xp); while (remaining >= xpForNextLevel(level)) { remaining -= xpForNextLevel(level); level += 1; if (level > 1000) { break; } } return level; } export function progressInLevel(xp: number): { level: number; intoLevel: number; needed: number } { const level = levelFromXp(xp); const base = totalXpForLevel(level); const intoLevel = xp - base; const needed = xpForNextLevel(level); return { level, intoLevel, needed }; } export function randomInt(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; } export const LevelUpModeSchema = z.enum(['CHANNEL', 'DM', 'OFF']); export type LevelUpMode = z.infer; export const MultiplierMapSchema = z.record(z.string(), z.number().positive()); export type MultiplierMap = z.infer; export function parseMultiplierMap(raw: unknown): MultiplierMap { const parsed = MultiplierMapSchema.safeParse(raw); return parsed.success ? parsed.data : {}; } export function formatCurrency(amount: number, symbol: string, name: string): string { return `${symbol} ${amount.toLocaleString()} ${name}`; } export const EIGHT_BALL_ANSWERS = { de: [ 'Ja.', 'Nein.', 'Vielleicht.', 'Frag später nochmal.', 'Sieht gut aus.', 'Zweifelhaft.', 'Auf keinen Fall.', 'Absolut.' ], en: [ 'Yes.', 'No.', 'Maybe.', 'Ask again later.', 'Looks good.', 'Doubtful.', 'Absolutely not.', 'Absolutely.' ] } as const; export const TRIVIA_QUESTIONS = [ { q: { de: 'Welche Farbe hat der Himmel an einem klaren Tag?', en: 'What color is the sky on a clear day?' }, options: { de: ['Blau', 'Grün', 'Rot', 'Gelb'], en: ['Blue', 'Green', 'Red', 'Yellow'] }, answer: 0 }, { q: { de: 'Wie viele Kontinente gibt es?', en: 'How many continents are there?' }, options: { de: ['5', '6', '7', '8'], en: ['5', '6', '7', '8'] }, answer: 2 }, { q: { de: 'Welches Tier ist ein Säugetier?', en: 'Which animal is a mammal?' }, options: { de: ['Hai', 'Delphin', 'Thunfisch', 'Lachs'], en: ['Shark', 'Dolphin', 'Tuna', 'Salmon'] }, answer: 1 }, { q: { de: 'Was ist 2 + 2?', en: 'What is 2 + 2?' }, options: { de: ['3', '4', '5', '22'], en: ['3', '4', '5', '22'] }, answer: 1 }, { q: { de: 'Hauptstadt von Frankreich?', en: 'Capital of France?' }, options: { de: ['Berlin', 'Madrid', 'Paris', 'Rom'], en: ['Berlin', 'Madrid', 'Paris', 'Rome'] }, answer: 2 } ] as const; export const HANGMAN_WORDS = { de: ['DISCORD', 'NEXUMI', 'SERVER', 'MODERATION', 'BOT', 'KANAL', 'ROLLE'], en: ['DISCORD', 'NEXUMI', 'SERVER', 'MODERATION', 'BOT', 'CHANNEL', 'ROLE'] } as const;