mindcraft/src/utils/mcdata.js
MaxRobinsonTheGreat cc6fc66be2 fixed a couple bugs
2024-10-15 17:03:51 -05:00

298 lines
No EOL
8.2 KiB
JavaScript

import minecraftData from 'minecraft-data';
import settings from '../../settings.js';
import { createBot } from 'mineflayer';
import prismarine_items from 'prismarine-item';
import { pathfinder } from 'mineflayer-pathfinder';
import { plugin as pvp } from 'mineflayer-pvp';
import { plugin as collectblock } from 'mineflayer-collectblock';
import { plugin as autoEat } from 'mineflayer-auto-eat';
import plugin from 'mineflayer-armor-manager';
const armorManager = plugin;
const mc_version = settings.minecraft_version;
const mcdata = minecraftData(mc_version);
const Item = prismarine_items(mc_version);
/**
* @typedef {string} ItemName
* @typedef {string} BlockName
*/
export const WOOD_TYPES = ['oak', 'spruce', 'birch', 'jungle', 'acacia', 'dark_oak'];
export const MATCHING_WOOD_BLOCKS = [
'log',
'planks',
'sign',
'boat',
'fence_gate',
'door',
'fence',
'slab',
'stairs',
'button',
'pressure_plate',
'trapdoor'
]
export const WOOL_COLORS = [
'white',
'orange',
'magenta',
'light_blue',
'yellow',
'lime',
'pink',
'gray',
'light_gray',
'cyan',
'purple',
'blue',
'brown',
'green',
'red',
'black'
]
export function initBot(username) {
let bot = createBot({
username: username,
host: settings.host,
port: settings.port,
auth: settings.auth,
version: mc_version,
});
bot.loadPlugin(pathfinder);
bot.loadPlugin(pvp);
bot.loadPlugin(collectblock);
bot.loadPlugin(autoEat);
bot.loadPlugin(armorManager); // auto equip armor
return bot;
}
export function isHuntable(mob) {
if (!mob || !mob.name) return false;
const animals = ['chicken', 'cow', 'llama', 'mooshroom', 'pig', 'rabbit', 'sheep'];
return animals.includes(mob.name.toLowerCase()) && !mob.metadata[16]; // metadata 16 is not baby
}
export function isHostile(mob) {
if (!mob || !mob.name) return false;
return (mob.type === 'mob' || mob.type === 'hostile') && mob.name !== 'iron_golem' && mob.name !== 'snow_golem';
}
export function getItemId(itemName) {
let item = mcdata.itemsByName[itemName];
if (item) {
return item.id;
}
return null;
}
export function getItemName(itemId) {
let item = mcdata.items[itemId]
if (item) {
return item.name;
}
return null;
}
export function getBlockId(blockName) {
let block = mcdata.blocksByName[blockName];
if (block) {
return block.id;
}
return null;
}
export function getBlockName(blockId) {
let block = mcdata.blocks[blockId]
if (block) {
return block.name;
}
return null;
}
export function getAllItems(ignore) {
if (!ignore) {
ignore = [];
}
let items = []
for (const itemId in mcdata.items) {
const item = mcdata.items[itemId];
if (!ignore.includes(item.name)) {
items.push(item);
}
}
return items;
}
export function getAllItemIds(ignore) {
const items = getAllItems(ignore);
let itemIds = [];
for (const item of items) {
itemIds.push(item.id);
}
return itemIds;
}
export function getAllBlocks(ignore) {
if (!ignore) {
ignore = [];
}
let blocks = []
for (const blockId in mcdata.blocks) {
const block = mcdata.blocks[blockId];
if (!ignore.includes(block.name)) {
blocks.push(block);
}
}
return blocks;
}
export function getAllBlockIds(ignore) {
const blocks = getAllBlocks(ignore);
let blockIds = [];
for (const block of blocks) {
blockIds.push(block.id);
}
return blockIds;
}
export function getAllBiomes() {
return mcdata.biomes;
}
export function getItemCraftingRecipes(itemName) {
let itemId = getItemId(itemName);
if (!mcdata.recipes[itemId]) {
return null;
}
let recipes = [];
for (let r of mcdata.recipes[itemId]) {
let recipe = {};
let ingredients = [];
if (r.ingredients) {
ingredients = r.ingredients;
} else if (r.inShape) {
ingredients = r.inShape.flat();
}
for (let ingredient of ingredients) {
let ingredientName = getItemName(ingredient);
if (ingredientName === null) continue;
if (!recipe[ingredientName])
recipe[ingredientName] = 0;
recipe[ingredientName]++;
}
recipes.push(recipe);
}
return recipes;
}
export function getItemSmeltingIngredient(itemName) {
return {
baked_potato: 'potato',
steak: 'raw_beef',
cooked_chicken: 'raw_chicken',
cooked_cod: 'raw_cod',
cooked_mutton: 'raw_mutton',
cooked_porkchop: 'raw_porkchop',
cooked_rabbit: 'raw_rabbit',
cooked_salmon: 'raw_salmon',
dried_kelp: 'kelp',
iron_ingot: 'raw_iron',
gold_ingot: 'raw_gold',
copper_ingot: 'raw_copper',
glass: 'sand'
}[itemName];
}
export function getItemBlockSources(itemName) {
let itemId = getItemId(itemName);
let sources = [];
for (let block of getAllBlocks()) {
if (block.drops.includes(itemId)) {
sources.push(block.name);
}
}
return sources;
}
export function getItemAnimalSource(itemName) {
return {
raw_beef: 'cow',
raw_chicken: 'chicken',
raw_cod: 'cod',
raw_mutton: 'sheep',
raw_porkchop: 'pig',
raw_rabbit: 'rabbit',
raw_salmon: 'salmon',
leather: 'cow',
wool: 'sheep'
}[itemName];
}
export function getBlockTool(blockName) {
let block = mcdata.blocksByName[blockName];
if (!block || !block.harvestTools) {
return null;
}
return getItemName(Object.keys(block.harvestTools)[0]); // Double check first tool is always simplest
}
export function makeItem(name, amount=1) {
return new Item(getItemId(name), amount);
}
/**
* Returns the number of ingredients required to use the recipe once.
*
* @param {Recipe} recipe
* @returns {Object<mc.ItemName, number>} an object describing the number of each ingredient.
*/
export function ingredientsFromPrismarineRecipe(recipe) {
let requiredIngedients = {};
if (recipe.inShape)
for (const ingredient of recipe.inShape.flat()) {
if(ingredient.id<0) continue; //prismarine-recipe uses id -1 as an empty crafting slot
const ingredientName = getItemName(ingredient.id);
requiredIngedients[ingredientName] ??=0;
requiredIngedients[ingredientName] += ingredient.count;
}
if (recipe.ingredients)
for (const ingredient of recipe.ingredients) {
if(ingredient.id<0) continue;
const ingredientName = getItemName(ingredient.id);
requiredIngedients[ingredientName] ??=0;
requiredIngedients[ingredientName] -= ingredient.count;
//Yes, the `-=` is intended.
//prismarine-recipe uses positive numbers for the shaped ingredients but negative for unshaped.
//Why this is the case is beyond my understanding.
}
return requiredIngedients;
}
/**
* Calculates the number of times an action, such as a crafing recipe, can be completed before running out of resources.
* @template T - doesn't have to be an item. This could be any resource.
* @param {Object.<T, number>} availableItems - The resources available; e.g, `{'cobble_stone': 7, 'stick': 10}`
* @param {Object.<T, number>} requiredItems - The resources required to complete the action once; e.g, `{'cobble_stone': 3, 'stick': 2}`
* @param {boolean} discrete - Is the action discrete?
* @returns {{num: number, limitingResource: (T | null)}} the number of times the action can be completed and the limmiting resource; e.g `{num: 2, limitingResource: 'cobble_stone'}`
*/
export function calculateLimitingResource(availableItems, requiredItems, discrete=true) {
let limitingResource = null;
let num = Infinity;
for (const itemType in requiredItems) {
if (availableItems[itemType] < requiredItems[itemType] * num) {
limitingResource = itemType;
num = availableItems[itemType] / requiredItems[itemType];
}
}
if(discrete) num = Math.floor(num);
return {num, limitingResource}
}