prismarine-item 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: npm
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
8
+ ignore:
9
+ - dependency-name: "@types/node"
10
+ versions:
11
+ - 15.0.0
12
+ - dependency-name: minecraft-data
13
+ versions:
14
+ - 2.80.0
@@ -0,0 +1,25 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ build:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ matrix:
16
+ node-version: [14.x]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - name: Use Node.js ${{ matrix.node-version }}
21
+ uses: actions/setup-node@v1
22
+ with:
23
+ node-version: ${{ matrix.node-version }}
24
+ - run: npm install
25
+ - run: npm test
@@ -0,0 +1,25 @@
1
+ name: npm-publish
2
+ on:
3
+ push:
4
+ branches:
5
+ - master # Change this to your default branch
6
+ jobs:
7
+ npm-publish:
8
+ name: npm-publish
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout repository
12
+ uses: actions/checkout@master
13
+ - name: Set up Node.js
14
+ uses: actions/setup-node@master
15
+ with:
16
+ node-version: 10.0.0
17
+ - name: Publish if version has been updated
18
+ uses: pascalgn/npm-publish-action@4f4bf159e299f65d21cd1cbd96fc5d53228036df
19
+ with: # All of theses inputs are optional
20
+ tag_name: "%s"
21
+ tag_message: "%s"
22
+ commit_pattern: "^Release (\\S+)"
23
+ env: # More info about the environment variables in the README
24
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this as is, it's automatically generated
25
+ NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} # You need to set this in your repo settings
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # prismarine-item
2
+ [![NPM version](https://img.shields.io/npm/v/prismarine-item.svg)](http://npmjs.com/package/prismarine-item)
3
+ [![Build Status](https://github.com/PrismarineJS/prismarine-item/workflows/CI/badge.svg)](https://github.com/PrismarineJS/prismarine-item/actions?query=workflow%3A%22CI%22)
4
+
5
+ Represent a minecraft item with its associated data
6
+
7
+ ## Usage
8
+
9
+ ```js
10
+ const Item = require('prismarine-item')('1.8')
11
+
12
+ const ironShovelItem = new Item(256, 1)
13
+ console.log(ironShovelItem)
14
+
15
+ const notchItem = Item.toNotch(ironShovelItem)
16
+ console.log(notchItem)
17
+ console.log(Item.fromNotch(notchItem))
18
+ ```
19
+
20
+ ## API
21
+
22
+ ### Item(type, count[, metadata], nbt)
23
+
24
+ #### Item.toNotch(item)
25
+
26
+ Take an `item` in the format of the minecraft packets and return an `Item` instance.
27
+
28
+ #### Item.fromNotch(item)
29
+
30
+ Take an `Item` instance and return it in the format of the minecraft packets.
31
+
32
+ ### Item.anvil(itemOne, itemTwo, creative[, newName])
33
+
34
+ Take two seperate `item` instances, and makes one item using the same combining done by the vanilla anvil
35
+
36
+ ### Item.equal(itemOne, itemTwo[, matchStackSize])
37
+
38
+ `itemOne` - first item
39
+
40
+ `itemTwo` - second item
41
+
42
+ `matchStackSize` - whether to check for count equality
43
+
44
+ Checks equality between two items based on itemType, count, metadata, and stringified nbt
45
+
46
+ #### item.type
47
+
48
+ Numerical id.
49
+
50
+ #### item.count
51
+
52
+ #### item.metadata
53
+
54
+ Number which represents different things depending on the item.
55
+ See http://www.minecraftwiki.net/wiki/Data_values#Data
56
+
57
+ #### item.nbt
58
+
59
+ Buffer.
60
+
61
+ #### item.name
62
+
63
+ #### item.displayName
64
+
65
+ #### item.stackSize
66
+
67
+ #### item.equal(otherItem)
68
+
69
+ Return true if items are equal.
70
+
71
+ #### item.durabilityUsed
72
+
73
+ A getter/setter for abstracting the underlying nbt
74
+
75
+ #### item.customName
76
+
77
+ the item's custom name (ie. anvil name)
78
+
79
+ #### item.customLore
80
+
81
+ the item's custom lore (ie. set in give command)
82
+
83
+ #### item.enchants
84
+
85
+ A getter/setter for abstracting the underlying nbt (does calculations)
86
+
87
+ #### item.repairCost
88
+
89
+ A getter/setter for abstracting the underlying nbt.
90
+ See https://minecraft.gamepedia.com/Anvil_mechanics#Anvil_Uses
91
+
92
+ #### item.spawnEggMobName
93
+
94
+ A getter for abstracting the underlying nbt, get's the mob name from a spawn egg Item. e.g. a zombie spawn egg on 1.8 will return `Zombie`
95
+
96
+
97
+ ## History
98
+
99
+ ## 1.11.0
100
+
101
+ * fix typings
102
+ * add .customLore
103
+ * .customName now returns null when there is no custom name
104
+
105
+ ## 1.10.1
106
+
107
+ * update typings (thanks @stzups)
108
+
109
+ ## 1.10.0
110
+
111
+ * add item.spawnEggMobName (thanks @U9G)
112
+
113
+ ## 1.9.1
114
+
115
+ * fix item present detection (thanks @U9G)
116
+
117
+ ## 1.9.0
118
+
119
+ * Revise typings (thanks @extremeheat)
120
+ * Revise deps (thanks @rom1504)
121
+ * Correctly identify null items in MC 1.13 (thanks @u9g)
122
+
123
+ ## 1.8.0
124
+
125
+ * add matchStackSize option on Item.equal (thanks @u9g)
126
+
127
+ ## 1.7.0
128
+
129
+ * Item.equal checks nbt equality (thanks @u9g)
130
+
131
+ ## 1.6.0
132
+
133
+ * Item.anvil added, along with a ton of getters & setters for Item (thanks @u9g)
134
+
135
+ ### 1.5.0
136
+
137
+ * 1.16 support (thanks @DrakoTrogdor)
138
+
139
+ ### 1.4.0
140
+
141
+ * typescripts definitions (thanks @IdanHo)
142
+
143
+ ### 1.3.0
144
+
145
+ * 1.15 support
146
+
147
+ ### 1.2.0
148
+
149
+ * 1.14 support
150
+
151
+ ### 1.1.1
152
+
153
+ * allow unknown items
154
+
155
+ ### 1.1.0
156
+
157
+ * 1.13 support
158
+
159
+ ### 1.0.2
160
+
161
+ * make nbt default to null
162
+ * display the item id if it is not found in minecraft data
163
+
164
+ ### 1.0.1
165
+
166
+ * bump mcdata
167
+
168
+ ### 1.0.0
169
+
170
+ * bump dependencies
171
+
172
+ ### 0.0.0
173
+
174
+ * Import from mineflayer
package/example.js ADDED
@@ -0,0 +1,21 @@
1
+ const Item = require('./')('1.8')
2
+
3
+ const ironShovelItem = new Item(256, 1)
4
+
5
+ console.log(ironShovelItem)
6
+
7
+ const notchItem = Item.toNotch(ironShovelItem)
8
+ console.log(notchItem)
9
+
10
+ console.log(Item.fromNotch(notchItem))
11
+
12
+ const Item113 = require('./')('1.13.2')
13
+
14
+ const ironShovelItem2 = new Item113(472, 1)
15
+
16
+ console.log(ironShovelItem2)
17
+
18
+ const notchItem2 = Item113.toNotch(ironShovelItem2)
19
+ console.log(notchItem2)
20
+
21
+ console.log(Item113.fromNotch(notchItem2))
package/index.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ /// <reference types="node" />
2
+
3
+ import { Tags, TagType } from 'prismarine-nbt'
4
+
5
+ type ItemLike = Item | null
6
+
7
+ declare class Item {
8
+ constructor(type: number, count: number, metadata?: number, nbt?: object);
9
+ type: number;
10
+ slot: number;
11
+ count: number;
12
+ metadata: number;
13
+ nbt: Tags[TagType] | null;
14
+ name: string;
15
+ displayName: string;
16
+ stackSize: number;
17
+ durabilityUsed: number;
18
+ enchants: NormalizedEnchant[];
19
+ repairCost: number;
20
+ customName: string | null;
21
+ customLore: string | null;
22
+ readonly spawnEggMobName: string;
23
+ static equal(item1: Item, item2: Item, matchStackSize: boolean): boolean;
24
+ static toNotch(item: ItemLike): NotchItem;
25
+ static fromNotch(item: NotchItem): ItemLike;
26
+ static anvil (itemOne: ItemLike, itemTwo: ItemLike, creative: boolean, rename: string | undefined): { xpCost: number, item: ItemLike }
27
+ }
28
+
29
+ declare interface NotchItem {
30
+ // 1.8 - 1.12
31
+ blockId?: number;
32
+ itemDamage?: number;
33
+ // 1.13 - 1.15
34
+ present?: boolean;
35
+ itemId?: number;
36
+
37
+ itemCount?: number;
38
+ nbtData?: Buffer;
39
+ }
40
+
41
+ declare interface NormalizedEnchant {
42
+ name: string;
43
+ lvl: number
44
+ }
45
+
46
+ export default function loader(mcVersion: string): typeof Item;
package/index.js ADDED
@@ -0,0 +1,197 @@
1
+ const nbt = require('prismarine-nbt')
2
+ function loader (version) {
3
+ const mcData = require('minecraft-data')(version)
4
+ class Item {
5
+ constructor (type, count, metadata, nbt) {
6
+ if (type == null) return
7
+
8
+ if (metadata instanceof Object && metadata !== null) {
9
+ nbt = metadata
10
+ metadata = 0
11
+ }
12
+
13
+ this.type = type
14
+ this.count = count
15
+ this.metadata = metadata == null ? 0 : metadata
16
+ this.nbt = nbt || null
17
+
18
+ const itemEnum = mcData.findItemOrBlockById(type)
19
+ if (itemEnum) {
20
+ this.name = itemEnum.name
21
+ this.displayName = itemEnum.displayName
22
+ if ('variations' in itemEnum) {
23
+ for (const i in itemEnum.variations) {
24
+ if (itemEnum.variations[i].metadata === metadata) { this.displayName = itemEnum.variations[i].displayName }
25
+ }
26
+ }
27
+ this.stackSize = itemEnum.stackSize
28
+ } else {
29
+ this.name = 'unknown'
30
+ this.displayName = 'unknown'
31
+ this.stackSize = 1
32
+ }
33
+ }
34
+
35
+ static equal (item1, item2, matchStackSize = true) {
36
+ if (item1 == null && item2 == null) {
37
+ return true
38
+ } else if (item1 == null) {
39
+ return false
40
+ } else if (item2 == null) {
41
+ return false
42
+ } else {
43
+ return (item1.type === item2.type &&
44
+ (matchStackSize ? item1.count === item2.count : true) &&
45
+ item1.metadata === item2.metadata &&
46
+ JSON.stringify(item1.nbt) === JSON.stringify(item2.nbt))
47
+ }
48
+ }
49
+
50
+ static toNotch (item) {
51
+ if (mcData.isNewerOrEqualTo('1.13')) {
52
+ if (item == null) return { present: false }
53
+ const notchItem = {
54
+ present: true,
55
+ itemId: item.type,
56
+ itemCount: item.count
57
+ }
58
+ if (item.nbt && item.nbt.length !== 0) { notchItem.nbtData = item.nbt }
59
+ return notchItem
60
+ } else {
61
+ if (item == null) return { blockId: -1 }
62
+ const notchItem = {
63
+ blockId: item.type,
64
+ itemCount: item.count,
65
+ itemDamage: item.metadata
66
+ }
67
+ if (item.nbt && item.nbt.length !== 0) { notchItem.nbtData = item.nbt }
68
+ return notchItem
69
+ }
70
+ }
71
+
72
+ static fromNotch (item) {
73
+ if (mcData.isNewerOrEqualTo('1.14')) {
74
+ if (item.present === false) return null
75
+ return new Item(item.itemId, item.itemCount, item.nbtData)
76
+ } else if (mcData.isNewerOrEqualTo('1.13')) {
77
+ if (item.itemId === -1 || item.present === false) return null
78
+ return new Item(item.itemId, item.itemCount, item.nbtData)
79
+ } else {
80
+ if (item.blockId === -1) return null
81
+ return new Item(item.blockId, item.itemCount, item.itemDamage, item.nbtData)
82
+ }
83
+ }
84
+
85
+ get customName () {
86
+ if (Object.keys(this).length === 0) return null
87
+ return this?.nbt?.value?.display?.value?.Name?.value ?? null
88
+ }
89
+
90
+ set customName (newName) {
91
+ if (!this.nbt) this.nbt = { name: '', type: 'compound', value: {} }
92
+ if (!this.nbt.value.display) this.nbt.value.display = { type: 'compound', value: {} }
93
+ this.nbt.value.display.value.Name = { type: 'string', value: newName }
94
+ }
95
+
96
+ get customLore () {
97
+ if (Object.keys(this).length === 0) return null
98
+ return this?.nbt?.value?.display?.value?.Name?.value ?? null
99
+ }
100
+
101
+ set customLore (newLore) {
102
+ if (!this.nbt) this.nbt = { name: '', type: 'compound', value: {} }
103
+ if (!this.nbt.value.display) this.nbt.value.display = { type: 'compound', value: {} }
104
+ this.nbt.value.display.value.Lore = { type: 'string', value: newLore }
105
+ }
106
+
107
+ // gets the cost based on previous anvil uses
108
+ get repairCost () {
109
+ if (Object.keys(this).length === 0) return 0
110
+ return this?.nbt?.value?.RepairCost?.value ?? 0
111
+ }
112
+
113
+ set repairCost (value) {
114
+ if (!this?.nbt) this.nbt = { name: '', type: 'compound', value: {} }
115
+ this.nbt.value.RepairCost = { type: 'int', value }
116
+ }
117
+
118
+ get enchants () {
119
+ if (Object.keys(this).length === 0) return []
120
+ if (mcData.isOlderThan('1.13')) {
121
+ let itemEnch
122
+ if (this.name === 'enchanted_book' && this?.nbt?.value?.StoredEnchantments) {
123
+ itemEnch = nbt.simplify(this.nbt).StoredEnchantments
124
+ } else if (this?.nbt?.value?.ench) {
125
+ itemEnch = nbt.simplify(this.nbt).ench
126
+ } else {
127
+ itemEnch = []
128
+ }
129
+ return itemEnch.map(ench => ({ lvl: ench.lvl, name: mcData.enchantments[ench.id].name }))
130
+ } else {
131
+ let itemEnch = []
132
+ if (this?.nbt?.value?.Enchantments) {
133
+ itemEnch = nbt.simplify(this.nbt).Enchantments
134
+ } else if (this?.nbt?.value?.StoredEnchantments) {
135
+ itemEnch = nbt.simplify(this.nbt).StoredEnchantments
136
+ } else {
137
+ itemEnch = []
138
+ }
139
+ return itemEnch.map(ench => ({ lvl: ench.lvl, name: ench.id.replace(/minecraft:/, '') }))
140
+ }
141
+ }
142
+
143
+ set enchants (normalizedEnchArray) {
144
+ const isBook = this.name === 'enchanted_book'
145
+ const postEnchantChange = mcData.isOlderThan('1.13')
146
+ const enchListName = postEnchantChange ? 'ench' : 'Enchantments'
147
+ const type = postEnchantChange ? 'short' : 'string'
148
+ if (!this.nbt) this.nbt = { name: '', type: 'compound', value: {} }
149
+
150
+ const enchs = normalizedEnchArray.map(({ name, lvl }) => {
151
+ const value = postEnchantChange ? mcData.enchantmentsByName[name].id : `minecraft:${mcData.enchantmentsByName[name].name}`
152
+ return { id: { type, value }, lvl: { type: 'short', value: lvl } }
153
+ })
154
+
155
+ if (enchs.length !== 0) {
156
+ this.nbt.value[isBook ? 'StoredEnchantments' : enchListName] = { type: 'list', value: { type: 'compound', value: enchs } }
157
+ }
158
+
159
+ if (mcData.isNewerOrEqualTo('1.13') && mcData.itemsByName[this.name].maxDurability) this.nbt.value.Damage = { type: 'int', value: 0 }
160
+ }
161
+
162
+ get durabilityUsed () {
163
+ if (Object.keys(this).length === 0) return null
164
+ if (mcData.isNewerOrEqualTo('1.13')) {
165
+ return this?.nbt?.value?.Damage?.value ?? 0
166
+ } else {
167
+ return this.metadata ?? 0
168
+ }
169
+ }
170
+
171
+ set durabilityUsed (value) {
172
+ if (mcData.isNewerOrEqualTo('1.13')) {
173
+ if (!this?.nbt) this.nbt = { name: '', type: 'compound', value: {} }
174
+ this.nbt.value.Damage = { type: 'int', value }
175
+ } else {
176
+ this.metadata = value
177
+ }
178
+ }
179
+
180
+ get spawnEggMobName () {
181
+ if (mcData.isOlderThan('1.9')) {
182
+ return mcData.entitiesArray.find(o => o.internalId === this.metadata).name
183
+ }
184
+ if (mcData.isOlderThan('1.13')) {
185
+ const data = nbt.simplify(this.nbt)
186
+ const entityName = data.EntityTag.id
187
+ return entityName.replace('minecraft:', '')
188
+ }
189
+ return this.name.replace('_spawn_egg', '')
190
+ }
191
+ }
192
+
193
+ Item.anvil = require('./lib/anvil.js')(mcData, Item)
194
+ return Item
195
+ }
196
+
197
+ module.exports = loader
package/lib/anvil.js ADDED
@@ -0,0 +1,173 @@
1
+ function loader (mcData, Item) {
2
+ function combine (itemOne, itemTwo, creative, renamedName) {
3
+ const rename = typeof renamedName === 'string'
4
+ const data = {
5
+ finalEnchs: [],
6
+ fixedDurability: 0
7
+ }
8
+ let onlyRename = false // to tell if its just a rename
9
+ if (!combinePossible(itemOne, itemTwo) && itemTwo !== null) return { xpCost: 0, item: null }
10
+ let cost = 0
11
+ if (rename) {
12
+ onlyRename = true
13
+ const renameCost = getRenameCost(itemOne)
14
+ if (renameCost === -1) return { xpCost: 0, item: null }
15
+ cost += renameCost
16
+ }
17
+ if (itemOne.durabilityUsed !== 0) {
18
+ onlyRename = false
19
+ const { xpLevelCost: repairCost, fixedDurability, usedMats } = getRepairCost(itemOne, itemTwo)
20
+ data.fixedDurability = fixedDurability
21
+ data.usedMats = usedMats
22
+ cost += repairCost
23
+ }
24
+ if (itemTwo && (itemTwo.name === itemOne.name || itemTwo.name === 'enchanted_book')) {
25
+ onlyRename = false
26
+ const { xpLevelCost: enchantCost, finalEnchs } = combineEnchants(itemOne, itemTwo, creative)
27
+ data.finalEnchs = finalEnchs
28
+ if (enchantCost === 0 && !rename && itemOne.metadata === 0) return { xpCost: 0, item: null } // no change
29
+ cost += enchantCost
30
+ }
31
+ if (itemTwo === null && itemOne.customName === renamedName) return { xpCost: 0, item: null } // no change
32
+ cost += itemOne.repairCost + (itemTwo?.repairCost ?? 0)
33
+
34
+ if (cost > 39 && onlyRename) cost = 39
35
+ else if (cost >= 40) return { xpCost: 0, item: null } // show too expensive message
36
+
37
+ let finalItem = null
38
+ if (itemOne) {
39
+ finalItem = new Item(itemOne.type, itemOne.count, 0, JSON.parse(JSON.stringify(itemOne.nbt)))
40
+ const repairCost = Math.max(itemOne.repairCost, (itemTwo?.repairCost ?? 0)) * 2 + 1
41
+ if (data?.finalEnchs.length > 0) finalItem.enchants = data.finalEnchs
42
+ if (rename) finalItem.customName = renamedName
43
+ finalItem.repairCost = repairCost
44
+ if (itemOne.name !== 'enchanted_book') finalItem.durabilityUsed = itemOne.durabilityUsed - data.fixedDurability
45
+ }
46
+ return { xpCost: cost, item: finalItem, usedMats: data.usedMats }
47
+ }
48
+
49
+ /**
50
+ *
51
+ * @param {Item} itemOne left hand item
52
+ * @param {Item} itemTwo right hand item
53
+ * @returns {xpLevelCost, finalEnchs}
54
+ * xpLevelCost is enchant data that is strictly from combining enchants
55
+ * finalEnchs is the array of enchants on the final object
56
+ */
57
+ function combineEnchants (itemOne, itemTwo, creative) {
58
+ const rightIsBook = itemTwo.name === 'enchanted_book'
59
+ const finalEnchs = itemOne.enchants
60
+ const finalEnchsByName = finalEnchs.map(x => x.name)
61
+ const itemTwoEnch = itemTwo.enchants
62
+ let xpLevelCost = 0
63
+ for (const ench of itemTwoEnch) {
64
+ const enchOnItemOne = finalEnchs.find(x => x.name === ench.name)
65
+ let { exclude, maxLevel, category, weight } = mcData.enchantmentsByName[ench.name]
66
+ const multiplier = getMultipliers(weight, rightIsBook)
67
+ if (!(itemOne.name === 'enchanted_book' && rightIsBook) && !mcData.itemsByName[itemOne.name].enchantCategories.includes(category) && !creative) continue
68
+ else if (enchOnItemOne === undefined) { // first item doesn't have this ench
69
+ exclude = exclude.map(name => mcData.enchantmentsByName[name].name)
70
+ if (exclude.some(excludedEnch => finalEnchsByName.includes(excludedEnch))) { // has an excluded enchant
71
+ xpLevelCost++
72
+ } else {
73
+ const finalLevel = ench.lvl
74
+ xpLevelCost += finalLevel * multiplier
75
+ finalEnchs.push({ name: ench.name, lvl: ench.lvl })
76
+ }
77
+ } else {
78
+ let finalLevel = 0
79
+ const itemOneLevel = enchOnItemOne.lvl
80
+ const itemTwoLevel = ench.lvl
81
+ if (itemOneLevel === itemTwoLevel) {
82
+ finalLevel = Math.min(itemOneLevel + 1, maxLevel)
83
+ enchOnItemOne.lvl = finalLevel
84
+ } else if (itemTwoLevel > itemOneLevel) {
85
+ finalLevel = itemTwoLevel
86
+ enchOnItemOne.lvl = finalLevel
87
+ } else if (itemOneLevel > itemTwoLevel) {
88
+ finalLevel = itemOneLevel
89
+ }
90
+ xpLevelCost += finalLevel * multiplier
91
+ }
92
+ }
93
+ return { xpLevelCost, finalEnchs }
94
+ }
95
+
96
+ // converts enchant weight to enchant cost multiplier
97
+ function getMultipliers (weight, isBook) {
98
+ const itemMultiplier = {
99
+ 10: 1,
100
+ 5: 2,
101
+ 2: 4,
102
+ 1: 8
103
+ }[weight]
104
+ return isBook ? Math.max(1, itemMultiplier / 2) : itemMultiplier
105
+ }
106
+
107
+ /**
108
+ *
109
+ * @param {Item} itemOne left hand item
110
+ * @param {Item} itemTwo right hand item
111
+ * @returns {xpLevelCost, fixedDurability, usedMats}
112
+ * xpLevelCost is the number of xp levels used for repair (if any)
113
+ * fixedDurability is duribility after using the anvil
114
+ * usedMats is the number of materials used to fix the broken item (if many mats is used)
115
+ */
116
+ function getRepairCost (itemOne, itemTwo) {
117
+ if (itemTwo === null) return { xpLevelCost: 0, fixedDurability: 0, usedMats: 0 } // air
118
+ else if (itemTwo.name === 'enchanted_book') return { xpLevelCost: 0, fixedDurability: 0, usedMats: 0 }
119
+
120
+ const maxDurability = mcData.itemsByName[itemOne.name].maxDurability
121
+ const durabilityLost = itemOne.durabilityUsed
122
+ const fixMaterials = mcData.itemsByName[itemOne.name].repairWith.concat([itemOne.name])
123
+ if (!fixMaterials.includes(itemTwo.name) && itemOne.name !== itemTwo.name) {
124
+ return 0 // Enchanted book can't fix
125
+ }
126
+ let results = {
127
+ fixedDurability: 0,
128
+ xpLevelCost: 0,
129
+ usedMats: 0
130
+ }
131
+ if (itemTwo.name === itemOne.name) {
132
+ const possibleFixedDura = Math.floor(0.12 * maxDurability) + itemTwo.metadata
133
+ results = {
134
+ fixedDurability: itemOne.metadata < possibleFixedDura ? itemOne.durabilityUsed : possibleFixedDura,
135
+ xpLevelCost: 2,
136
+ usedMats: 1
137
+ }
138
+ } else if (durabilityLost !== 0) {
139
+ const durabilityFixedPerMat = Math.floor(maxDurability * 0.25)
140
+ const matsToFullyRepair = Math.ceil(durabilityLost / durabilityFixedPerMat)
141
+ if (itemTwo.count > matsToFullyRepair) { // takeall of itemTwo
142
+ results = {
143
+ fixedDurability: maxDurability - itemOne.durabilityUsed,
144
+ xpLevelCost: matsToFullyRepair, // 1 exp lvl per mat used
145
+ usedMats: matsToFullyRepair
146
+ }
147
+ } else if (itemOne && itemTwo) {
148
+ results = {
149
+ fixedDurability: Math.min(itemOne.durabilityUsed, durabilityFixedPerMat * itemTwo.count),
150
+ xpLevelCost: itemTwo.count, // 1 exp lvl per mat used
151
+ usedMats: itemTwo.count
152
+ }
153
+ }
154
+ }
155
+ return results
156
+ }
157
+
158
+ function getRenameCost (item) {
159
+ if (item?.nbt?.value?.RepairCost?.value === 0x7fffffff) return -1
160
+ return 1
161
+ }
162
+
163
+ function combinePossible (itemOne, itemTwo) {
164
+ if (!itemOne?.name || !itemTwo?.name || (!itemOne?.name && !itemTwo?.name)) return false
165
+ let fixMaterials = (mcData.itemsByName[itemOne.name].repairWith ?? []).concat([itemOne.name])
166
+ if (itemOne.name !== 'enchanted_book') fixMaterials = fixMaterials.concat(['enchanted_book'])
167
+ return fixMaterials.includes(itemTwo.name)
168
+ }
169
+
170
+ return combine
171
+ }
172
+
173
+ module.exports = loader