minecraft-inventory 0.1.0 → 0.1.2

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.
@@ -1,17 +1,23 @@
1
- import type { InventoryTypeDefinition } from '../types'
1
+ import type { InventoryTypeDefinition, SlotDefinition } from '../types'
2
2
 
3
3
  const SLOT_SIZE = 18
4
4
 
5
- function playerInv(
6
- yOffset: number,
7
- invStartIndex = 9,
8
- hotbarStartIndex = 36,
9
- ): InventoryTypeDefinition['slots'] {
10
- const slots: InventoryTypeDefinition['slots'] = []
5
+ type RawSlot = Omit<SlotDefinition, 'index'> & { index?: number }
6
+
7
+ function withSequentialIndexes(slots: RawSlot[]): SlotDefinition[] {
8
+ let next = 0
9
+ return slots.map((slot) => {
10
+ const index = slot.index !== undefined ? slot.index : next
11
+ next = index + 1
12
+ return { ...slot, index }
13
+ })
14
+ }
15
+
16
+ function playerInv(yOffset: number): RawSlot[] {
17
+ const slots: RawSlot[] = []
11
18
  for (let row = 0; row < 3; row++) {
12
19
  for (let col = 0; col < 9; col++) {
13
20
  slots.push({
14
- index: invStartIndex + row * 9 + col,
15
21
  x: 8 + col * SLOT_SIZE,
16
22
  y: yOffset + row * SLOT_SIZE,
17
23
  group: 'inventory',
@@ -20,7 +26,6 @@ function playerInv(
20
26
  }
21
27
  for (let col = 0; col < 9; col++) {
22
28
  slots.push({
23
- index: hotbarStartIndex + col,
24
29
  x: 8 + col * SLOT_SIZE,
25
30
  y: yOffset + 58,
26
31
  group: 'hotbar',
@@ -29,9 +34,8 @@ function playerInv(
29
34
  return slots
30
35
  }
31
36
 
32
- function hotbarSlots(yOffset: number, indexOffset = 36, xOffset = 8): InventoryTypeDefinition['slots'] {
37
+ function hotbarSlots(yOffset: number, xOffset = 8): RawSlot[] {
33
38
  return Array.from({ length: 9 }, (_, col) => ({
34
- index: indexOffset + col,
35
39
  x: xOffset + col * SLOT_SIZE,
36
40
  y: yOffset,
37
41
  group: 'hotbar',
@@ -39,18 +43,16 @@ function hotbarSlots(yOffset: number, indexOffset = 36, xOffset = 8): InventoryT
39
43
  }
40
44
 
41
45
  function gridSlots(
42
- startIndex: number,
43
46
  cols: number,
44
47
  rows: number,
45
48
  x: number,
46
49
  y: number,
47
50
  group: string,
48
- ): InventoryTypeDefinition['slots'] {
49
- const slots: InventoryTypeDefinition['slots'] = []
51
+ ): RawSlot[] {
52
+ const slots: RawSlot[] = []
50
53
  for (let row = 0; row < rows; row++) {
51
54
  for (let col = 0; col < cols; col++) {
52
55
  slots.push({
53
- index: startIndex + row * cols + col,
54
56
  x: x + col * SLOT_SIZE,
55
57
  y: y + row * SLOT_SIZE,
56
58
  group,
@@ -60,89 +62,110 @@ function gridSlots(
60
62
  return slots
61
63
  }
62
64
 
63
- export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
65
+ // Slot positions within gui/widgets hotbar texture — matches layouts.mjs Hotbar:
66
+ // itemgrid at x: 3, y: 4, margin: 4, width: 9 (20px per slot: 16+4)
67
+ const HUD_SLOT_STEP = 20
68
+ function hudHotbarSlots(): RawSlot[] {
69
+ return Array.from({ length: 9 }, (_, col) => ({
70
+ // First slot carries explicit index: 36 so the running counter starts there
71
+ ...(col === 0 ? { index: 36 } : {}),
72
+ x: 3.2 + col * HUD_SLOT_STEP,
73
+ y: 3.2,
74
+ group: 'hotbar',
75
+ }))
76
+ }
77
+
78
+ type RawInventoryDefinition = Omit<InventoryTypeDefinition, 'slots'> & { slots: RawSlot[] }
79
+
80
+ const makeInventoryDefinitions = <T extends string>(
81
+ inv: Record<T, RawInventoryDefinition>,
82
+ ): Record<T, InventoryTypeDefinition> =>
83
+ Object.fromEntries(
84
+ Object.entries(inv).map(([k, v]) => [
85
+ k,
86
+ { ...(v as RawInventoryDefinition), slots: withSequentialIndexes((v as RawInventoryDefinition).slots) },
87
+ ]),
88
+ ) as Record<T, InventoryTypeDefinition>
89
+
90
+ export const inventoryDefinitions = makeInventoryDefinitions({
64
91
  player: {
65
92
  name: 'player',
66
93
  title: 'Inventory',
67
- backgroundTexture: 'gui/container/inventory',
94
+ backgroundTexture: '1.21.11/textures/gui/container/inventory.png',
68
95
  backgroundWidth: 176,
69
96
  backgroundHeight: 166,
70
97
  slots: [
71
- // Result
72
- { index: 0, x: 154, y: 28, group: 'result', resultSlot: true },
73
- // Crafting 2x2
74
- ...gridSlots(1, 2, 2, 98, 18, 'crafting'),
75
- // Armor (head to feet = slots 5-8)
76
- { index: 5, x: 8, y: 8, group: 'armor', label: 'Head' },
77
- { index: 6, x: 8, y: 26, group: 'armor', label: 'Chest' },
78
- { index: 7, x: 8, y: 44, group: 'armor', label: 'Legs' },
79
- { index: 8, x: 8, y: 62, group: 'armor', label: 'Feet' },
80
- // Offhand
81
- { index: 45, x: 77, y: 62, group: 'offhand', label: 'Offhand' },
98
+ // Result (0)
99
+ { x: 154, y: 28, group: 'result', resultSlot: true },
100
+ // Crafting 2x2 (1-4)
101
+ ...gridSlots(2, 2, 98, 18, 'crafting'),
102
+ // Armor head-to-feet (5-8)
103
+ { x: 8, y: 8, group: 'armor', label: 'Head' },
104
+ { x: 8, y: 26, group: 'armor', label: 'Chest' },
105
+ { x: 8, y: 44, group: 'armor', label: 'Legs' },
106
+ { x: 8, y: 62, group: 'armor', label: 'Feet' },
82
107
  // Player inventory 9-35, hotbar 36-44
83
- ...playerInv(84, 9, 36),
108
+ ...playerInv(84),
109
+ // Offhand (45) — must come after playerInv so auto-index lands at 45
110
+ { x: 77, y: 62, group: 'offhand', label: 'Offhand' },
84
111
  ],
85
112
  },
86
113
 
87
114
  chest: {
88
115
  name: 'chest',
89
116
  title: 'Chest',
90
- backgroundTexture: 'gui/container/shulker_box',
117
+ backgroundTexture: '1.21.11/textures/gui/container/shulker_box.png',
91
118
  backgroundWidth: 176,
92
119
  backgroundHeight: 166,
93
120
  playerInventoryOffset: { x: 8, y: 84 },
94
121
  slots: [
95
- ...gridSlots(0, 9, 3, 8, 18, 'container'),
96
- // player inv: 27-53, hotbar: 54-62
97
- ...playerInv(84, 27, 54),
122
+ ...gridSlots(9, 3, 8, 18, 'container'),
123
+ ...playerInv(84),
98
124
  ],
99
125
  },
100
126
 
101
127
  generic_9x1: {
102
128
  name: 'generic_9x1',
103
129
  title: 'Container',
104
- backgroundTexture: 'gui/container/shulker_box',
130
+ backgroundTexture: '1.21.11/textures/gui/container/shulker_box.png',
105
131
  backgroundWidth: 176,
106
132
  backgroundHeight: 96,
107
133
  slots: [
108
- ...gridSlots(0, 9, 1, 8, 18, 'container'),
109
- // inv: 9-35, hotbar: 36-44
110
- ...playerInv(14, 9, 36),
134
+ ...gridSlots(9, 1, 8, 18, 'container'),
135
+ ...playerInv(14),
111
136
  ],
112
137
  },
113
138
 
114
139
  large_chest: {
115
140
  name: 'large_chest',
116
141
  title: 'Large Chest',
117
- backgroundTexture: 'gui/container/generic_54',
142
+ backgroundTexture: '1.21.11/textures/gui/container/generic_54.png',
118
143
  backgroundWidth: 176,
119
144
  backgroundHeight: 222,
120
145
  playerInventoryOffset: { x: 8, y: 140 },
121
146
  slots: [
122
- ...gridSlots(0, 9, 6, 8, 18, 'container'),
123
- // player inv: 54-80, hotbar: 81-89
124
- ...playerInv(140, 54, 81),
147
+ ...gridSlots(9, 6, 8, 18, 'container'),
148
+ ...playerInv(140),
125
149
  ],
126
150
  },
127
151
 
128
152
  crafting_table: {
129
153
  name: 'crafting_table',
130
154
  title: 'Crafting',
131
- backgroundTexture: 'gui/container/crafting_table',
155
+ backgroundTexture: '1.21.11/textures/gui/container/crafting_table.png',
132
156
  backgroundWidth: 176,
133
157
  backgroundHeight: 166,
134
158
  slots: [
135
- { index: 0, x: 124, y: 35, group: 'result', resultSlot: true },
136
- ...gridSlots(1, 3, 3, 30, 17, 'crafting'),
137
- // inv: 10-36, hotbar: 37-45
138
- ...playerInv(84, 10, 37),
159
+ { x: 124, y: 35, group: 'result', resultSlot: true },
160
+ ...gridSlots(3, 3, 30, 17, 'crafting'),
161
+ ...playerInv(84),
139
162
  ],
140
163
  },
141
164
 
142
165
  furnace: {
143
166
  name: 'furnace',
144
167
  title: 'Furnace',
145
- backgroundTexture: 'gui/container/furnace',
168
+ backgroundTexture: '1.21.11/textures/gui/container/furnace.png',
146
169
  backgroundWidth: 176,
147
170
  backgroundHeight: 166,
148
171
  properties: {
@@ -171,18 +194,17 @@ export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
171
194
  },
172
195
  ],
173
196
  slots: [
174
- { index: 0, x: 56, y: 17, group: 'input' },
175
- { index: 1, x: 56, y: 53, group: 'fuel', fuelSlot: true },
176
- { index: 2, x: 116, y: 35, group: 'result', resultSlot: true },
177
- // player inv: 3-29, hotbar: 30-38
178
- ...playerInv(84, 3, 30),
197
+ { x: 56, y: 17, group: 'input' },
198
+ { x: 56, y: 53, group: 'fuel', fuelSlot: true },
199
+ { x: 116, y: 35, group: 'result', resultSlot: true },
200
+ ...playerInv(84),
179
201
  ],
180
202
  },
181
203
 
182
204
  blast_furnace: {
183
205
  name: 'blast_furnace',
184
206
  title: 'Blast Furnace',
185
- backgroundTexture: 'gui/container/blast_furnace',
207
+ backgroundTexture: '1.21.11/textures/gui/container/blast_furnace.png',
186
208
  backgroundWidth: 176,
187
209
  backgroundHeight: 166,
188
210
  properties: {
@@ -210,17 +232,17 @@ export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
210
232
  },
211
233
  ],
212
234
  slots: [
213
- { index: 0, x: 56, y: 17, group: 'input' },
214
- { index: 1, x: 56, y: 53, group: 'fuel', fuelSlot: true },
215
- { index: 2, x: 116, y: 35, group: 'result', resultSlot: true },
216
- ...playerInv(84, 3, 30),
235
+ { x: 56, y: 17, group: 'input' },
236
+ { x: 56, y: 53, group: 'fuel', fuelSlot: true },
237
+ { x: 116, y: 35, group: 'result', resultSlot: true },
238
+ ...playerInv(84),
217
239
  ],
218
240
  },
219
241
 
220
242
  smoker: {
221
243
  name: 'smoker',
222
244
  title: 'Smoker',
223
- backgroundTexture: 'gui/container/smoker',
245
+ backgroundTexture: '1.21.11/textures/gui/container/smoker.png',
224
246
  backgroundWidth: 176,
225
247
  backgroundHeight: 166,
226
248
  properties: {
@@ -248,18 +270,17 @@ export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
248
270
  },
249
271
  ],
250
272
  slots: [
251
- { index: 0, x: 56, y: 17, group: 'input' },
252
- { index: 1, x: 56, y: 53, group: 'fuel', fuelSlot: true },
253
- { index: 2, x: 116, y: 35, group: 'result', resultSlot: true },
254
- // smoker: inv 3-29, hotbar: 30-38
255
- ...playerInv(84, 3, 30),
273
+ { x: 56, y: 17, group: 'input' },
274
+ { x: 56, y: 53, group: 'fuel', fuelSlot: true },
275
+ { x: 116, y: 35, group: 'result', resultSlot: true },
276
+ ...playerInv(84),
256
277
  ],
257
278
  },
258
279
 
259
280
  brewing_stand: {
260
281
  name: 'brewing_stand',
261
282
  title: 'Brewing Stand',
262
- backgroundTexture: 'gui/container/brewing_stand',
283
+ backgroundTexture: '1.21.11/textures/gui/container/brewing_stand.png',
263
284
  backgroundWidth: 176,
264
285
  backgroundHeight: 166,
265
286
  properties: {
@@ -285,52 +306,50 @@ export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
285
306
  },
286
307
  ],
287
308
  slots: [
288
- { index: 0, x: 56, y: 51, group: 'bottle', label: 'Left' },
289
- { index: 1, x: 79, y: 58, group: 'bottle', label: 'Center' },
290
- { index: 2, x: 102, y: 51, group: 'bottle', label: 'Right' },
291
- { index: 3, x: 79, y: 17, group: 'ingredient' },
292
- { index: 4, x: 17, y: 17, group: 'fuel', fuelSlot: true },
293
- // inv: 5-31, hotbar: 32-40
294
- ...playerInv(84, 5, 32),
309
+ { x: 56, y: 51, group: 'bottle', label: 'Left' },
310
+ { x: 79, y: 58, group: 'bottle', label: 'Center' },
311
+ { x: 102, y: 51, group: 'bottle', label: 'Right' },
312
+ { x: 79, y: 17, group: 'ingredient' },
313
+ { x: 17, y: 17, group: 'fuel', fuelSlot: true },
314
+ ...playerInv(84),
295
315
  ],
296
316
  },
297
317
 
298
318
  anvil: {
299
319
  name: 'anvil',
300
320
  title: 'Repair & Name',
301
- backgroundTexture: 'gui/container/anvil',
321
+ backgroundTexture: '1.21.11/textures/gui/container/anvil.png',
302
322
  backgroundWidth: 176,
303
323
  backgroundHeight: 166,
304
324
  properties: {
305
325
  repairCost: { dataSlot: 0, description: 'Level cost' },
306
326
  },
307
327
  slots: [
308
- { index: 0, x: 27, y: 47, group: 'input' },
309
- { index: 1, x: 76, y: 47, group: 'input' },
310
- { index: 2, x: 134, y: 47, group: 'result', resultSlot: true },
311
- // inv: 3-29, hotbar: 30-38
312
- ...playerInv(84, 3, 30),
328
+ { x: 27, y: 47, group: 'input' },
329
+ { x: 76, y: 47, group: 'input' },
330
+ { x: 134, y: 47, group: 'result', resultSlot: true },
331
+ ...playerInv(84),
313
332
  ],
314
333
  },
315
334
 
316
335
  grindstone: {
317
336
  name: 'grindstone',
318
337
  title: 'Repair & Disenchant',
319
- backgroundTexture: 'gui/container/grindstone',
338
+ backgroundTexture: '1.21.11/textures/gui/container/grindstone.png',
320
339
  backgroundWidth: 176,
321
340
  backgroundHeight: 166,
322
341
  slots: [
323
- { index: 0, x: 49, y: 19, group: 'input' },
324
- { index: 1, x: 49, y: 40, group: 'input' },
325
- { index: 2, x: 129, y: 34, group: 'result', resultSlot: true },
326
- ...playerInv(84, 3, 30),
342
+ { x: 49, y: 19, group: 'input' },
343
+ { x: 49, y: 40, group: 'input' },
344
+ { x: 129, y: 34, group: 'result', resultSlot: true },
345
+ ...playerInv(84),
327
346
  ],
328
347
  },
329
348
 
330
349
  enchanting_table: {
331
350
  name: 'enchanting_table',
332
351
  title: 'Enchant',
333
- backgroundTexture: 'gui/container/enchanting_table',
352
+ backgroundTexture: '1.21.11/textures/gui/container/enchanting_table.png',
334
353
  backgroundWidth: 176,
335
354
  backgroundHeight: 166,
336
355
  properties: {
@@ -343,270 +362,259 @@ export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
343
362
  bottomEnchantId: { dataSlot: 6, description: 'Bottom enchantment ID' },
344
363
  },
345
364
  slots: [
346
- { index: 0, x: 15, y: 47, group: 'enchant' },
347
- { index: 1, x: 35, y: 47, group: 'lapis', label: 'Lapis Lazuli' },
348
- // inv: 2-28, hotbar: 29-37
349
- ...playerInv(84, 2, 29),
365
+ { x: 15, y: 47, group: 'enchant' },
366
+ { x: 35, y: 47, group: 'lapis', label: 'Lapis Lazuli' },
367
+ ...playerInv(84),
350
368
  ],
351
369
  },
352
370
 
353
371
  smithing_table: {
354
372
  name: 'smithing_table',
355
373
  title: 'Upgrade Gear',
356
- backgroundTexture: 'gui/container/smithing',
357
- guiTextureVersion: '1.21.4',
374
+ backgroundTexture: '1.21.11/textures/gui/container/smithing.png',
358
375
  backgroundWidth: 176,
359
376
  backgroundHeight: 166,
377
+ titleOffset: {
378
+ x: 40,
379
+ y: 13,
380
+ },
360
381
  slots: [
361
- { index: 0, x: 8, y: 48, group: 'template', label: 'Template' },
362
- { index: 1, x: 26, y: 48, group: 'input', label: 'Base' },
363
- { index: 2, x: 44, y: 48, group: 'addition', label: 'Material' },
364
- { index: 3, x: 98, y: 48, group: 'result', resultSlot: true },
365
- // inv: 4-30, hotbar: 31-39
366
- ...playerInv(84, 4, 31),
382
+ { x: 8, y: 48, group: 'template', label: 'Template' },
383
+ { x: 26, y: 48, group: 'input', label: 'Base' },
384
+ { x: 44, y: 48, group: 'addition', label: 'Material' },
385
+ { x: 98, y: 48, group: 'result', resultSlot: true },
386
+ ...playerInv(84),
367
387
  ],
368
388
  },
369
389
 
370
390
  smithing_table_legacy: {
371
391
  name: 'smithing_table_legacy',
372
392
  title: 'Upgrade Gear',
373
- backgroundTexture: 'gui/container/smithing',
393
+ backgroundTexture: '1.16.4/textures/gui/container/smithing.png',
374
394
  backgroundWidth: 176,
375
395
  backgroundHeight: 166,
396
+ titleOffset: {
397
+ x: 50,
398
+ y: 13,
399
+ },
376
400
  slots: [
377
- { index: 0, x: 27, y: 47, group: 'input', label: 'Base' },
378
- { index: 1, x: 76, y: 47, group: 'addition', label: 'Material' },
379
- { index: 2, x: 134, y: 47, group: 'result', resultSlot: true },
380
- ...playerInv(84, 3, 30),
401
+ { x: 27, y: 47, group: 'input', label: 'Base' },
402
+ { x: 76, y: 47, group: 'addition', label: 'Material' },
403
+ { x: 134, y: 47, group: 'result', resultSlot: true },
404
+ ...playerInv(84),
381
405
  ],
382
406
  },
383
407
 
384
408
  hopper: {
385
409
  name: 'hopper',
386
410
  title: 'Item Hopper',
387
- backgroundTexture: 'gui/container/hopper',
411
+ backgroundTexture: '1.21.11/textures/gui/container/hopper.png',
388
412
  backgroundWidth: 176,
389
413
  backgroundHeight: 133,
390
414
  slots: [
391
- ...gridSlots(0, 5, 1, 44, 20, 'container'),
392
- ...Array.from({ length: 27 }, (_, i) => ({
393
- index: 5 + i,
394
- x: 8 + (i % 9) * SLOT_SIZE,
395
- y: 51 + Math.floor(i / 9) * SLOT_SIZE,
396
- group: 'inventory' as const,
397
- })),
398
- ...hotbarSlots(109, 32),
415
+ ...gridSlots(5, 1, 44, 20, 'container'),
416
+ ...gridSlots(9, 3, 8, 51, 'inventory'),
417
+ ...hotbarSlots(109),
399
418
  ],
400
419
  },
401
420
 
402
421
  dispenser: {
403
422
  name: 'dispenser',
404
423
  title: 'Dispenser',
405
- backgroundTexture: 'gui/container/dispenser',
424
+ backgroundTexture: '1.21.11/textures/gui/container/dispenser.png',
406
425
  backgroundWidth: 176,
407
426
  backgroundHeight: 166,
408
427
  slots: [
409
- ...gridSlots(0, 3, 3, 62, 17, 'container'),
410
- // inv: 9-35, hotbar: 36-44
411
- ...playerInv(84, 9, 36),
428
+ ...gridSlots(3, 3, 62, 17, 'container'),
429
+ ...playerInv(84),
412
430
  ],
413
431
  },
414
432
 
415
433
  dropper: {
416
434
  name: 'dropper',
417
435
  title: 'Dropper',
418
- backgroundTexture: 'gui/container/dispenser',
436
+ backgroundTexture: '1.21.11/textures/gui/container/dispenser.png',
419
437
  backgroundWidth: 176,
420
438
  backgroundHeight: 166,
421
439
  slots: [
422
- ...gridSlots(0, 3, 3, 62, 17, 'container'),
423
- ...playerInv(84, 9, 36),
440
+ ...gridSlots(3, 3, 62, 17, 'container'),
441
+ ...playerInv(84),
424
442
  ],
425
443
  },
426
444
 
427
445
  beacon: {
428
446
  name: 'beacon',
429
447
  title: 'Beacon',
430
- backgroundTexture: 'gui/container/beacon',
448
+ backgroundTexture: '1.21.11/textures/gui/container/beacon.png',
431
449
  backgroundWidth: 230,
432
450
  backgroundHeight: 219,
433
451
  slots: [
434
- { index: 0, x: 136, y: 110, group: 'payment', label: 'Payment' },
435
- // inv: 1-27, hotbar: 28-36
436
- ...playerInv(136, 1, 28),
452
+ { x: 136, y: 110, group: 'payment', label: 'Payment' },
453
+ ...playerInv(136),
437
454
  ],
438
455
  },
439
456
 
440
457
  horse: {
441
458
  name: 'horse',
442
459
  title: 'Horse',
443
- backgroundTexture: 'gui/container/horse',
460
+ backgroundTexture: '1.21.11/textures/gui/container/horse.png',
444
461
  backgroundWidth: 176,
445
462
  backgroundHeight: 166,
446
463
  slots: [
447
- { index: 0, x: 8, y: 18, group: 'saddle', label: 'Saddle' },
448
- { index: 1, x: 8, y: 36, group: 'armor', label: 'Horse Armor' },
449
- // inv: 2-28, hotbar: 29-37
450
- ...playerInv(84, 2, 29),
464
+ { x: 8, y: 18, group: 'saddle', label: 'Saddle' },
465
+ { x: 8, y: 36, group: 'armor', label: 'Horse Armor' },
466
+ ...playerInv(84),
451
467
  ],
452
468
  },
453
469
 
454
470
  donkey: {
455
471
  name: 'donkey',
456
472
  title: 'Donkey',
457
- backgroundTexture: 'gui/container/horse',
473
+ backgroundTexture: '1.21.11/textures/gui/container/horse.png',
458
474
  backgroundWidth: 176,
459
475
  backgroundHeight: 166,
460
476
  slots: [
461
- { index: 0, x: 8, y: 18, group: 'saddle', label: 'Saddle' },
462
- // chest items 2-16 (3x5) when has chest
463
- ...Array.from({ length: 15 }, (_, i) => ({
464
- index: 2 + i,
465
- x: 80 + (i % 5) * SLOT_SIZE,
466
- y: 18 + Math.floor(i / 5) * SLOT_SIZE,
467
- group: 'chest' as const,
468
- })),
469
- // inv: 17-43, hotbar: 44-52 (after 2 equipment + 15 chest)
470
- ...playerInv(84, 17, 44),
477
+ // Saddle (0); slot 1 (horse armor) is absent for donkeys — gap is intentional
478
+ { x: 8, y: 18, group: 'saddle', label: 'Saddle' },
479
+ // Chest items start at index 2; explicit index jumps the counter over the gap
480
+ ...gridSlots(5, 3, 80, 18, 'chest').map((s, i) => i === 0 ? { ...s, index: 2 } : s),
481
+ ...playerInv(84),
471
482
  ],
472
483
  },
473
484
 
474
485
  llama: {
475
486
  name: 'llama',
476
487
  title: 'Llama',
477
- backgroundTexture: 'gui/container/horse',
488
+ backgroundTexture: '1.21.11/textures/gui/container/horse.png',
478
489
  backgroundWidth: 176,
479
490
  backgroundHeight: 166,
480
491
  slots: [
481
- { index: 0, x: 8, y: 18, group: 'saddle', label: 'Carpet' },
482
- ...Array.from({ length: 15 }, (_, i) => ({
483
- index: 2 + i,
484
- x: 80 + (i % 5) * SLOT_SIZE,
485
- y: 18 + Math.floor(i / 5) * SLOT_SIZE,
486
- group: 'chest' as const,
487
- })),
488
- ...playerInv(84, 17, 44),
492
+ { x: 8, y: 18, group: 'saddle', label: 'Carpet' },
493
+ ...gridSlots(5, 3, 80, 18, 'chest').map((s, i) => i === 0 ? { ...s, index: 2 } : s),
494
+ ...playerInv(84),
489
495
  ],
490
496
  },
491
497
 
492
498
  villager: {
493
499
  name: 'villager',
494
500
  title: 'Villager',
495
- backgroundTexture: 'gui/container/villager2',
501
+ backgroundTexture: '1.14/textures/gui/container/villager2.png',
496
502
  backgroundWidth: 276,
497
503
  backgroundHeight: 166,
498
504
  slots: [
499
- { index: 0, x: 136, y: 37, group: 'trade_input1' },
500
- { index: 1, x: 162, y: 37, group: 'trade_input2' },
501
- { index: 2, x: 216, y: 37, group: 'trade_result', resultSlot: true },
502
- ...Array.from({ length: 27 }, (_, i) => ({
503
- index: 3 + i,
504
- x: 108 + (i % 9) * SLOT_SIZE,
505
- y: 84 + Math.floor(i / 9) * SLOT_SIZE,
506
- group: 'inventory' as const,
507
- })),
508
- ...hotbarSlots(142, 30, 108),
505
+ { x: 136, y: 37, group: 'trade_input1' },
506
+ { x: 162, y: 37, group: 'trade_input2' },
507
+ { x: 216, y: 37, group: 'trade_result', resultSlot: true },
508
+ ...gridSlots(9, 3, 108, 84, 'inventory'),
509
+ ...hotbarSlots(142, 108),
509
510
  ],
510
511
  },
511
512
 
512
513
  shulker_box: {
513
514
  name: 'shulker_box',
514
515
  title: 'Shulker Box',
515
- backgroundTexture: 'gui/container/shulker_box',
516
+ backgroundTexture: '1.21.11/textures/gui/container/shulker_box.png',
516
517
  backgroundWidth: 176,
517
518
  backgroundHeight: 166,
518
519
  slots: [
519
- ...gridSlots(0, 9, 3, 8, 18, 'container'),
520
- ...playerInv(84, 27, 54),
520
+ ...gridSlots(9, 3, 8, 18, 'container'),
521
+ ...playerInv(84),
521
522
  ],
522
523
  },
523
524
 
524
525
  barrel: {
525
526
  name: 'barrel',
526
527
  title: 'Barrel',
527
- backgroundTexture: 'gui/container/shulker_box',
528
+ backgroundTexture: '1.21.11/textures/gui/container/shulker_box.png',
528
529
  backgroundWidth: 176,
529
530
  backgroundHeight: 166,
530
531
  slots: [
531
- ...gridSlots(0, 9, 3, 8, 18, 'container'),
532
- ...playerInv(84, 27, 54),
532
+ ...gridSlots(9, 3, 8, 18, 'container'),
533
+ ...playerInv(84),
533
534
  ],
534
535
  },
535
536
 
536
537
  cartography_table: {
537
538
  name: 'cartography_table',
538
539
  title: 'Cartography Table',
539
- backgroundTexture: 'gui/container/cartography_table',
540
+ backgroundTexture: '1.21.11/textures/gui/container/cartography_table.png',
540
541
  backgroundWidth: 176,
541
542
  backgroundHeight: 166,
542
543
  slots: [
543
- { index: 0, x: 15, y: 15, group: 'input', label: 'Map' },
544
- { index: 1, x: 15, y: 39, group: 'input', label: 'Paper/Map' },
545
- { index: 2, x: 145, y: 39, group: 'result', resultSlot: true },
546
- ...playerInv(84, 3, 30),
544
+ { x: 15, y: 15, group: 'input', label: 'Map' },
545
+ { x: 15, y: 39, group: 'input', label: 'Paper/Map' },
546
+ { x: 145, y: 39, group: 'result', resultSlot: true },
547
+ ...playerInv(84),
547
548
  ],
548
549
  },
549
550
 
550
551
  loom: {
551
552
  name: 'loom',
552
553
  title: 'Loom',
553
- backgroundTexture: 'gui/container/loom',
554
+ backgroundTexture: '1.21.11/textures/gui/container/loom.png',
554
555
  backgroundWidth: 176,
555
556
  backgroundHeight: 166,
556
557
  slots: [
557
- { index: 0, x: 13, y: 26, group: 'input', label: 'Banner' },
558
- { index: 1, x: 33, y: 26, group: 'input', label: 'Dye' },
559
- { index: 2, x: 23, y: 45, group: 'input', label: 'Pattern' },
560
- { index: 3, x: 143, y: 58, group: 'result', resultSlot: true },
561
- ...playerInv(84, 4, 31),
558
+ { x: 13, y: 26, group: 'input', label: 'Banner' },
559
+ { x: 33, y: 26, group: 'input', label: 'Dye' },
560
+ { x: 23, y: 45, group: 'input', label: 'Pattern' },
561
+ { x: 143, y: 58, group: 'result', resultSlot: true },
562
+ ...playerInv(84),
562
563
  ],
563
564
  },
564
565
 
565
566
  stonecutter: {
566
567
  name: 'stonecutter',
567
568
  title: 'Stonecutter',
568
- backgroundTexture: 'gui/container/stonecutter',
569
+ backgroundTexture: '1.21.11/textures/gui/container/stonecutter.png',
569
570
  backgroundWidth: 176,
570
571
  backgroundHeight: 166,
571
572
  slots: [
572
- { index: 0, x: 20, y: 33, group: 'input' },
573
- { index: 1, x: 143, y: 33, group: 'result', resultSlot: true },
574
- ...playerInv(84, 2, 29),
573
+ { x: 20, y: 33, group: 'input' },
574
+ { x: 143, y: 33, group: 'result', resultSlot: true },
575
+ ...playerInv(84),
575
576
  ],
576
577
  },
577
578
 
578
- lectern: {
579
- name: 'lectern',
580
- title: 'Lectern',
581
- backgroundTexture: 'gui/container/lectern',
582
- backgroundWidth: 176,
583
- backgroundHeight: 166,
584
- slots: [],
585
- },
586
-
587
579
  crafter: {
588
580
  name: 'crafter',
589
581
  title: 'Crafter',
590
- backgroundTexture: 'gui/container/crafter',
591
- guiTextureVersion: '1.21.4',
582
+ backgroundTexture: '1.21.11/textures/gui/container/crafter.png',
592
583
  backgroundWidth: 176,
593
584
  backgroundHeight: 166,
594
585
  slots: [
595
- ...gridSlots(0, 3, 3, 30, 17, 'crafting'),
596
- { index: 9, x: 124, y: 35, group: 'result', resultSlot: true },
597
- ...playerInv(84, 10, 37),
586
+ ...gridSlots(3, 3, 30, 17, 'crafting'),
587
+ { x: 124, y: 35, group: 'result', resultSlot: true },
588
+ ...playerInv(84),
598
589
  ],
599
590
  },
600
591
 
601
592
  creative: {
602
593
  name: 'creative',
603
594
  title: 'Creative Inventory',
604
- backgroundTexture: 'gui/container/creative_inventory/tab_items',
595
+ backgroundTexture: '1.21.11/textures/gui/container/creative_inventory/tab_items.png',
605
596
  backgroundWidth: 195,
606
597
  backgroundHeight: 136,
607
598
  slots: [
608
- ...gridSlots(0, 9, 5, 9, 48, 'body'),
609
- ...hotbarSlots(142, 0),
599
+ ...gridSlots(9, 5, 9, 48, 'body'),
600
+ // Creative hotbar indices start at 0 (separate window mapping), explicit to override counter
601
+ ...hotbarSlots(142).map((s, i) => ({ ...s, index: i })),
610
602
  ],
611
603
  },
612
- }
604
+
605
+ // Standalone hotbar HUD container — mirrors layouts.mjs `Hotbar`
606
+ // Background: gui/widgets (182×22 hotbar strip), slots at HUD pixel coords
607
+ // Indices 36–44 are the standard player hotbar
608
+ hotbar: {
609
+ name: 'hotbar',
610
+ title: '',
611
+ backgroundTexture: '1.15/textures/gui/widgets.png',
612
+ backgroundWidth: 182,
613
+ backgroundHeight: 22,
614
+ slots: [
615
+ // 9 hotbar slots (36–44) within the 182×22 hotbar strip.
616
+ // index: 36 on the first slot starts the running counter at 36.
617
+ ...hudHotbarSlots(),
618
+ ],
619
+ },
620
+ })