minecraft-inventory 0.1.20 → 0.1.22

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-inventory",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "release": {
@@ -310,7 +310,7 @@ export function InventoryOverlay({
310
310
  lineHeight: 1,
311
311
  }}
312
312
  >
313
- INV 0.1.20
313
+ INV 0.1.22
314
314
  </a>
315
315
  )}
316
316
 
@@ -0,0 +1,45 @@
1
+ import React from 'react'
2
+ import { useScale } from '../../context/ScaleContext'
3
+ import { useInventoryContext } from '../../context/InventoryContext'
4
+
5
+ interface AnvilCostProps {
6
+ properties: Record<string, number>
7
+ backgroundWidth: number
8
+ }
9
+
10
+ export function AnvilCost({ properties, backgroundWidth }: AnvilCostProps) {
11
+ const { scale } = useScale()
12
+ const { windowState } = useInventoryContext()
13
+
14
+ const cost = properties.repairCost ?? 0
15
+ const hasResult = windowState?.slots.some((s) => s.index === 2 && s.item !== null) ?? false
16
+ if (cost <= 0) return null
17
+ if (!hasResult) return null
18
+
19
+ const tooExpensive = cost >= 40
20
+ const label = tooExpensive ? 'Too Expensive!' : `Enchantment Cost: ${cost}`
21
+ const color = tooExpensive ? '#FF6060' : '#80FF20'
22
+
23
+ return (
24
+ <div
25
+ style={{
26
+ position: 'absolute',
27
+ right: 8 * scale,
28
+ top: 67 * scale,
29
+ height: 12 * scale,
30
+ display: 'flex',
31
+ alignItems: 'center',
32
+ justifyContent: 'flex-end',
33
+ padding: `0 ${2 * scale}px`,
34
+ background: 'rgba(0, 0, 0, 0.3)',
35
+ fontSize: 7 * scale,
36
+ fontFamily: "'Minecraftia', 'Minecraft', monospace",
37
+ color,
38
+ whiteSpace: 'nowrap',
39
+ pointerEvents: 'none',
40
+ }}
41
+ >
42
+ {label}
43
+ </div>
44
+ )
45
+ }
@@ -11,6 +11,7 @@ import { VillagerTradeList } from './VillagerTradeList'
11
11
  import { EnchantmentOptions } from './EnchantmentOptions'
12
12
  import { HotbarExtras } from './HotbarExtras'
13
13
  import { AnvilInput } from './AnvilInput'
14
+ import { AnvilCost } from './AnvilCost'
14
15
  import { EntityDisplay } from './EntityDisplay'
15
16
 
16
17
  interface InventoryWindowProps {
@@ -139,8 +140,9 @@ export function InventoryWindow({
139
140
  />
140
141
  )}
141
142
 
142
- {/* Anvil rename input */}
143
+ {/* Anvil rename input + cost display */}
143
144
  {isAnvil && <AnvilInput x={59} y={20} width={110} height={16} />}
145
+ {isAnvil && <AnvilCost properties={effectiveProperties} backgroundWidth={def.backgroundWidth} />}
144
146
 
145
147
  {/* Hotbar extras: active slot indicator, offhand slot, open-inventory button */}
146
148
  {isHotbar && (
@@ -52,6 +52,26 @@ export interface MineflayerConnectorOptions {
52
52
  * Return a human-readable string for display.
53
53
  */
54
54
  formatTitle?: (rawTitle: any) => string
55
+
56
+ /**
57
+ * Optional client-side anvil cost calculator. Called when items in an anvil
58
+ * window change to compute repairCost instantly (before the server's
59
+ * craft_progress_bar packet arrives). Receives the two input slot items.
60
+ * Return the XP cost, or null/0 to clear. Server packets will override this.
61
+ *
62
+ * @example Using prismarine-item:
63
+ * ```ts
64
+ * const Item = require('prismarine-item')(bot.registry)
65
+ * createMineflayerConnector(bot, {
66
+ * computeAnvilCost: (item1, item2) => {
67
+ * if (!item1) return null
68
+ * const { xpCost } = Item.anvil(item1, item2, bot.game.gameMode === 'creative')
69
+ * return xpCost
70
+ * },
71
+ * })
72
+ * ```
73
+ */
74
+ computeAnvilCost?: (itemOne: RawSlot | null, itemTwo: RawSlot | null) => number | null
55
75
  }
56
76
 
57
77
  function makeSlotConverter(itemMapper?: MineflayerConnectorOptions['itemMapper']) {
@@ -138,6 +158,7 @@ export function createMineflayerConnector(bot: MineflayerBot, options?: Mineflay
138
158
  const convert = makeSlotConverter(options?.itemMapper)
139
159
  const hotbarOnly = options?.hotbarOnly ?? false
140
160
  const formatTitle = options?.formatTitle
161
+ const computeAnvilCost = options?.computeAnvilCost
141
162
 
142
163
  // Track window properties (furnace progress, enchant levels, etc.)
143
164
  const windowProperties: Record<string, number> = {}
@@ -297,6 +318,27 @@ export function createMineflayerConnector(bot: MineflayerBot, options?: Mineflay
297
318
  }
298
319
  }
299
320
 
321
+ /** Compute anvil cost client-side if callback is provided and window is anvil. */
322
+ function tryComputeAnvilCost() {
323
+ if (!computeAnvilCost || !currentWindowType) return
324
+ const resolved = getInventoryType(currentWindowType)
325
+ if (resolved?.name !== 'anvil') return
326
+ const win = bot.currentWindow
327
+ if (!win) return
328
+ const item1 = (win.slots[0] as RawSlot | null) ?? null
329
+ const item2 = (win.slots[1] as RawSlot | null) ?? null
330
+ try {
331
+ const cost = item1 ? computeAnvilCost(item1, item2) : null
332
+ // Only SET client cost, never delete server-provided values.
333
+ // The server's craft_progress_bar will send 0 to clear when appropriate.
334
+ if (cost != null && cost > 0) {
335
+ windowProperties.repairCost = cost
336
+ }
337
+ } catch (err) {
338
+ // Client-side computation failed — rely on server cost
339
+ }
340
+ }
341
+
300
342
  const onWindowOpen = () => {
301
343
  const state = buildWindowState()
302
344
  if (state) emit({ type: 'windowOpen', state })
@@ -307,6 +349,7 @@ export function createMineflayerConnector(bot: MineflayerBot, options?: Mineflay
307
349
  }
308
350
 
309
351
  const onSetSlot = () => {
352
+ tryComputeAnvilCost()
310
353
  const state = buildWindowState()
311
354
  if (state) emit({ type: 'windowUpdate', state })
312
355
  emit({ type: 'playerUpdate', state: buildPlayerState() })
@@ -496,11 +539,17 @@ export function createMineflayerConnector(bot: MineflayerBot, options?: Mineflay
496
539
  return
497
540
  }
498
541
 
499
- if (action.type === 'rename' && win && isAnvilWindow(win)) {
500
- const w = win as { slots?: unknown[]; findInventoryItem?: (id: number) => unknown; rename: (item: unknown, name: string) => Promise<void> }
501
- const inputSlot = w.slots?.[0] as { type?: number; metadata?: number; count?: number; nbt?: unknown } | null
502
- const item = inputSlot?.type ? (w.findInventoryItem?.(inputSlot.type) ?? inputSlot) : null
503
- if (item) await w.rename(item, action.text)
542
+ if (action.type === 'rename' && win && isAnvilWindow(win) && ext._client) {
543
+ // Send name_item packet directly don't use mineflayer's win.rename()
544
+ // because it tries to transfer items from player inventory into anvil,
545
+ // which fails when the user already placed items via the UI.
546
+ if (ext.supportFeature?.('useMCItemName')) {
547
+ if (!ext._client.registerChannel) return
548
+ ext._client.registerChannel('MC|ItemName', 'string')
549
+ ext._client.writeChannel?.('MC|ItemName', action.text)
550
+ } else {
551
+ ext._client.write('name_item', { name: action.text })
552
+ }
504
553
  return
505
554
  }
506
555