Viewing old revision of Module:Consumables
You are viewing an old revision of this page from 3/30/2026, 10:28:08 AM.
View latest versionconst Utils = await require("Utils");
const Wiki = await require("Wiki");
const Game = await require("Game");
const Cache = await require("Cache");
const { latest: latestVersion } = await requireData("Versions");
const { TooltipBuilder } = await require("Tooltip_Builder");
const { InfoboxBuilder } = await require("Infobox_Builder");
async function buildConsumableTooltipHtml(itemData, itemId, version = latestVersion) {
const tooltip = new TooltipBuilder(`Consumable:Item:${itemId}`);
const name = itemData.name || itemId;
const description = itemData.description || "";
const sanitizedDescription = description
.replace(/\n/g, " ")
.replace(/<color=(.*?)>(.*?)<\/color>/g,
"<span style='color:$1;'>$2</span>");
tooltip.addLine(
`<div style="display:flex;justify-content:space-between;">` +
`<div>'''${name}'''</div>` +
`<div>[[File:${itemData.id}_icon.png|26px|inline|alt=${name}]]</div>` +
`</div>`
);
if (sanitizedDescription) {
tooltip.addLine(`<div>${sanitizedDescription.replace(/\n/g, "<br>")}</div>`);
}
tooltip.addLine(`<div>Rarity: ${Wiki.wikiRarity(itemData.rarity)}</div>`);
tooltip.addLine(`<div>Stack Size: ${itemData.stack?.max ?? "N/A"}</div>`);
switch (itemData.category) {
case "classtome":
const { data: classData } = await Utils.resolveData("Classes", false, version);
const classInfo = Utils.findByProperty(classData, "guid", itemData.unlocks?.classGuid);
if (itemData.unlocks?.tierIndex) {
tooltip.addLine(`<div>Unlocks Class: ${classInfo?.name ?? itemData.unlocks?.classGuid ?? "N/A"} (Tier: ${classInfo?.tiers[itemData.unlocks.tierIndex - 1]?.name ?? "N/A"})</div>`);
} else {
tooltip.addLine(`<div>Unlocks Class: ${classInfo?.name ?? itemData.unlocks?.classGuid ?? "N/A"}</div>`);
}
tooltip.addLine(`<div>Level Requirement: ${itemData.levelRequirement ?? "N/A"}</div>`);
break;
case "dye":
tooltip.addLine(`<div>Buy Price: ${itemData.buyPrice} Crowns</div>`);
tooltip.addLine(`<div>Sell Price: ${Game.getSellPrice(itemData.buyPrice)} Crowns</div>`);
tooltip.addLine(`<span>Dye Color:</span><span class="dye-box" style="background-color: ${itemId.replace("_dye", "")};"></span>`);
break;
case "skillscroll":
const { data: skillData } = await Utils.resolveData("Skills", false, version);
const skillInfo = Utils.findByProperty(skillData, "guid", itemData.unlocks?.skillGuid);
tooltip.addLine(`<div>Buy Price: ${itemData.buyPrice} Crowns</div>`);
tooltip.addLine(`<div>Sell Price: ${Game.getSellPrice(itemData.buyPrice)} Crowns</div>`);
tooltip.addLine(`<div>Unlocks Skill: ${skillInfo?.name ?? itemData.unlocks?.skillGuid ?? "N/A"}</div>`);
tooltip.addLine(`<div>Level Requirement: ${itemData.levelRequirement ?? "N/A"}</div>`);
break;
case "instant":
tooltip.addLine(`<div>Buy Price: ${itemData.buyPrice} Crowns</div>`);
tooltip.addLine(`<div>Sell Price: ${Game.getSellPrice(itemData.buyPrice)} Crowns</div>`);
if (itemData.effects.instant?.health) {
tooltip.addLine(`<div>Restores Health: ${itemData.effects.instant.health}</div>`);
}
if (itemData.effects.instant?.mana) {
tooltip.addLine(`<div>Restores Mana: ${itemData.effects.instant.mana}</div>`);
}
if (itemData.effects.instant?.stamina) {
tooltip.addLine(`<div>Restores Stamina: ${itemData.effects.instant.stamina}</div>`);
}
if (itemData.effects.instant?.experience) {
tooltip.addLine(`<div>Restores Experience: ${itemData.effects.instant.experience}</div>`);
}
if (itemData.effects.instant?.resetSkillPoints) {
tooltip.addLine(`<div>Resets Skill Points</div>`);
}
if (itemData.effects.instant?.resetAttributePoints) {
tooltip.addLine(`<div>Resets Attribute Points</div>`);
}
break;
case "status":
tooltip.addLine(`<div>Buy Price: ${itemData.buyPrice} Crowns</div>`);
tooltip.addLine(`<div>Sell Price: ${Game.getSellPrice(itemData.buyPrice)} Crowns</div>`);
if (itemData.effects.statusCondition?.conditionGuid) {
const { data: conditionData } = await Utils.resolveData("Conditions", false, version);
const conditionInfo = Utils.findByProperty(conditionData, "guid", itemData.effects.statusCondition.conditionGuid);
tooltip.addLine(`<div>Applies Status Condition: ${conditionInfo?.name ?? itemData.effects.statusCondition.conditionGuid ?? "N/A"}</div>`);
}
break;
case "tool":
tooltip.addLine(`<div>Buy Price: ${itemData.buyPrice} Crowns</div>`);
tooltip.addLine(`<div>Sell Price: ${Game.getSellPrice(itemData.buyPrice)} Crowns</div>`);
break;
}
return tooltip.build();
}
async function wikiTable(props) {
const args = Utils.resolveArgs(props);
const selectedVersion = args["version"] || args[0];
const { data, version } = await Utils.resolveData("Consumables", false, selectedVersion);
if (Utils.isModuleEmpty(data)) {
return `<div id="Consumables-wikiTable">⚠️ Consumable data is unavailable for version ${version}.${Wiki.versionSelector(version, "Consumables", "wikiTable")}</div>`;
}
const sortedData = Utils.sortObjectByKey(data);
let table = "";
let tooltips = "";
table += `{| class="wiki-table" style="width:auto;"\n`;
table += `! colspan="5" | :Version: ${Wiki.versionSelector(version, "Consumables", "wikiTable")}\n`;
table += `|-\n`;
table += `! Name !! Rarity !! Max Stack !! Buy Price !! Sell Price\n`;
for (const id in sortedData) {
const item = sortedData[id];
const itemId = item.id || id;
table += `|-\n`;
const tooltipId = `Consumable:Item:${itemId}`;
table += `| <span data-tooltip-id="${tooltipId}">${availablePages.has(`File:${item.id}_icon.png`) ? `[[File:${item.id}_icon.png|16px|inline|alt=${item.name}|link=Consumables/${item.name}]]` : ""}[[Consumables/${item.name}|${item.name}]]</span>\n`;
table += `| ${Wiki.wikiRarity(item.rarity)}\n`;
table += `| ${item.stack?.max ?? "N/A"}\n`;
if (item.buyPrice != undefined) {
table += `| ${item.buyPrice} Crowns\n`;
table += `| ${Game.getSellPrice(item.buyPrice)} Crowns\n`;
} else {
table += `| N/A\n`;
table += `| N/A\n`;
}
if (!Cache.has("ConsumableTooltips", tooltipId)) {
Cache.set("ConsumableTooltips", tooltipId, await buildConsumableTooltipHtml(item, itemId, version));
tooltips += Cache.get("ConsumableTooltips", tooltipId);
}
}
table += `|}\n`;
return `<div id="Consumables-wikiTable">${table}${tooltips}</div>`;
}
async function wikiNavbox() {
const { data, version } = await Utils.resolveData("Consumables", false);
if (Utils.isModuleEmpty(data)) {
return `<div id="Consumables-wikiNavbox">⚠️ Consumable data is unavailable for version ${version}.${Wiki.versionSelector(version, "Consumables", "wikiNavbox")}</div>`;
}
const sortedData = Utils.sortObjectByKey(data);
let navbox = "";
let tooltips = "";
const versionTooltip = new TooltipBuilder(`Consumables:Version:${version.replace(/\./g, '_')}`);
versionTooltip.addLine(
`Data version: '''${version}''' <span class="${version === latestVersion ? 'latest' : 'outdated'}">(${version === latestVersion ? 'latest' : 'outdated'})</span>`
);
navbox += `{| class="wiki-table navbox"\n`;
navbox += `! :Version: ${Wiki.versionCode(version, versionTooltip)}\n`;
navbox += `|-\n`;
navbox += `! Consumables Navigation\n`;
navbox += `|-\n| `;
const itemLinks = [];
for (const id in sortedData) {
const item = sortedData[id];
const itemId = item.id || id;
const tooltipId = `Consumable:Item:${itemId}`;
itemLinks.push(`<span data-tooltip-id="${tooltipId}">${availablePages.has(`File:${item.id}_icon.png`) ? `[[File:${item.id}_icon.png|16px|inline|alt=${item.name}|link=Consumables/${item.name}]]` : ""}[[Consumables/${item.name}|${item.name}]]</span>`);
if (!Cache.has("ConsumableTooltips", tooltipId)) {
Cache.set("ConsumableTooltips", tooltipId, await buildConsumableTooltipHtml(item, itemId, version));
tooltips += Cache.get("ConsumableTooltips", tooltipId);
}
}
navbox += itemLinks.join(" • ") + `\n`;
navbox += `|}\n`;
return `<div id="Consumables-wikiNavbox">${navbox}${tooltips}</div>`;
}
async function wikiTooltip(props) {
const args = Utils.resolveArgs(props);
const itemArg = args["item"] || args[0];
if (!itemArg) return "";
const itemId = itemArg.toLowerCase().replace(/\s+|\'/g, '_').split("/").pop();
const tooltipId = `Consumable:Item:${itemId}`;
if (Cache.has("ConsumableTooltips", tooltipId)) {
return Cache.get("ConsumableTooltips", tooltipId);
}
const { data } = await Utils.resolveData("Consumables", false);
if (Utils.isModuleEmpty(data)) {
return `⚠️ Consumable data is unavailable.`;
}
const itemData = data[itemId];
if (!itemData) {
return `⚠️ Consumable '${itemId}' not found.`;
}
const tooltipHtml = await buildConsumableTooltipHtml(itemData, itemId);
const linkHtml =
`<span data-tooltip-id="${tooltipId}">` +
`[[Consumables/${itemData.name}|${itemData.name}]]` +
`</span>`;
const result = linkHtml + tooltipHtml;
Cache.set("ConsumableTooltips", tooltipId, result);
return result;
}
async function wikiInfobox(props) {
const args = Utils.resolveArgs(props);
const itemArg = args["item"] || args[0];
const selectedVersion = args["version"] || args[1];
const { data, version } = await Utils.resolveData("Consumables", false, selectedVersion);
if (Utils.isModuleEmpty(data)) {
return `⚠️ Consumable data is unavailable for version ${selectedVersion}.${Wiki.versionSelector(version, "Consumables", "wikiInfobox", [itemArg])}`;
}
const itemId = itemArg?.toLowerCase().replace(/\s+|'/g, '_').split("/").pop();
const itemData = data[itemId];
if (!itemData) {
return `⚠️ Consumable '${itemArg}' not found in data version ${version}.`;
}
const infobox = new InfoboxBuilder();
infobox.setTitle((itemData.name || itemArg) + Wiki.versionSelector(version, "Consumables", "wikiInfobox", [itemArg]));
const humanize = (value) =>
value?.toString().replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
const subtitleParts = ["Consumable"];
if (itemData.category) {
subtitleParts.push(humanize(itemData.category));
}
const subtitle = subtitleParts.join(" · ");
infobox.setSubtitle(subtitle);
infobox.setRarity(itemData.rarity);
infobox.setImage(`[[File:${itemData.id || itemId}_icon.png|32px|${itemData.name || itemArg}]]`);
if (itemData.description) {
infobox.setDescription(itemData.description
.replace(/\n{2,}/g, "<br>")
.replace(/<color=(.*?)>(.*?)<\/color>/g, "<span style='color:$1;'>$2</span>"));
}
const general = infobox.startSection("General");
if (itemData.category) {
general.addRow("Category", humanize(itemData.category));
}
if (itemData.stack?.max) {
general.addRow("Max Stack Size", itemData.stack.max);
}
if (itemData.levelRequirement != null) {
general.addRow("Level Requirement", itemData.levelRequirement);
}
switch (itemData.category) {
case "classtome": {
const { data: classData } = await Utils.resolveData("Classes", false, version);
const classInfo = Utils.findByProperty(classData, "guid", itemData.unlocks?.classGuid);
const className = classInfo?.name || itemData.unlocks?.classGuid || "N/A";
const tierName = itemData.unlocks?.tierIndex ? classInfo?.tiers?.[itemData.unlocks.tierIndex - 1]?.name : null;
general.addRow("Unlocks", tierName ? `${className} (Tier: ${tierName})` : className);
break;
}
case "skillscroll": {
const { data: skillData } = await Utils.resolveData("Skills", false, version);
const skillInfo = Utils.findByProperty(skillData, "guid", itemData.unlocks?.skillGuid);
general.addRow("Unlocks", skillInfo?.name || itemData.unlocks?.skillGuid || "N/A");
break;
}
case "dye": {
const colorCode = itemId.replace(/_dye$/i, "");
general.addRow("Dye Color", `<span style="display:inline-block;width:18px;height:18px;border:1px solid #999;background-color:${colorCode};vertical-align:middle;margin-right:6px"></span>${colorCode}`);
break;
}
case "instant": {
if (itemData.effects?.instant) {
const instant = itemData.effects.instant;
if (instant.health != null) general.addRow("Restores Health", instant.health);
if (instant.mana != null) general.addRow("Restores Mana", instant.mana);
if (instant.stamina != null) general.addRow("Restores Stamina", instant.stamina);
if (instant.experience != null) general.addRow("Gives Experience", instant.experience);
if (instant.resetSkillPoints) general.addRow("Resets", "Skill Points");
if (instant.resetAttributePoints) general.addRow("Resets", "Attribute Points");
}
break;
}
case "status": {
if (itemData.effects?.statusCondition?.conditionGuid) {
const { data: conditionData } = await Utils.resolveData("Conditions", false, version);
const conditionInfo = Utils.findByProperty(conditionData, "guid", itemData.effects.statusCondition.conditionGuid);
general.addRow("Applies Status", conditionInfo?.name || itemData.effects.statusCondition.conditionGuid);
}
break;
}
case "tool": {
general.addRow("Tool Type", humanize(itemData.subtype || "Tool"));
break;
}
}
if (itemData.buyPrice != null) {
const priceSection = infobox.startSection("Price");
priceSection.addRow("Buy Price", `${itemData.buyPrice} Crowns`);
priceSection.addRow("Sell Price", `${Game.getSellPrice(itemData.buyPrice)} Crowns`);
}
const itemRarityData = Game.getRarityLabel(itemData.rarity);
const title = itemData.name || itemArg;
const buyPrice = itemData.buyPrice != null ? `${itemData.buyPrice} Crowns` : "N/A";
const sellPrice = itemData.buyPrice != null ? `${Game.getSellPrice(itemData.buyPrice)} Crowns` : "N/A";
const cleanFooter = (itemData.description || "").replace(/\n+/g, " ").replace(/<.*?>/g, "").trim().slice(0, 160) || title;
const ogImageBlock = `{{#og_image:
| layout = card
| title = ${title}
| subtitle = ${subtitle}
| image = File:${itemId}_icon.png
| primary = ${humanize(itemData.category) || "Consumable"}
| secondary = Max Stack: ${itemData.stack?.max || "N/A"}, Buy Price: ${buyPrice}, Sell Price: ${sellPrice}
| footer = ${cleanFooter}
| accent = ${itemRarityData.color}
| branding = ATLYSS TechPendium
}}`;
return `{{#og:|type=summary_large_image|title=${title}}}${ogImageBlock}<div id="Consumables-wikiInfobox" style="float: ${infobox.float || "right"}">${infobox.build()}</div>`;
}
exports = {
wikiTable,
wikiNavbox,
wikiTooltip,
wikiInfobox
}