Files
amayo/src/commands/messages/game/pelear.ts

212 lines
7.3 KiB
TypeScript
Raw Normal View History

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 { formatToolLabel, combatSummaryRPG } from "../../../game/lib/rpgFormat";
import { buildAreaMetadataBlocks } from "./_helpers";
const FIGHT_ACCENT = 0x992d22;
export const command: CommandMessage = {
name: "pelear",
type: "message",
aliases: ["fight", "arena"],
cooldown: 8,
category: "Minijuegos",
description: "Entra a la arena y pelea (usa espada si está disponible).",
usage:
"pelear [nivel] [toolKey] [area:clave] (ej: pelear 1 weapon.sword.iron)",
run: async (message, args, _client: Amayo) => {
const userId = message.author.id;
const guildId = message.guild!.id;
const { levelArg, providedTool, areaOverride } = parseGameArgs(args);
const areaInfo = areaOverride
? await resolveGuildAreaWithFallback(guildId, areaOverride)
: await resolveAreaByType(guildId, "FIGHT");
if (!areaInfo.area) {
if (areaOverride) {
await message.reply(
`⚠️ No existe un área con key \`${areaOverride}\` para este servidor.`
);
} else {
await message.reply(
"⚠️ No hay un área de tipo **FIGHT** configurada. Crea una con `!area-crear` o especifica `area:<key>`."
);
}
return;
}
const { area, source } = areaInfo;
const globalNotice =
source === "global"
? ` Usando configuración global para \`${area.key}\`. Puedes crear \`gameArea\` tipo **FIGHT** para personalizarla en este servidor.`
: null;
const level = levelArg ?? (await getDefaultLevel(userId, guildId, area.id));
const toolKey =
providedTool ?? (await findBestToolKey(userId, guildId, "sword"));
try {
const result = await runMinigame(userId, guildId, area.key, level, {
toolKey: toolKey ?? undefined,
});
const rewardKeys = result.rewards
.filter(
(r): r is { type: "item"; itemKey: string; qty?: number } =>
r.type === "item" && Boolean(r.itemKey)
)
.map((r) => r.itemKey!);
if (result.tool?.key) rewardKeys.push(result.tool.key);
const rewardItems = await fetchItemBasics(guildId, rewardKeys);
// Actualizar stats y misiones
await updateStats(userId, guildId, { fightsCompleted: 1 });
await updateQuestProgress(userId, guildId, "fight_count", 1);
// Contar mobs derrotados
const mobsCount = result.mobs.length;
if (mobsCount > 0) {
await updateStats(userId, guildId, { mobsDefeated: mobsCount });
await updateQuestProgress(
userId,
guildId,
"mob_defeat_count",
mobsCount
);
}
const newAchievements = await checkAchievements(
userId,
guildId,
"fight_count"
);
let rewardLines = result.rewards.length
? 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")
: "• —";
if (result.rewardModifiers?.baseCoinsAwarded != null) {
const { baseCoinsAwarded, coinsAfterPenalty, fatigueCoinMultiplier } =
result.rewardModifiers;
if (
fatigueCoinMultiplier != null &&
fatigueCoinMultiplier < 1 &&
baseCoinsAwarded != null &&
coinsAfterPenalty != null
) {
const pct = Math.round((1 - fatigueCoinMultiplier) * 100);
rewardLines += `\n (⚠️ Fatiga: monedas base ${baseCoinsAwarded}${coinsAfterPenalty} (-${pct}%) )`;
}
}
const mobsLines = result.mobs.length
? result.mobs.map((m) => `${m}`).join("\n")
: "• —";
const toolInfo = result.tool?.key
? formatToolLabel({
key: result.tool.key,
displayName: formatItemLabel(
rewardItems.get(result.tool.key) ?? {
key: result.tool.key,
name: null,
icon: null,
},
{ fallbackIcon: "🗡️" }
),
instancesRemaining: result.tool.instancesRemaining,
broken: result.tool.broken,
brokenInstance: result.tool.brokenInstance,
durabilityDelta: result.tool.durabilityDelta,
remaining: result.tool.remaining,
max: result.tool.max,
source: result.tool.toolSource,
})
: "—";
const combatSummary = result.combat
? combatSummaryRPG({
mobs: result.mobs.length,
mobsDefeated: result.combat.mobsDefeated,
totalDamageDealt: result.combat.totalDamageDealt,
totalDamageTaken: result.combat.totalDamageTaken,
playerStartHp: result.combat.playerStartHp,
playerEndHp: result.combat.playerEndHp,
outcome: result.combat.outcome,
})
: null;
const blocks = [textBlock("# ⚔️ Arena")];
if (globalNotice) {
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
blocks.push(textBlock(globalNotice));
}
blocks.push(dividerBlock());
const areaScope =
source === "global"
? "🌐 Configuración global"
: "📍 Configuración local";
blocks.push(
textBlock(
`**Área:** \`${area.key}\`${areaScope}\n**Nivel:** ${level}\n**Arma:** ${toolInfo}`
)
);
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
blocks.push(textBlock(`**Recompensas**\n${rewardLines}`));
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
blocks.push(textBlock(`**Enemigos**\n${mobsLines}`));
if (combatSummary) {
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
blocks.push(textBlock(combatSummary));
}
// Añadir metadata del área
const metaBlocks = buildAreaMetadataBlocks(area);
if (metaBlocks.length) {
blocks.push(dividerBlock());
blocks.push(...metaBlocks);
}
if (newAchievements.length > 0) {
blocks.push(dividerBlock({ divider: false, spacing: 2 }));
const achLines = newAchievements
.map((ach) => `✨ **${ach.name}** — ${ach.description}`)
.join("\n");
blocks.push(textBlock(`🏆 ¡Logro desbloqueado!\n${achLines}`));
}
const display = buildDisplay(FIGHT_ACCENT, blocks);
await sendDisplayReply(message, display);
} catch (e: any) {
await message.reply(`❌ No se pudo pelear: ${e?.message ?? e}`);
}
},
};