Implement leveling and economy features with associated models and commands
- Added new models for leveling and economy functionalities in the Prisma schema, including LevelingConfig, MemberLevel, LevelReward, EconomyConfig, MemberEconomy, ShopItem, and InventoryItem. - Introduced commands for managing XP, economy transactions, and shop interactions, enhancing user engagement. - Updated job handling to include reminders and poll closing functionalities. - Enhanced localization support for new commands and features in both German and English. - Improved the bot's job processing capabilities with a dedicated reminder worker for scheduled tasks.
This commit is contained in:
112
packages/shared/src/phase3.ts
Normal file
112
packages/shared/src/phase3.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
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<typeof LevelUpModeSchema>;
|
||||
|
||||
export const MultiplierMapSchema = z.record(z.string(), z.number().positive());
|
||||
export type MultiplierMap = z.infer<typeof MultiplierMapSchema>;
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user