179 lines
7.3 KiB
JavaScript
179 lines
7.3 KiB
JavaScript
|
function loader (registry, Item) {
|
||
|
// TODO: anvil support for bedrock
|
||
|
if (registry.type === 'bedrock') return undefined
|
||
|
|
||
|
function combine (itemOne, itemTwo, creative, renamedName) {
|
||
|
const rename = typeof renamedName === 'string'
|
||
|
const data = {
|
||
|
finalEnchs: [],
|
||
|
fixedDurability: 0
|
||
|
}
|
||
|
let onlyRename = false // to tell if it's just a rename
|
||
|
if (!combinePossible(itemOne, itemTwo) && itemTwo !== null) return { xpCost: 0, item: null }
|
||
|
let cost = 0
|
||
|
if (rename) {
|
||
|
onlyRename = true
|
||
|
const renameCost = getRenameCost(itemOne)
|
||
|
if (renameCost === -1) return { xpCost: 0, item: null }
|
||
|
cost += renameCost
|
||
|
}
|
||
|
if (itemOne.durabilityUsed !== 0) {
|
||
|
onlyRename = false
|
||
|
const { xpLevelCost: repairCost, fixedDurability, usedMats } = getRepairCost(itemOne, itemTwo)
|
||
|
data.fixedDurability = fixedDurability
|
||
|
data.usedMats = usedMats
|
||
|
cost += repairCost
|
||
|
}
|
||
|
if (itemTwo && (itemTwo.name === itemOne.name || itemTwo.name === 'enchanted_book')) {
|
||
|
onlyRename = false
|
||
|
const { xpLevelCost: enchantCost, finalEnchs } = combineEnchants(itemOne, itemTwo, creative)
|
||
|
data.finalEnchs = finalEnchs
|
||
|
if (enchantCost === 0 && !rename && itemOne.metadata === 0) return { xpCost: 0, item: null } // no change
|
||
|
cost += enchantCost
|
||
|
}
|
||
|
if (itemTwo === null && itemOne.customName === renamedName) return { xpCost: 0, item: null } // no change
|
||
|
cost += itemOne.repairCost + (itemTwo?.repairCost ?? 0)
|
||
|
|
||
|
if (cost > 39 && onlyRename) cost = 39
|
||
|
else if (cost >= 40) return { xpCost: 0, item: null } // show too expensive message
|
||
|
|
||
|
let finalItem = null
|
||
|
if (itemOne) {
|
||
|
finalItem = new Item(itemOne.type, itemOne.count, 0, JSON.parse(JSON.stringify(itemOne.nbt)), null, true)
|
||
|
const resultDurability = itemOne.durabilityUsed - data.fixedDurability
|
||
|
const repairCost = Math.max(itemOne.repairCost, (itemTwo?.repairCost ?? 0)) * 2 + 1
|
||
|
if (data?.finalEnchs.length > 0) finalItem.enchants = data.finalEnchs
|
||
|
if (rename) finalItem.customName = renamedName
|
||
|
finalItem.repairCost = repairCost
|
||
|
if (resultDurability && itemOne.name !== 'enchanted_book') finalItem.durabilityUsed = resultDurability
|
||
|
}
|
||
|
return { xpCost: cost, item: finalItem, usedMats: data.usedMats }
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param {Item} itemOne left hand item
|
||
|
* @param {Item} itemTwo right hand item
|
||
|
* @param {boolean} creative whether the bot is in creative mode
|
||
|
* @returns {{finalEnchs: (*|[]|{lvl: *, name: *|null}[]|NormalizedEnchant[]), xpLevelCost: number}}
|
||
|
* xpLevelCost is enchant data that is strictly from combining enchants
|
||
|
* finalEnchs is the array of enchants on the final object
|
||
|
*/
|
||
|
function combineEnchants (itemOne, itemTwo, creative) {
|
||
|
const rightIsBook = itemTwo.name === 'enchanted_book'
|
||
|
const finalEnchs = itemOne.enchants
|
||
|
const finalEnchsByName = finalEnchs.map(x => x.name)
|
||
|
const itemTwoEnch = itemTwo.enchants
|
||
|
let xpLevelCost = 0
|
||
|
for (const ench of itemTwoEnch) {
|
||
|
const enchOnItemOne = finalEnchs.find(x => x.name === ench.name)
|
||
|
let { exclude, maxLevel, category, weight } = registry.enchantmentsByName[ench.name]
|
||
|
const multiplier = getMultipliers(weight, rightIsBook)
|
||
|
if (!(itemOne.name === 'enchanted_book' && rightIsBook) && !registry.itemsByName[itemOne.name].enchantCategories.includes(category) && !creative) continue
|
||
|
else if (enchOnItemOne === undefined) { // first item doesn't have this ench
|
||
|
exclude = exclude.map(name => registry.enchantmentsByName[name].name)
|
||
|
if (exclude.some(excludedEnch => finalEnchsByName.includes(excludedEnch))) { // has an excluded enchant
|
||
|
xpLevelCost++
|
||
|
} else {
|
||
|
const finalLevel = ench.lvl
|
||
|
xpLevelCost += finalLevel * multiplier
|
||
|
finalEnchs.push({ name: ench.name, lvl: ench.lvl })
|
||
|
}
|
||
|
} else {
|
||
|
let finalLevel = 0
|
||
|
const itemOneLevel = enchOnItemOne.lvl
|
||
|
const itemTwoLevel = ench.lvl
|
||
|
if (itemOneLevel === itemTwoLevel) {
|
||
|
finalLevel = Math.min(itemOneLevel + 1, maxLevel)
|
||
|
enchOnItemOne.lvl = finalLevel
|
||
|
} else if (itemTwoLevel > itemOneLevel) {
|
||
|
finalLevel = itemTwoLevel
|
||
|
enchOnItemOne.lvl = finalLevel
|
||
|
} else if (itemOneLevel > itemTwoLevel) {
|
||
|
finalLevel = itemOneLevel
|
||
|
}
|
||
|
xpLevelCost += finalLevel * multiplier
|
||
|
}
|
||
|
}
|
||
|
return { xpLevelCost, finalEnchs }
|
||
|
}
|
||
|
|
||
|
// converts enchant weight to enchant cost multiplier
|
||
|
function getMultipliers (weight, isBook) {
|
||
|
const itemMultiplier = {
|
||
|
10: 1,
|
||
|
5: 2,
|
||
|
2: 4,
|
||
|
1: 8
|
||
|
}[weight]
|
||
|
return isBook ? Math.max(1, itemMultiplier / 2) : itemMultiplier
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param {Item} itemOne left hand item
|
||
|
* @param {Item} itemTwo right hand item
|
||
|
* @returns {{usedMats: number, fixedDurability: number, xpLevelCost: number}|number}
|
||
|
* xpLevelCost is the number of xp levels used for repair (if any)
|
||
|
* fixedDurability is duribility after using the anvil
|
||
|
* usedMats is the number of materials used to fix the broken item (if many mats is used)
|
||
|
*/
|
||
|
function getRepairCost (itemOne, itemTwo) {
|
||
|
if (itemTwo === null) return { xpLevelCost: 0, fixedDurability: 0, usedMats: 0 } // air
|
||
|
else if (itemTwo.name === 'enchanted_book') return { xpLevelCost: 0, fixedDurability: 0, usedMats: 0 }
|
||
|
|
||
|
const maxDurability = registry.itemsByName[itemOne.name].maxDurability
|
||
|
const durabilityLost = itemOne.durabilityUsed
|
||
|
const fixMaterials = registry.itemsByName[itemOne.name].repairWith.concat([itemOne.name])
|
||
|
if (!fixMaterials.includes(itemTwo.name) && itemOne.name !== itemTwo.name) {
|
||
|
return 0 // Enchanted book can't fix
|
||
|
}
|
||
|
let results = {
|
||
|
fixedDurability: 0,
|
||
|
xpLevelCost: 0,
|
||
|
usedMats: 0
|
||
|
}
|
||
|
if (itemTwo.name === itemOne.name) {
|
||
|
const possibleFixedDura = Math.floor(0.12 * maxDurability) + itemTwo.metadata
|
||
|
results = {
|
||
|
fixedDurability: itemOne.metadata < possibleFixedDura ? itemOne.durabilityUsed : possibleFixedDura,
|
||
|
xpLevelCost: 2,
|
||
|
usedMats: 1
|
||
|
}
|
||
|
} else if (durabilityLost !== 0) {
|
||
|
const durabilityFixedPerMat = Math.floor(maxDurability * 0.25)
|
||
|
const matsToFullyRepair = Math.ceil(durabilityLost / durabilityFixedPerMat)
|
||
|
if (itemTwo.count > matsToFullyRepair) { // takeall of itemTwo
|
||
|
results = {
|
||
|
fixedDurability: maxDurability - itemOne.durabilityUsed,
|
||
|
xpLevelCost: matsToFullyRepair, // 1 exp lvl per mat used
|
||
|
usedMats: matsToFullyRepair
|
||
|
}
|
||
|
} else if (itemOne && itemTwo) {
|
||
|
results = {
|
||
|
fixedDurability: Math.min(itemOne.durabilityUsed, durabilityFixedPerMat * itemTwo.count),
|
||
|
xpLevelCost: itemTwo.count, // 1 exp lvl per mat used
|
||
|
usedMats: itemTwo.count
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return results
|
||
|
}
|
||
|
|
||
|
function getRenameCost (item) {
|
||
|
if (item?.nbt?.value?.RepairCost?.value === 0x7fffffff) return -1
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
function combinePossible (itemOne, itemTwo) {
|
||
|
if (!itemOne?.name || !itemTwo?.name || (!itemOne?.name && !itemTwo?.name)) return false
|
||
|
let fixMaterials = (registry.itemsByName[itemOne.name].repairWith ?? []).concat([itemOne.name])
|
||
|
if (itemOne.name !== 'enchanted_book') fixMaterials = fixMaterials.concat(['enchanted_book'])
|
||
|
return fixMaterials.includes(itemTwo.name)
|
||
|
}
|
||
|
|
||
|
return combine
|
||
|
}
|
||
|
|
||
|
module.exports = loader
|