import { prisma } from "../src/core/database/prisma"; import { Prisma } from "@prisma/client"; /** * fullServerSetup.ts * * Script idempotente para poblar UN servidor con todo lo necesario: * - Economy items (herramientas, armas, materiales, cofres, pociones) * - Item recipes (crafteo) * - Item mutations (encantamientos) * - Game areas y niveles * - Mobs con drops * - Opcional: programar ataques de mobs demo * * Uso: provee GUILD_ID como variable de entorno opcional. Si no se provee, usa el id por defecto. * GUILD_ID=1316592320954630144 npx tsx scripts/fullServerSetup.ts */ const DEFAULT_GUILD = process.env.GUILD_ID ?? "1316592320954630144"; async function upsertEconomyItem( guildId: string | null, key: string, data: Omit ) { const existing = await prisma.economyItem.findFirst({ where: { key, guildId }, }); if (existing) return prisma.economyItem.update({ where: { id: existing.id }, data: { ...data }, }); return prisma.economyItem.create({ data: { ...data, key, guildId } }); } async function upsertGameArea( guildId: string | null, key: string, data: Omit ) { const existing = await prisma.gameArea.findFirst({ where: { key, guildId } }); if (existing) return prisma.gameArea.update({ where: { id: existing.id }, data: { ...data }, }); return prisma.gameArea.create({ data: { ...data, key, guildId } }); } async function upsertMob( guildId: string | null, key: string, data: Omit ) { const existing = await prisma.mob.findFirst({ where: { key, guildId } }); if (existing) return prisma.mob.update({ where: { id: existing.id }, data: { ...data } }); return prisma.mob.create({ data: { ...data, key, guildId } }); } async function upsertItemRecipe( guildId: string | null, productKey: string, ingredients: { itemKey: string; qty: number }[], productQty = 1 ) { // Ensure product exists const product = await prisma.economyItem.findFirst({ where: { key: productKey, OR: [{ guildId }, { guildId: null }] }, orderBy: [{ guildId: "desc" }], }); if (!product) throw new Error(`Product item not found: ${productKey}`); // Find existing recipe by productItemId const existing = await prisma.itemRecipe.findUnique({ where: { productItemId: product.id }, }); if (existing) { // Recreate ingredients set await prisma.recipeIngredient.deleteMany({ where: { recipeId: existing.id }, }); for (const ing of ingredients) { const it = await prisma.economyItem.findFirst({ where: { key: ing.itemKey, OR: [{ guildId }, { guildId: null }] }, orderBy: [{ guildId: "desc" }], }); if (!it) throw new Error(`Ingredient item not found: ${ing.itemKey}`); await prisma.recipeIngredient.create({ data: { recipeId: existing.id, itemId: it.id, quantity: ing.qty }, }); } return existing; } const r = await prisma.itemRecipe.create({ data: { productItemId: product.id, productQuantity: productQty }, }); for (const ing of ingredients) { const it = await prisma.economyItem.findFirst({ where: { key: ing.itemKey, OR: [{ guildId }, { guildId: null }] }, orderBy: [{ guildId: "desc" }], }); if (!it) throw new Error(`Ingredient item not found: ${ing.itemKey}`); await prisma.recipeIngredient.create({ data: { recipeId: r.id, itemId: it.id, quantity: ing.qty }, }); } return r; } export async function runFullServerSetup( guildIdArg?: string | null, options?: { dryRun?: boolean } ) { const guildId = guildIdArg ?? DEFAULT_GUILD; console.log("Starting full server setup for guild=", guildId, options ?? {}); // --- Items: tools, weapons, materials --- await upsertEconomyItem(guildId, "tool.pickaxe.basic", { name: "Pico Básico", stackable: false, props: { tool: { type: "pickaxe", tier: 1 }, breakable: { enabled: true, maxDurability: 100, durabilityPerUse: 5 }, } as unknown as Prisma.InputJsonValue, tags: ["tool", "mine"], }); await upsertEconomyItem(guildId, "tool.pickaxe.iron", { name: "Pico de Hierro", stackable: false, props: { tool: { type: "pickaxe", tier: 2 }, breakable: { enabled: true, maxDurability: 180, durabilityPerUse: 4 }, } as unknown as Prisma.InputJsonValue, tags: ["tool", "mine", "tier2"], }); await upsertEconomyItem(guildId, "weapon.sword.iron", { name: "Espada de Hierro", stackable: false, props: { damage: 10, tool: { type: "sword", tier: 1 }, breakable: { enabled: true, maxDurability: 150, durabilityPerUse: 2 }, } as unknown as Prisma.InputJsonValue, tags: ["weapon"], }); await upsertEconomyItem(guildId, "armor.leather.basic", { name: "Armadura de Cuero", stackable: false, props: { defense: 3 } as unknown as Prisma.InputJsonValue, tags: ["armor"], }); await upsertEconomyItem(guildId, "ore.iron", { name: "Mineral de Hierro", stackable: true, props: { craftingOnly: true } as unknown as Prisma.InputJsonValue, tags: ["ore", "common"], }); await upsertEconomyItem(guildId, "ingot.iron", { name: "Lingote de Hierro", stackable: true, props: {} as unknown as Prisma.InputJsonValue, tags: ["ingot", "metal"], }); // Consumibles y pociones await upsertEconomyItem(guildId, "food.meat.small", { name: "Carne Pequeña", stackable: true, props: { food: { healHp: 8, cooldownSeconds: 20 }, } as unknown as Prisma.InputJsonValue, tags: ["food"], }); await upsertEconomyItem(guildId, "potion.energy", { name: "Poción Energética", stackable: true, props: { potion: { removeEffects: ["FATIGUE"], cooldownSeconds: 90 }, } as unknown as Prisma.InputJsonValue, tags: ["potion", "utility"], }); // Cofre con recompensas await upsertEconomyItem(guildId, "chest.daily", { name: "Cofre Diario", stackable: true, props: { chest: { enabled: true, consumeOnOpen: true, randomMode: "single", rewards: [ { type: "coins", amount: 200 }, { type: "item", itemKey: "ingot.iron", qty: 2 }, ], }, } as unknown as Prisma.InputJsonValue, tags: ["chest"], }); // --- Mutations / enchants catalog --- // Item mutations (catalog) const existingRuby = await prisma.itemMutation.findFirst({ where: { key: "ruby_core", guildId }, }); if (existingRuby) { await prisma.itemMutation.update({ where: { id: existingRuby.id }, data: { name: "Núcleo de Rubí", effects: { damageBonus: 15 } as any }, }); } else { await prisma.itemMutation.create({ data: { key: "ruby_core", name: "Núcleo de Rubí", guildId, effects: { damageBonus: 15 } as any, } as any, }); } const existingEmerald = await prisma.itemMutation.findFirst({ where: { key: "emerald_core", guildId }, }); if (existingEmerald) { await prisma.itemMutation.update({ where: { id: existingEmerald.id }, data: { name: "Núcleo de Esmeralda", effects: { defenseBonus: 10, maxHpBonus: 20 } as any, }, }); } else { await prisma.itemMutation.create({ data: { key: "emerald_core", name: "Núcleo de Esmeralda", guildId, effects: { defenseBonus: 10, maxHpBonus: 20 } as any, } as any, }); } // --- Recipes (crafteo): iron_ingot <- iron ore x3 // Create ingredient items if missing await upsertEconomyItem(guildId, "ingot.iron", { name: "Lingote de Hierro", stackable: true, props: {} as unknown as Prisma.InputJsonValue, tags: ["ingot"], }); await upsertEconomyItem(guildId, "ore.iron", { name: "Mineral de Hierro", stackable: true, props: {} as unknown as Prisma.InputJsonValue, tags: ["ore"], }); await upsertItemRecipe( guildId, "ingot.iron", [{ itemKey: "ore.iron", qty: 3 }], 1 ); // --- Areas & Levels --- const mine = await upsertGameArea(guildId, "mine.cavern", { name: "Mina: Caverna", type: "MINE", config: { cooldownSeconds: 10 } as unknown as Prisma.InputJsonValue, }); await prisma.gameAreaLevel.upsert({ where: { areaId_level: { areaId: mine.id, level: 1 } }, update: {}, create: { areaId: mine.id, level: 1, requirements: { tool: { required: true, toolType: "pickaxe", minTier: 1 }, } as any, rewards: { draws: 2, table: [ { type: "item", itemKey: "ore.iron", qty: 2, weight: 70 }, { type: "coins", amount: 10, weight: 30 }, ], } as any, mobs: { draws: 1, table: [ { mobKey: "slime.green", weight: 50 }, { mobKey: "bat", weight: 50 }, ], } as any, }, }); const lagoon = await upsertGameArea(guildId, "lagoon.shore", { name: "Laguna: Orilla", type: "LAGOON", config: { cooldownSeconds: 12 } as unknown as Prisma.InputJsonValue, }); await prisma.gameAreaLevel.upsert({ where: { areaId_level: { areaId: lagoon.id, level: 1 } }, update: {}, create: { areaId: lagoon.id, level: 1, requirements: { tool: { required: true, toolType: "rod", minTier: 1 }, } as any, rewards: { draws: 2, table: [ { type: "item", itemKey: "food.meat.small", qty: 1, weight: 70 }, { type: "coins", amount: 10, weight: 30 }, ], } as any, mobs: { draws: 0, table: [] } as any, }, }); // --- Basic mobs --- await upsertMob(guildId, "slime.green", { name: "Slime Verde", stats: { attack: 4, hp: 18 } as any, drops: [{ itemKey: "ingot.iron", qty: 1, weight: 10 }] as any, }); await upsertMob(guildId, "bat", { name: "Murciélago", stats: { attack: 3, hp: 10 } as any, drops: Prisma.DbNull, }); // Advanced mobs await upsertMob(guildId, "goblin", { name: "Duende", stats: { attack: 8, hp: 30 } as any, drops: [{ itemKey: "ore.iron", qty: 1, weight: 50 }] as any, }); await upsertMob(guildId, "orc", { name: "Orco", stats: { attack: 12, hp: 50 } as any, drops: Prisma.DbNull, }); // Programar un par de ataques demo (opcional) const targetUser = process.env.TARGET_USER ?? null; if (targetUser) { const slime = await prisma.mob.findFirst({ where: { key: "slime.green", OR: [{ guildId }, { guildId: null }] }, orderBy: [{ guildId: "desc" }], }); if (slime) { const now = Date.now(); await prisma.scheduledMobAttack.createMany({ data: [ { userId: targetUser, guildId: guildId ?? "global", mobId: slime.id, scheduleAt: new Date(now + 5_000), }, { userId: targetUser, guildId: guildId ?? "global", mobId: slime.id, scheduleAt: new Date(now + 15_000), }, ], }); } } console.log("Full server setup complete."); } // Backwards-compatible CLI entry if (require.main === module) { const gid = process.env.GUILD_ID ?? DEFAULT_GUILD; runFullServerSetup(gid) .then(() => process.exit(0)) .catch((e) => { console.error(e); process.exit(1); }); }