2025-10-07 22:17:03 -05:00
|
|
|
|
import type { CommandMessage } from "../../../core/types/commands";
|
|
|
|
|
|
import type Amayo from "../../../core/client";
|
|
|
|
|
|
import { runMinigame } from "../../../game/minigames/service";
|
|
|
|
|
|
import {
|
|
|
|
|
|
getDefaultLevel,
|
|
|
|
|
|
findBestToolKey,
|
|
|
|
|
|
parseGameArgs,
|
|
|
|
|
|
resolveGuildAreaWithFallback,
|
|
|
|
|
|
resolveAreaByType,
|
|
|
|
|
|
sendDisplayReply,
|
|
|
|
|
|
fetchItemBasics,
|
|
|
|
|
|
formatItemLabel,
|
|
|
|
|
|
} from "./_helpers";
|
|
|
|
|
|
import { updateStats } from "../../../game/stats/service";
|
|
|
|
|
|
import { updateQuestProgress } from "../../../game/quests/service";
|
|
|
|
|
|
import { checkAchievements } from "../../../game/achievements/service";
|
|
|
|
|
|
import {
|
|
|
|
|
|
buildDisplay,
|
|
|
|
|
|
dividerBlock,
|
|
|
|
|
|
textBlock,
|
|
|
|
|
|
} from "../../../core/lib/componentsV2";
|
|
|
|
|
|
import { buildAreaMetadataBlocks } from "./_helpers";
|
2025-10-05 21:37:54 -05:00
|
|
|
|
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const MINING_ACCENT = 0xc27c0e;
|
2025-10-05 01:54:12 -05:00
|
|
|
|
|
|
|
|
|
|
export const command: CommandMessage = {
|
2025-10-07 22:17:03 -05:00
|
|
|
|
name: "mina",
|
|
|
|
|
|
type: "message",
|
|
|
|
|
|
aliases: ["minar"],
|
2025-10-05 01:54:12 -05:00
|
|
|
|
cooldown: 5,
|
2025-10-07 22:17:03 -05:00
|
|
|
|
description:
|
|
|
|
|
|
"Ir a la mina (usa pico si está disponible) y obtener recompensas según el nivel.",
|
|
|
|
|
|
usage: "mina [nivel] [toolKey] [area:clave] (ej: mina 2 tool.pickaxe.basic)",
|
2025-10-05 01:54:12 -05:00
|
|
|
|
run: async (message, args, _client: Amayo) => {
|
|
|
|
|
|
const userId = message.author.id;
|
|
|
|
|
|
const guildId = message.guild!.id;
|
2025-10-05 19:58:26 -05:00
|
|
|
|
const { levelArg, providedTool, areaOverride } = parseGameArgs(args);
|
2025-10-05 01:54:12 -05:00
|
|
|
|
|
2025-10-05 19:58:26 -05:00
|
|
|
|
const areaInfo = areaOverride
|
|
|
|
|
|
? await resolveGuildAreaWithFallback(guildId, areaOverride)
|
2025-10-07 22:17:03 -05:00
|
|
|
|
: await resolveAreaByType(guildId, "MINE");
|
2025-10-05 19:58:26 -05:00
|
|
|
|
|
|
|
|
|
|
if (!areaInfo.area) {
|
|
|
|
|
|
if (areaOverride) {
|
2025-10-07 22:17:03 -05:00
|
|
|
|
await message.reply(
|
|
|
|
|
|
`⚠️ No existe un área con key \`${areaOverride}\` para este servidor.`
|
|
|
|
|
|
);
|
2025-10-05 19:58:26 -05:00
|
|
|
|
} else {
|
2025-10-07 22:17:03 -05:00
|
|
|
|
await message.reply(
|
|
|
|
|
|
"⚠️ No hay un área de tipo **MINE** configurada. Crea una con `!area-crear` o especifica `area:<key>`."
|
|
|
|
|
|
);
|
2025-10-05 19:58:26 -05:00
|
|
|
|
}
|
2025-10-05 19:34:45 -05:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-05 19:58:26 -05:00
|
|
|
|
|
|
|
|
|
|
const { area, source } = areaInfo;
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const globalNotice =
|
|
|
|
|
|
source === "global"
|
|
|
|
|
|
? `ℹ️ Usando configuración global para \`${area.key}\`. Puedes crear \`gameArea\` tipo **MINE** para personalizarla en este servidor.`
|
|
|
|
|
|
: null;
|
2025-10-05 01:54:12 -05:00
|
|
|
|
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const level = levelArg ?? (await getDefaultLevel(userId, guildId, area.id));
|
|
|
|
|
|
const toolKey =
|
|
|
|
|
|
providedTool ?? (await findBestToolKey(userId, guildId, "pickaxe"));
|
2025-10-05 01:54:12 -05:00
|
|
|
|
|
|
|
|
|
|
try {
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const result = await runMinigame(userId, guildId, area.key, level, {
|
|
|
|
|
|
toolKey: toolKey ?? undefined,
|
|
|
|
|
|
});
|
2025-10-05 21:37:54 -05:00
|
|
|
|
|
|
|
|
|
|
const rewardKeys = result.rewards
|
2025-10-07 22:17:03 -05:00
|
|
|
|
.filter(
|
|
|
|
|
|
(r): r is { type: "item"; itemKey: string; qty?: number } =>
|
|
|
|
|
|
r.type === "item" && Boolean(r.itemKey)
|
|
|
|
|
|
)
|
2025-10-05 21:37:54 -05:00
|
|
|
|
.map((r) => r.itemKey!);
|
|
|
|
|
|
if (result.tool?.key) rewardKeys.push(result.tool.key);
|
|
|
|
|
|
const rewardItems = await fetchItemBasics(guildId, rewardKeys);
|
2025-10-07 22:17:03 -05:00
|
|
|
|
|
2025-10-05 05:35:32 -05:00
|
|
|
|
// Actualizar stats
|
|
|
|
|
|
await updateStats(userId, guildId, { minesCompleted: 1 });
|
2025-10-05 19:44:54 -05:00
|
|
|
|
|
|
|
|
|
|
// Actualizar progreso de misiones
|
2025-10-07 22:17:03 -05:00
|
|
|
|
await updateQuestProgress(userId, guildId, "mine_count", 1);
|
|
|
|
|
|
|
2025-10-05 05:35:32 -05:00
|
|
|
|
// Verificar logros
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const newAchievements = await checkAchievements(
|
|
|
|
|
|
userId,
|
|
|
|
|
|
guildId,
|
|
|
|
|
|
"mine_count"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-10-05 21:37:54 -05:00
|
|
|
|
const rewardLines = result.rewards.length
|
2025-10-07 22:17:03 -05:00
|
|
|
|
? result.rewards
|
|
|
|
|
|
.map((r) => {
|
|
|
|
|
|
if (r.type === "coins") return `• 🪙 +${r.amount}`;
|
|
|
|
|
|
const info = rewardItems.get(r.itemKey!);
|
|
|
|
|
|
const label = formatItemLabel(
|
|
|
|
|
|
info ?? { key: r.itemKey!, name: null, icon: null }
|
|
|
|
|
|
);
|
|
|
|
|
|
return `• ${label} x${r.qty ?? 1}`;
|
|
|
|
|
|
})
|
|
|
|
|
|
.join("\n")
|
|
|
|
|
|
: "• —";
|
2025-10-05 21:37:54 -05:00
|
|
|
|
const mobsLines = result.mobs.length
|
2025-10-07 22:17:03 -05:00
|
|
|
|
? result.mobs.map((m) => `• ${m}`).join("\n")
|
|
|
|
|
|
: "• —";
|
2025-10-05 21:37:54 -05:00
|
|
|
|
const toolInfo = result.tool?.key
|
2025-10-07 22:17:03 -05:00
|
|
|
|
? `${formatItemLabel(
|
|
|
|
|
|
rewardItems.get(result.tool.key) ?? {
|
|
|
|
|
|
key: result.tool.key,
|
|
|
|
|
|
name: null,
|
|
|
|
|
|
icon: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ fallbackIcon: "🔧" }
|
|
|
|
|
|
)}${
|
|
|
|
|
|
result.tool.broken
|
|
|
|
|
|
? " (rota)"
|
|
|
|
|
|
: ` (-${result.tool.durabilityDelta ?? 0} dur.)`
|
|
|
|
|
|
}`
|
|
|
|
|
|
: "—";
|
2025-10-05 21:37:54 -05:00
|
|
|
|
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const blocks = [textBlock("# ⛏️ Mina")];
|
2025-10-05 21:37:54 -05:00
|
|
|
|
|
|
|
|
|
|
if (globalNotice) {
|
|
|
|
|
|
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
|
|
|
|
|
blocks.push(textBlock(globalNotice));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
blocks.push(dividerBlock());
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const areaScope =
|
|
|
|
|
|
source === "global"
|
|
|
|
|
|
? "🌐 Configuración global"
|
|
|
|
|
|
: "📍 Configuración local";
|
|
|
|
|
|
blocks.push(
|
|
|
|
|
|
textBlock(
|
|
|
|
|
|
`**Área:** \`${area.key}\` • ${areaScope}\n**Nivel:** ${level}\n**Herramienta:** ${toolInfo}`
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
2025-10-05 21:37:54 -05:00
|
|
|
|
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
|
|
|
|
|
blocks.push(textBlock(`**Recompensas**\n${rewardLines}`));
|
|
|
|
|
|
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
|
|
|
|
|
blocks.push(textBlock(`**Mobs**\n${mobsLines}`));
|
2025-10-05 05:35:32 -05:00
|
|
|
|
|
2025-10-07 22:17:03 -05:00
|
|
|
|
// Añadir metadata del área (imagen/descripcion) si existe
|
|
|
|
|
|
const metaBlocks = buildAreaMetadataBlocks(area);
|
|
|
|
|
|
if (metaBlocks.length) {
|
|
|
|
|
|
blocks.push(dividerBlock());
|
|
|
|
|
|
blocks.push(...metaBlocks);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-05 05:35:32 -05:00
|
|
|
|
if (newAchievements.length > 0) {
|
2025-10-05 21:37:54 -05:00
|
|
|
|
blocks.push(dividerBlock({ divider: false, spacing: 2 }));
|
2025-10-07 22:17:03 -05:00
|
|
|
|
const achLines = newAchievements
|
|
|
|
|
|
.map((ach) => `✨ **${ach.name}** — ${ach.description}`)
|
|
|
|
|
|
.join("\n");
|
2025-10-05 21:37:54 -05:00
|
|
|
|
blocks.push(textBlock(`🏆 ¡Logro desbloqueado!\n${achLines}`));
|
2025-10-05 05:35:32 -05:00
|
|
|
|
}
|
2025-10-05 21:37:54 -05:00
|
|
|
|
|
|
|
|
|
|
const display = buildDisplay(MINING_ACCENT, blocks);
|
|
|
|
|
|
await sendDisplayReply(message, display);
|
2025-10-05 01:54:12 -05:00
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
await message.reply(`❌ No se pudo minar: ${e?.message ?? e}`);
|
|
|
|
|
|
}
|
2025-10-07 22:17:03 -05:00
|
|
|
|
},
|
2025-10-05 01:54:12 -05:00
|
|
|
|
};
|