aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/moulberry-bush/neuRepo.ts153
-rw-r--r--src/lib/common/util/Minecraft.ts234
-rw-r--r--src/lib/common/util/Minecraft_Test.ts86
3 files changed, 361 insertions, 112 deletions
diff --git a/src/commands/moulberry-bush/neuRepo.ts b/src/commands/moulberry-bush/neuRepo.ts
index 24f83ad..9d76810 100644
--- a/src/commands/moulberry-bush/neuRepo.ts
+++ b/src/commands/moulberry-bush/neuRepo.ts
@@ -1,5 +1,16 @@
import { BushCommand, clientSendAndPermCheck, type ArgType, type CommandMessage, type SlashMessage } from '#lib';
-import { ApplicationCommandOptionType, AutocompleteInteraction, CacheType, PermissionFlagsBits } from 'discord.js';
+import canvas from 'canvas';
+import {
+ ApplicationCommandOptionType,
+ AttachmentBuilder,
+ AutocompleteInteraction,
+ CacheType,
+ PermissionFlagsBits
+} from 'discord.js';
+import { dirname, join } from 'path';
+import tinycolor from 'tinycolor2';
+import { fileURLToPath } from 'url';
+import { formattingInfo, RawNeuItem } from '../../lib/common/util/Minecraft.js';
export default class NeuRepoCommand extends BushCommand {
public static items: { name: string; id: string }[] = [];
@@ -34,14 +45,148 @@ export default class NeuRepoCommand extends BushCommand {
],
slash: true,
clientPermissions: (m) => clientSendAndPermCheck(m, [PermissionFlagsBits.EmbedLinks], true),
- userPermissions: []
+ userPermissions: [],
+ ownerOnly: true
});
}
public override async exec(
message: CommandMessage | SlashMessage,
args: { item: ArgType<'string'> /* dangerous: ArgType<'flag'> */ }
- ) {}
+ ) {
+ const itemPath = join(import.meta.url, '..', '..', '..', '..', '..', 'neu-item-repo-dangerous', 'items', `${args.item}.json`);
+ const item = (await import(itemPath, { assert: { type: 'json' } })).default as RawNeuItem;
- public override async autocomplete(interaction: AutocompleteInteraction<CacheType>) {}
+ const toolTip = this.toolTip(item);
+
+ return message.util.reply({
+ files: [new AttachmentBuilder(toolTip, { name: `${item.internalname}.png`, description: item.displayname })]
+ });
+ }
+
+ public toolTip(item: RawNeuItem): Buffer {
+ canvas.registerFont(join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', '..', 'assets', 'Faithful.ttf'), {
+ family: 'Faithful'
+ });
+
+ const background = '#100010';
+
+ const width = 250;
+ const height = 250;
+ const scale = 10;
+
+ const itemRender = canvas.createCanvas(width, height),
+ ctx = itemRender.getContext('2d');
+
+ ctx.globalAlpha = 0.94;
+ ctx.fillStyle = background;
+
+ // top outside
+ ctx.fillRect(scale, 0, width - 2 * scale, scale);
+
+ // bottom outside
+ ctx.fillRect(scale, height - scale, width - 2 * scale, scale);
+
+ // left outside
+ ctx.fillRect(0, scale, scale, height - 2 * scale);
+
+ // right outside
+ ctx.fillRect(width - scale, scale, scale, height - 2 * scale);
+
+ // middle
+ ctx.fillRect(2 * scale, 2 * scale, width - 4 * scale, height - 4 * scale);
+
+ ctx.globalAlpha = 0.78;
+
+ const borderColorStart = parseInt(new tinycolor(this.getPrimaryColour(item.displayname)).toHex(), 16);
+ const borderColorEnd = ((borderColorStart & 0xfefefe) >> 1) | (borderColorStart & 0xff000000);
+
+ const borderColorStartStr = `#${borderColorStart.toString(16)}`;
+ const borderColorEndStr = `#${borderColorEnd.toString(16)}`;
+
+ ctx.fillStyle = borderColorStartStr;
+
+ // top highlight
+ ctx.fillRect(scale, scale, width - 2 * scale, scale);
+
+ // left highlight
+ ctx.fillRect(scale, 2 * scale, scale, height - 3 * scale);
+
+ // bottom highlight
+ {
+ const x = 2 * scale,
+ y = height - 2 * scale,
+ w = width - 3 * scale,
+ h = scale;
+ const gradient = ctx.createLinearGradient(x, y, x + w, y + h);
+ gradient.addColorStop(0, borderColorStartStr);
+ gradient.addColorStop(1, borderColorEndStr);
+ ctx.fillStyle = gradient;
+
+ ctx.fillRect(x, y, w, h);
+ }
+
+ // right highlight
+ {
+ const x = width - 2 * scale,
+ y = 2 * scale,
+ w = scale,
+ h = height - 4 * scale;
+ const gradient = ctx.createLinearGradient(x, y, x + w, y + h);
+ gradient.addColorStop(0, borderColorStartStr);
+ gradient.addColorStop(1, borderColorEndStr);
+ ctx.fillStyle = gradient;
+
+ ctx.fillRect(x, y, w, h);
+ }
+
+ item.displayname.split('');
+
+ return itemRender.toBuffer();
+ }
+
+ // stolen from NEU and modified
+ public getPrimaryColourCode(displayname: string): code {
+ let lastColourCode = -99;
+ let currentColour = 0;
+ const mostCommon = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ for (let i = 0; i < displayname.length; i++) {
+ const c = displayname.charAt(i);
+ if (c === '\u00A7') {
+ lastColourCode = i;
+ } else if (lastColourCode === i - 1) {
+ const colIndex = '0123456789abcdef'.indexOf(c);
+ if (colIndex >= 0) {
+ currentColour = colIndex;
+ } else {
+ currentColour = 0;
+ }
+ } else if ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.indexOf(c) >= 0) {
+ if (currentColour > 0) {
+ mostCommon[currentColour] = mostCommon[currentColour]++;
+ }
+ }
+ }
+ let mostCommonCount = 0;
+ for (let index = 0; index < mostCommon.length; index++) {
+ if (mostCommon[index] > mostCommonCount) {
+ mostCommonCount = mostCommon[index];
+ currentColour = index;
+ }
+ }
+
+ return <code>'0123456789abcdef'.charAt(currentColour);
+ }
+
+ // stolen from NEU and modified
+ public getPrimaryColour(displayname: string) {
+ const code = this.getPrimaryColourCode(displayname);
+ return formattingInfo[`§${code}`].foregroundDarker;
+ }
+
+ public override async autocomplete(interaction: AutocompleteInteraction<CacheType>) {
+ return interaction.respond([{ name: 'Blazetekk™ Ham Radio', value: 'BLAZETEKK_HAM_RADIO' }]);
+ }
}
+
+type code = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
diff --git a/src/lib/common/util/Minecraft.ts b/src/lib/common/util/Minecraft.ts
index b4b013f..a12ebf2 100644
--- a/src/lib/common/util/Minecraft.ts
+++ b/src/lib/common/util/Minecraft.ts
@@ -1,6 +1,5 @@
import { Byte, Int, parse } from '@ironm00n/nbt-ts';
import { BitField } from 'discord.js';
-import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
@@ -34,22 +33,118 @@ export enum FormattingCodes {
// https://minecraft.fandom.com/wiki/Formatting_codes
export const formattingInfo = {
- [FormattingCodes.Black]: { foreground: '#000000', background: '#000000', ansi: '\u001b[0;30m' },
- [FormattingCodes.DarkBlue]: { foreground: '#0000AA', background: '#00002A', ansi: '\u001b[0;34m' },
- [FormattingCodes.DarkGreen]: { foreground: '#00AA00', background: '#002A00', ansi: '\u001b[0;32m' },
- [FormattingCodes.DarkAqua]: { foreground: '#00AAAA', background: '#002A2A', ansi: '\u001b[0;36m' },
- [FormattingCodes.DarkRed]: { foreground: '#AA0000', background: '#2A0000', ansi: '\u001b[0;31m' },
- [FormattingCodes.DarkPurple]: { foreground: '#AA00AA', background: '#2A002A', ansi: '\u001b[0;35m' },
- [FormattingCodes.Gold]: { foreground: '#FFAA00', background: '#2A2A00', ansi: '\u001b[0;33m' },
- [FormattingCodes.Gray]: { foreground: '#AAAAAA', background: '#2A2A2A', ansi: '\u001b[0;37m' },
- [FormattingCodes.DarkGray]: { foreground: '#555555', background: '#151515', ansi: '\u001b[0;90m' },
- [FormattingCodes.Blue]: { foreground: '#5555FF', background: '#15153F', ansi: '\u001b[0;94m' },
- [FormattingCodes.Green]: { foreground: '#55FF55', background: '#153F15', ansi: '\u001b[0;92m' },
- [FormattingCodes.Aqua]: { foreground: '#55FFFF', background: '#153F3F', ansi: '\u001b[0;96m' },
- [FormattingCodes.Red]: { foreground: '#FF5555', background: '#3F1515', ansi: '\u001b[0;91m' },
- [FormattingCodes.LightPurple]: { foreground: '#FF55FF', background: '#3F153F', ansi: '\u001b[0;95m' },
- [FormattingCodes.Yellow]: { foreground: '#FFFF55', background: '#3F3F15', ansi: '\u001b[0;93m' },
- [FormattingCodes.White]: { foreground: '#FFFFFF', background: '#3F3F3F', ansi: '\u001b[0;97m' },
+ [FormattingCodes.Black]: {
+ foreground: 'rgb(0, 0, 0)',
+ foregroundDarker: 'rgb(0, 0, 0)',
+ background: 'rgb(0, 0, 0)',
+ backgroundDarker: 'rgb(0, 0, 0)',
+ ansi: '\u001b[0;30m'
+ },
+ [FormattingCodes.DarkBlue]: {
+ foreground: 'rgb(0, 0, 170)',
+ foregroundDarker: 'rgb(0, 0, 118)',
+ background: 'rgb(0, 0, 42)',
+ backgroundDarker: 'rgb(0, 0, 29)',
+ ansi: '\u001b[0;34m'
+ },
+ [FormattingCodes.DarkGreen]: {
+ foreground: 'rgb(0, 170, 0)',
+ foregroundDarker: 'rgb(0, 118, 0)',
+ background: 'rgb(0, 42, 0)',
+ backgroundDarker: 'rgb(0, 29, 0)',
+ ansi: '\u001b[0;32m'
+ },
+ [FormattingCodes.DarkAqua]: {
+ foreground: 'rgb(0, 170, 170)',
+ foregroundDarker: 'rgb(0, 118, 118)',
+ background: 'rgb(0, 42, 42)',
+ backgroundDarker: 'rgb(0, 29, 29)',
+ ansi: '\u001b[0;36m'
+ },
+ [FormattingCodes.DarkRed]: {
+ foreground: 'rgb(170, 0, 0)',
+ foregroundDarker: 'rgb(118, 0, 0)',
+ background: 'rgb(42, 0, 0)',
+ backgroundDarker: 'rgb(29, 0, 0)',
+ ansi: '\u001b[0;31m'
+ },
+ [FormattingCodes.DarkPurple]: {
+ foreground: 'rgb(170, 0, 170)',
+ foregroundDarker: 'rgb(118, 0, 118)',
+ background: 'rgb(42, 0, 42)',
+ backgroundDarker: 'rgb(29, 0, 29)',
+ ansi: '\u001b[0;35m'
+ },
+ [FormattingCodes.Gold]: {
+ foreground: 'rgb(255, 170, 0)',
+ foregroundDarker: 'rgb(178, 118, 0)',
+ background: 'rgb(42, 42, 0)',
+ backgroundDarker: 'rgb(29, 29, 0)',
+ ansi: '\u001b[0;33m'
+ },
+ [FormattingCodes.Gray]: {
+ foreground: 'rgb(170, 170, 170)',
+ foregroundDarker: 'rgb(118, 118, 118)',
+ background: 'rgb(42, 42, 42)',
+ backgroundDarker: 'rgb(29, 29, 29)',
+ ansi: '\u001b[0;37m'
+ },
+ [FormattingCodes.DarkGray]: {
+ foreground: 'rgb(85, 85, 85)',
+ foregroundDarker: 'rgb(59, 59, 59)',
+ background: 'rgb(21, 21, 21)',
+ backgroundDarker: 'rgb(14, 14, 14)',
+ ansi: '\u001b[0;90m'
+ },
+ [FormattingCodes.Blue]: {
+ foreground: 'rgb(85, 85, 255)',
+ foregroundDarker: 'rgb(59, 59, 178)',
+ background: 'rgb(21, 21, 63)',
+ backgroundDarker: 'rgb(14, 14, 44)',
+ ansi: '\u001b[0;94m'
+ },
+ [FormattingCodes.Green]: {
+ foreground: 'rgb(85, 255, 85)',
+ foregroundDarker: 'rgb(59, 178, 59)',
+ background: 'rgb(21, 63, 21)',
+ backgroundDarker: 'rgb(14, 44, 14)',
+ ansi: '\u001b[0;92m'
+ },
+ [FormattingCodes.Aqua]: {
+ foreground: 'rgb(85, 255, 255)',
+ foregroundDarker: 'rgb(59, 178, 178)',
+ background: 'rgb(21, 63, 63)',
+ backgroundDarker: 'rgb(14, 44, 44)',
+ ansi: '\u001b[0;96m'
+ },
+ [FormattingCodes.Red]: {
+ foreground: 'rgb(255, 85, 85)',
+ foregroundDarker: 'rgb(178, 59, 59)',
+ background: 'rgb(63, 21, 21)',
+ backgroundDarker: 'rgb(44, 14, 14)',
+ ansi: '\u001b[0;91m'
+ },
+ [FormattingCodes.LightPurple]: {
+ foreground: 'rgb(255, 85, 255)',
+ foregroundDarker: 'rgb(178, 59, 178)',
+ background: 'rgb(63, 21, 63)',
+ backgroundDarker: 'rgb(44, 14, 44)',
+ ansi: '\u001b[0;95m'
+ },
+ [FormattingCodes.Yellow]: {
+ foreground: 'rgb(255, 255, 85)',
+ foregroundDarker: 'rgb(178, 178, 59)',
+ background: 'rgb(63, 63, 21)',
+ backgroundDarker: 'rgb(44, 44, 14)',
+ ansi: '\u001b[0;93m'
+ },
+ [FormattingCodes.White]: {
+ foreground: 'rgb(255, 255, 255)',
+ foregroundDarker: 'rgb(178, 178, 178)',
+ background: 'rgb(63, 63, 63)',
+ backgroundDarker: 'rgb(44, 44, 44)',
+ ansi: '\u001b[0;97m'
+ },
[FormattingCodes.Obfuscated]: { ansi: '\u001b[8m' },
[FormattingCodes.Bold]: { ansi: '\u001b[1m' },
@@ -68,7 +163,7 @@ export type SbRecipe = {
};
export type InfoType = 'WIKI_URL' | '';
-type Slayer = `${'WOLF' | 'BLAZE' | 'EMAN'}_${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
+export type Slayer = `${'WOLF' | 'BLAZE' | 'EMAN'}_${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
export interface RawNeuItem {
itemid: McItemId;
@@ -128,87 +223,8 @@ export function removeMCFormatting(str: string) {
}
const repo = path.join(__dirname, '..', '..', '..', '..', '..', 'neu-item-repo-dangerous');
-const itemPath = path.join(repo, 'items');
-const items = await fs.readdir(itemPath);
-
-// for (let i = 0; i < 5; i++) {
-for (const path_ of items) {
- // const randomItem = items[Math.floor(Math.random() * items.length)];
- // console.log(randomItem);
- const item = (await import(path.join(itemPath, /* randomItem */ path_), { assert: { type: 'json' } })).default as RawNeuItem;
- if (/.*?((_MONSTER)|(_NPC)|(_ANIMAL)|(_MINIBOSS)|(_BOSS)|(_SC))$/.test(item.internalname)) continue;
- if (!/.*?;[0-5]$/.test(item.internalname)) continue;
- /* console.log(path_);
- console.dir(item, { depth: Infinity }); */
-
- /* console.log('==========='); */
- const nbt = parse(item.nbttag) as NbtTag;
-
- if (nbt?.SkullOwner?.Properties?.textures?.[0]?.Value) {
- nbt.SkullOwner.Properties.textures[0].Value = parse(
- Buffer.from(nbt.SkullOwner.Properties.textures[0].Value, 'base64').toString('utf-8')
- ) as string;
- }
-
- if (nbt.ExtraAttributes?.petInfo) {
- nbt.ExtraAttributes.petInfo = JSON.parse(nbt.ExtraAttributes.petInfo as any as string);
- }
- // delete nbt.display?.Lore;
-
- console.dir(nbt, { depth: Infinity });
- console.log('===========');
-
- /* if (nbt?.display && nbt.display.Name !== item.displayname)
- console.log(`${path_} display name mismatch: ${mcToAnsi(nbt.display.Name)} != ${mcToAnsi(item.displayname)}`);
-
- if (nbt?.ExtraAttributes && nbt?.ExtraAttributes.id !== item.internalname)
- console.log(`${path_} internal name mismatch: ${mcToAnsi(nbt?.ExtraAttributes.id)} != ${mcToAnsi(item.internalname)}`); */
-
- /* console.log('===========');
-
- console.log(mcToAnsi(item.displayname));
- console.log(item.lore.map((l) => mcToAnsi(l)).join('\n')); */
-
- /* const keys = [
- 'itemid',
- 'displayname',
- 'nbttag',
- 'damage',
- 'lore',
- 'recipe',
- 'internalname',
- 'modver',
- 'infoType',
- 'info',
- 'crafttext',
- 'vanilla',
- 'useneucraft',
- 'slayer_req',
- 'clickcommand',
- 'x',
- 'y',
- 'z',
- 'island',
- 'recipes',
- 'parent',
- 'noseal'
- ];
-
- Object.keys(item).forEach((k) => {
- if (!keys.includes(k)) throw new Error(`Unknown key: ${k}`);
- });
-
- if (
- 'slayer_req' in item &&
- !new Array(10).flatMap((_, i) => ['WOLF', 'BLAZE', 'EMAN'].map((e) => e + (i + 1)).includes(item.slayer_req!))
- )
- throw new Error(`Unknown slayer req: ${item.slayer_req!}`); */
-
- /* console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-'); */
-}
-
-interface NbtTag {
+export interface NbtTag {
overrideMeta?: Byte;
Unbreakable?: Int;
ench?: string[];
@@ -218,22 +234,22 @@ interface NbtTag {
ExtraAttributes?: ExtraAttributes;
}
-interface SkullOwner {
+export interface SkullOwner {
Id?: string;
Properties?: {
textures?: { Value?: string }[];
};
}
-interface NbtTagDisplay {
+export interface NbtTagDisplay {
Lore?: string[];
color?: Int;
Name?: string;
}
-type RuneId = string;
+export type RuneId = string;
-interface ExtraAttributes {
+export interface ExtraAttributes {
originTag?: Origin;
id?: string;
generator_tier?: Int;
@@ -244,7 +260,7 @@ interface ExtraAttributes {
petInfo?: PetInfo;
}
-interface PetInfo {
+export interface PetInfo {
type: 'ZOMBIE';
active: boolean;
exp: number;
@@ -253,7 +269,7 @@ interface PetInfo {
candyUsed: number;
}
-type Origin = 'SHOP_PURCHASE';
+export type Origin = 'SHOP_PURCHASE';
const neuConstantsPath = path.join(repo, 'constants');
const neuPetsPath = path.join(neuConstantsPath, 'pets.json');
@@ -261,14 +277,14 @@ const neuPets = (await import(neuPetsPath, { assert: { type: 'json' } })) as Pet
const neuPetNumsPath = path.join(neuConstantsPath, 'petnums.json');
const neuPetNums = (await import(neuPetNumsPath, { assert: { type: 'json' } })) as PetNums;
-interface PetsConstants {
+export interface PetsConstants {
pet_rarity_offset: Record<string, number>;
pet_levels: number[];
custom_pet_leveling: Record<string, { type: number; pet_levels: number[]; max_level: number }>;
pet_types: Record<string, string>;
}
-interface PetNums {
+export interface PetNums {
[key: string]: {
[key: string]: {
'1': {
@@ -284,7 +300,7 @@ interface PetNums {
};
}
-class NeuItem {
+export class NeuItem {
public itemId: McItemId;
public displayName: string;
public nbtTag: NbtTag;
@@ -315,6 +331,8 @@ class NeuItem {
const curve = petInfoTier?.stats_levelling_curve?.split(';');
+ // todo: finish copying from neu
+
const minStatsLevel = parseInt(curve?.[0] ?? '0');
const maxStatsLevel = parseInt(curve?.[0] ?? '100');
@@ -323,7 +341,7 @@ class NeuItem {
}
}
-function mcToAnsi(str: string) {
+export function mcToAnsi(str: string) {
for (const format in formattingInfo) {
str = str.replaceAll(format, formattingInfo[format as keyof typeof formattingInfo].ansi);
}
diff --git a/src/lib/common/util/Minecraft_Test.ts b/src/lib/common/util/Minecraft_Test.ts
new file mode 100644
index 0000000..26ca648
--- /dev/null
+++ b/src/lib/common/util/Minecraft_Test.ts
@@ -0,0 +1,86 @@
+import fs from 'fs/promises';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import { mcToAnsi, RawNeuItem } from './Minecraft.js';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const repo = path.join(__dirname, '..', '..', '..', '..', '..', 'neu-item-repo-dangerous');
+const itemPath = path.join(repo, 'items');
+const items = await fs.readdir(itemPath);
+
+// for (let i = 0; i < 5; i++) {
+for (const path_ of items) {
+ // const randomItem = items[Math.floor(Math.random() * items.length)];
+ // console.log(randomItem);
+ const item = (await import(path.join(itemPath, /* randomItem */ path_), { assert: { type: 'json' } })).default as RawNeuItem;
+ if (/.*?((_MONSTER)|(_NPC)|(_ANIMAL)|(_MINIBOSS)|(_BOSS)|(_SC))$/.test(item.internalname)) continue;
+ if (!/.*?;[0-5]$/.test(item.internalname)) continue;
+ /* console.log(path_);
+ console.dir(item, { depth: Infinity }); */
+
+ /* console.log('==========='); */
+ // const nbt = parse(item.nbttag) as NbtTag;
+
+ // if (nbt?.SkullOwner?.Properties?.textures?.[0]?.Value) {
+ // nbt.SkullOwner.Properties.textures[0].Value = parse(
+ // Buffer.from(nbt.SkullOwner.Properties.textures[0].Value, 'base64').toString('utf-8')
+ // ) as string;
+ // }
+
+ // if (nbt.ExtraAttributes?.petInfo) {
+ // nbt.ExtraAttributes.petInfo = JSON.parse(nbt.ExtraAttributes.petInfo as any as string);
+ // }
+
+ // delete nbt.display?.Lore;
+
+ // console.dir(nbt, { depth: Infinity });
+ // console.log('===========');
+
+ /* if (nbt?.display && nbt.display.Name !== item.displayname)
+ console.log(`${path_} display name mismatch: ${mcToAnsi(nbt.display.Name)} != ${mcToAnsi(item.displayname)}`);
+
+ if (nbt?.ExtraAttributes && nbt?.ExtraAttributes.id !== item.internalname)
+ console.log(`${path_} internal name mismatch: ${mcToAnsi(nbt?.ExtraAttributes.id)} != ${mcToAnsi(item.internalname)}`); */
+
+ // console.log('===========');
+
+ console.log(mcToAnsi(item.displayname));
+ console.log(item.lore.map((l) => mcToAnsi(l)).join('\n'));
+
+ /* const keys = [
+ 'itemid',
+ 'displayname',
+ 'nbttag',
+ 'damage',
+ 'lore',
+ 'recipe',
+ 'internalname',
+ 'modver',
+ 'infoType',
+ 'info',
+ 'crafttext',
+ 'vanilla',
+ 'useneucraft',
+ 'slayer_req',
+ 'clickcommand',
+ 'x',
+ 'y',
+ 'z',
+ 'island',
+ 'recipes',
+ 'parent',
+ 'noseal'
+ ];
+
+ Object.keys(item).forEach((k) => {
+ if (!keys.includes(k)) throw new Error(`Unknown key: ${k}`);
+ });
+
+ if (
+ 'slayer_req' in item &&
+ !new Array(10).flatMap((_, i) => ['WOLF', 'BLAZE', 'EMAN'].map((e) => e + (i + 1)).includes(item.slayer_req!))
+ )
+ throw new Error(`Unknown slayer req: ${item.slayer_req!}`); */
+
+ /* console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-'); */
+}