feat: agregar barra de durabilidad y resumen de combate en comandos de mina, pelear y pescar; implementar comando para inspeccionar herramientas
This commit is contained in:
@@ -105,21 +105,52 @@ export const command: CommandMessage = {
|
||||
const mobsLines = result.mobs.length
|
||||
? result.mobs.map((m) => `• ${m}`).join("\n")
|
||||
: "• —";
|
||||
|
||||
const durabilityBar = () => {
|
||||
if (
|
||||
!result.tool ||
|
||||
result.tool.remaining == null ||
|
||||
result.tool.max == null
|
||||
)
|
||||
return "";
|
||||
const rem = Math.max(0, result.tool.remaining);
|
||||
const max = Math.max(1, result.tool.max);
|
||||
const ratio = rem / max;
|
||||
const totalSegs = 10;
|
||||
const filled = Math.round(ratio * totalSegs);
|
||||
const bar = Array.from({ length: totalSegs })
|
||||
.map((_, i) => (i < filled ? "█" : "░"))
|
||||
.join("");
|
||||
return `\nDurabilidad: [${bar}] ${rem}/${max}`;
|
||||
};
|
||||
|
||||
const toolInfo = result.tool?.key
|
||||
? `${formatItemLabel(
|
||||
rewardItems.get(result.tool.key) ?? {
|
||||
key: result.tool.key,
|
||||
name: null,
|
||||
icon: null,
|
||||
},
|
||||
{ fallbackIcon: "🔧" }
|
||||
)}${
|
||||
result.tool.broken
|
||||
? " (rota)"
|
||||
: ` (-${result.tool.durabilityDelta ?? 0} dur.)`
|
||||
}`
|
||||
? (() => {
|
||||
const base = formatItemLabel(
|
||||
rewardItems.get(result.tool.key) ?? {
|
||||
key: result.tool.key,
|
||||
name: null,
|
||||
icon: null,
|
||||
},
|
||||
{ fallbackIcon: "🔧" }
|
||||
);
|
||||
if (result.tool.broken) {
|
||||
return `${base} (agotada)${durabilityBar()}`;
|
||||
}
|
||||
if (result.tool.brokenInstance) {
|
||||
return `${base} (se rompió una instancia, quedan ${result.tool.instancesRemaining}) (-${result.tool.durabilityDelta ?? 0} dur.)${durabilityBar()}`;
|
||||
}
|
||||
const multi = result.tool.instancesRemaining && result.tool.instancesRemaining > 1 ? ` (x${result.tool.instancesRemaining})` : "";
|
||||
return `${base}${multi} (-${result.tool.durabilityDelta ?? 0} dur.)${durabilityBar()}`;
|
||||
})()
|
||||
: "—";
|
||||
|
||||
const combatSummary = (() => {
|
||||
if (!result.combat) return null;
|
||||
const c = result.combat;
|
||||
return `**Combate**\n• Mobs: ${c.mobs.length} | Derrotados: ${c.mobsDefeated}/${result.mobs.length}\n• Daño hecho: ${c.totalDamageDealt} | Daño recibido: ${c.totalDamageTaken}`;
|
||||
})();
|
||||
|
||||
const blocks = [textBlock("# ⛏️ Mina")];
|
||||
|
||||
if (globalNotice) {
|
||||
@@ -141,6 +172,10 @@ export const command: CommandMessage = {
|
||||
blocks.push(textBlock(`**Recompensas**\n${rewardLines}`));
|
||||
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
||||
blocks.push(textBlock(`**Mobs**\n${mobsLines}`));
|
||||
if (combatSummary) {
|
||||
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
||||
blocks.push(textBlock(combatSummary));
|
||||
}
|
||||
|
||||
// Añadir metadata del área (imagen/descripcion) si existe
|
||||
const metaBlocks = buildAreaMetadataBlocks(area);
|
||||
|
||||
@@ -115,20 +115,45 @@ export const command: CommandMessage = {
|
||||
const mobsLines = result.mobs.length
|
||||
? result.mobs.map((m) => `• ${m}`).join("\n")
|
||||
: "• —";
|
||||
const durabilityBar = () => {
|
||||
if (
|
||||
!result.tool ||
|
||||
result.tool.remaining == null ||
|
||||
result.tool.max == null
|
||||
)
|
||||
return "";
|
||||
const rem = Math.max(0, result.tool.remaining);
|
||||
const max = Math.max(1, result.tool.max);
|
||||
const ratio = rem / max;
|
||||
const totalSegs = 10;
|
||||
const filled = Math.round(ratio * totalSegs);
|
||||
const bar = Array.from({ length: totalSegs })
|
||||
.map((_, i) => (i < filled ? "█" : "░"))
|
||||
.join("");
|
||||
return `\nDurabilidad: [${bar}] ${rem}/${max}`;
|
||||
};
|
||||
const toolInfo = result.tool?.key
|
||||
? `${formatItemLabel(
|
||||
rewardItems.get(result.tool.key) ?? {
|
||||
key: result.tool.key,
|
||||
name: null,
|
||||
icon: null,
|
||||
},
|
||||
{ fallbackIcon: "🗡️" }
|
||||
)}${
|
||||
result.tool.broken
|
||||
? " (rota)"
|
||||
: ` (-${result.tool.durabilityDelta ?? 0} dur.)`
|
||||
}`
|
||||
? (() => {
|
||||
const base = formatItemLabel(
|
||||
rewardItems.get(result.tool.key) ?? {
|
||||
key: result.tool.key,
|
||||
name: null,
|
||||
icon: null,
|
||||
},
|
||||
{ fallbackIcon: "🗡️" }
|
||||
);
|
||||
if (result.tool.broken) return `${base} (agotada)${durabilityBar()}`;
|
||||
if (result.tool.brokenInstance)
|
||||
return `${base} (se rompió una instancia, quedan ${result.tool.instancesRemaining}) (-${result.tool.durabilityDelta ?? 0} dur.)${durabilityBar()}`;
|
||||
const multi = result.tool.instancesRemaining && result.tool.instancesRemaining > 1 ? ` (x${result.tool.instancesRemaining})` : "";
|
||||
return `${base}${multi} (-${result.tool.durabilityDelta ?? 0} dur.)${durabilityBar()}`;
|
||||
})()
|
||||
: "—";
|
||||
const combatSummary = (() => {
|
||||
if (!result.combat) return null;
|
||||
const c = result.combat;
|
||||
return `**Combate**\n• Mobs: ${c.mobs.length} | Derrotados: ${c.mobsDefeated}/${result.mobs.length}\n• Daño hecho: ${c.totalDamageDealt} | Daño recibido: ${c.totalDamageTaken}`;
|
||||
})();
|
||||
|
||||
const blocks = [textBlock("# ⚔️ Arena")];
|
||||
|
||||
@@ -151,6 +176,10 @@ export const command: CommandMessage = {
|
||||
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);
|
||||
|
||||
@@ -101,20 +101,45 @@ export const command: CommandMessage = {
|
||||
const mobsLines = result.mobs.length
|
||||
? result.mobs.map((m) => `• ${m}`).join("\n")
|
||||
: "• —";
|
||||
const durabilityBar = () => {
|
||||
if (
|
||||
!result.tool ||
|
||||
result.tool.remaining == null ||
|
||||
result.tool.max == null
|
||||
)
|
||||
return "";
|
||||
const rem = Math.max(0, result.tool.remaining);
|
||||
const max = Math.max(1, result.tool.max);
|
||||
const ratio = rem / max;
|
||||
const totalSegs = 10;
|
||||
const filled = Math.round(ratio * totalSegs);
|
||||
const bar = Array.from({ length: totalSegs })
|
||||
.map((_, i) => (i < filled ? "█" : "░"))
|
||||
.join("");
|
||||
return `\nDurabilidad: [${bar}] ${rem}/${max}`;
|
||||
};
|
||||
const toolInfo = result.tool?.key
|
||||
? `${formatItemLabel(
|
||||
rewardItems.get(result.tool.key) ?? {
|
||||
key: result.tool.key,
|
||||
name: null,
|
||||
icon: null,
|
||||
},
|
||||
{ fallbackIcon: "🎣" }
|
||||
)}${
|
||||
result.tool.broken
|
||||
? " (rota)"
|
||||
: ` (-${result.tool.durabilityDelta ?? 0} dur.)`
|
||||
}`
|
||||
? (() => {
|
||||
const base = formatItemLabel(
|
||||
rewardItems.get(result.tool.key) ?? {
|
||||
key: result.tool.key,
|
||||
name: null,
|
||||
icon: null,
|
||||
},
|
||||
{ fallbackIcon: "🎣" }
|
||||
);
|
||||
if (result.tool.broken) return `${base} (agotada)${durabilityBar()}`;
|
||||
if (result.tool.brokenInstance)
|
||||
return `${base} (se rompió una instancia, quedan ${result.tool.instancesRemaining}) (-${result.tool.durabilityDelta ?? 0} dur.)${durabilityBar()}`;
|
||||
const multi = result.tool.instancesRemaining && result.tool.instancesRemaining > 1 ? ` (x${result.tool.instancesRemaining})` : "";
|
||||
return `${base}${multi} (-${result.tool.durabilityDelta ?? 0} dur.)${durabilityBar()}`;
|
||||
})()
|
||||
: "—";
|
||||
const combatSummary = (() => {
|
||||
if (!result.combat) return null;
|
||||
const c = result.combat;
|
||||
return `**Combate**\n• Mobs: ${c.mobs.length} | Derrotados: ${c.mobsDefeated}/${result.mobs.length}\n• Daño hecho: ${c.totalDamageDealt} | Daño recibido: ${c.totalDamageTaken}`;
|
||||
})();
|
||||
|
||||
const blocks = [textBlock("# 🎣 Pesca")];
|
||||
|
||||
@@ -137,6 +162,10 @@ export const command: CommandMessage = {
|
||||
blocks.push(textBlock(`**Recompensas**\n${rewardLines}`));
|
||||
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
||||
blocks.push(textBlock(`**Mobs**\n${mobsLines}`));
|
||||
if (combatSummary) {
|
||||
blocks.push(dividerBlock({ divider: false, spacing: 1 }));
|
||||
blocks.push(textBlock(combatSummary));
|
||||
}
|
||||
|
||||
// Añadir metadata del área
|
||||
const metaBlocks = buildAreaMetadataBlocks(area);
|
||||
|
||||
93
src/commands/messages/game/toolinfo.ts
Normal file
93
src/commands/messages/game/toolinfo.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import type { CommandMessage } from "../../../core/types/commands";
|
||||
import type Amayo from "../../../core/client";
|
||||
import { getInventoryEntry } from "../../../game/economy/service";
|
||||
import {
|
||||
buildDisplay,
|
||||
textBlock,
|
||||
dividerBlock,
|
||||
} from "../../../core/lib/componentsV2";
|
||||
import { formatItemLabel, sendDisplayReply } from "./_helpers";
|
||||
|
||||
// Inspecciona la durabilidad de una herramienta (no apilable) mostrando barra.
|
||||
|
||||
function parseJSON<T>(v: unknown): T | null {
|
||||
if (!v || typeof v !== "object") return null;
|
||||
return v as T;
|
||||
}
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: "tool-info",
|
||||
type: "message",
|
||||
aliases: ["toolinfo", "herramienta", "inspectar", "inspeccionar"],
|
||||
cooldown: 3,
|
||||
description: "Muestra la durabilidad restante de una herramienta por su key.",
|
||||
usage: "tool-info <itemKey>",
|
||||
run: async (message, args, _client: Amayo) => {
|
||||
const userId = message.author.id;
|
||||
const guildId = message.guild!.id;
|
||||
const key = args[0];
|
||||
if (!key) {
|
||||
await message.reply(
|
||||
"⚠️ Debes indicar la key del item. Ej: `tool-info tool.pickaxe.basic`"
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { item, entry } = await getInventoryEntry(userId, guildId, key);
|
||||
if (!entry || !item) {
|
||||
await message.reply("❌ No tienes este ítem en tu inventario.");
|
||||
return;
|
||||
}
|
||||
const props = parseJSON<any>(item.props) ?? {};
|
||||
const breakable = props.breakable;
|
||||
if (!breakable || breakable.enabled === false) {
|
||||
await message.reply("ℹ️ Este ítem no tiene durabilidad activa.");
|
||||
return;
|
||||
}
|
||||
if (item.stackable) {
|
||||
await message.reply(`ℹ️ Ítem apilable. Cantidad: ${entry.quantity}`);
|
||||
return;
|
||||
}
|
||||
const state = parseJSON<any>(entry.state) ?? {};
|
||||
const instances: any[] = Array.isArray(state.instances)
|
||||
? state.instances
|
||||
: [];
|
||||
const max = Math.max(1, breakable.maxDurability ?? 1);
|
||||
const label = formatItemLabel(
|
||||
{ key: item.key, name: item.name, icon: item.icon },
|
||||
{ fallbackIcon: "🛠️" }
|
||||
);
|
||||
const renderBar = (cur: number) => {
|
||||
const ratio = cur / max;
|
||||
const totalSegs = 20;
|
||||
const filled = Math.round(ratio * totalSegs);
|
||||
return Array.from({ length: totalSegs })
|
||||
.map((_, i) => (i < filled ? "█" : "░"))
|
||||
.join("");
|
||||
};
|
||||
const durLines = instances.length
|
||||
? instances
|
||||
.map((inst, idx) => {
|
||||
const cur = Math.min(
|
||||
Math.max(0, inst?.durability ?? max),
|
||||
max
|
||||
);
|
||||
return `#${idx + 1} [${renderBar(cur)}] ${cur}/${max}`;
|
||||
})
|
||||
.join("\n")
|
||||
: "(sin instancias)";
|
||||
const blocks = [
|
||||
textBlock("# 🔍 Herramienta"),
|
||||
dividerBlock(),
|
||||
textBlock(`**Item:** ${label}`),
|
||||
textBlock(`Instancias: ${instances.length}`),
|
||||
textBlock(durLines),
|
||||
];
|
||||
const accent = 0x95a5a6;
|
||||
const display = buildDisplay(accent, blocks);
|
||||
await sendDisplayReply(message, display);
|
||||
} catch (e: any) {
|
||||
await message.reply(`❌ No se pudo inspeccionar: ${e?.message ?? e}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user