prismarine-windows 2.6.0 → 2.7.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.
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/commands.yml +22 -0
- package/.github/workflows/npm-publish.yml +1 -1
- package/HISTORY.md +15 -0
- package/index.d.ts +129 -9
- package/index.js +8 -12
- package/lib/Window.js +271 -94
- package/package.json +10 -7
- package/test/test.js +356 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Repo Commands
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment: # Handle comment commands
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request: # Handle renamed PRs
|
|
7
|
+
types: [edited]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
comment-trigger:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Check out repository
|
|
14
|
+
uses: actions/checkout@v3
|
|
15
|
+
- name: Run command handlers
|
|
16
|
+
uses: PrismarineJS/prismarine-repo-actions@master
|
|
17
|
+
with:
|
|
18
|
+
# NOTE: You must specify a Personal Access Token (PAT) with repo access here. While you can use the default GITHUB_TOKEN, actions taken with it will not trigger other actions, so if you have a CI workflow, commits created by this action will not trigger it.
|
|
19
|
+
token: ${{ secrets.PAT_PASSWORD }}
|
|
20
|
+
# See `Options` section below for more info on these options
|
|
21
|
+
install-command: npm install
|
|
22
|
+
/fixlint.fix-command: npm run fix
|
package/HISTORY.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
### 2.7.0
|
|
2
|
+
* [More click modes (with tests!) (#74)](https://github.com/PrismarineJS/prismarine-windows/commit/9f19fb357b2a96e09060fa5c2393b9041cc869fe) (thanks @kaduvert)
|
|
3
|
+
* [Add command gh workflow allowing to use release command in comments (#98)](https://github.com/PrismarineJS/prismarine-windows/commit/6ddc90092d63f3838136d83e583d7c1d7ace3ae2) (thanks @rom1504)
|
|
4
|
+
* [fix](https://github.com/PrismarineJS/prismarine-windows/commit/8ab955e465ce9586bfba0286ef1340b86dc914e4) (thanks @rom1504)
|
|
5
|
+
* [Update to node 18.0.0 (#96)](https://github.com/PrismarineJS/prismarine-windows/commit/98912036737aa4c2e2f743b12bdb66eac5073889) (thanks @rom1504)
|
|
6
|
+
* [Bump @types/node from 18.16.13 to 20.2.1 (#95)](https://github.com/PrismarineJS/prismarine-windows/commit/cf7401e3caf1ff2a5f582338b3815eb1f80187b2) (thanks @dependabot[bot])
|
|
7
|
+
* [Fix slot layout for dispencer and droppers (#86)](https://github.com/PrismarineJS/prismarine-windows/commit/b42576a0e3eac7b42c843fa96c80191218efe97a) (thanks @IceTank)
|
|
8
|
+
* [Change slots from Array<Item> to Array<Item | null> (#85)](https://github.com/PrismarineJS/prismarine-windows/commit/652cd992b27254dcc7173e93ec8a112fd56cb459) (thanks @yurei-dll)
|
|
9
|
+
* [use registry.supportFeature (#82)](https://github.com/PrismarineJS/prismarine-windows/commit/3d4514ab56274cfe1fc5d578e3e101d6e41391f7) (thanks @Epirito)
|
|
10
|
+
* [Bump typed-emitter from 1.4.0 to 2.1.0 (#76)](https://github.com/PrismarineJS/prismarine-windows/commit/86e6aa0727c7e6a4ffa0602bf1aba7fd9d70f71f) (thanks @dependabot[bot])
|
|
11
|
+
|
|
12
|
+
## 2.6.1
|
|
13
|
+
|
|
14
|
+
* fix typo
|
|
15
|
+
|
|
1
16
|
## 2.6.0
|
|
2
17
|
|
|
3
18
|
* mcData to registry refactoring + anticipation of feature check refactoring
|
package/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
26
26
|
/**
|
|
27
27
|
* Map of slot index to Item instance. null if the slot is empty
|
|
28
28
|
*/
|
|
29
|
-
slots: Array<Item>;
|
|
29
|
+
slots: Array<Item | null>;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Slot from where the player inventory start in the window
|
|
@@ -49,7 +49,7 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
49
49
|
craftingResultSlot: number;
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* Boolean only false for chests in pre-1.14 versions.
|
|
53
53
|
*/
|
|
54
54
|
requiresConfirmation: boolean;
|
|
55
55
|
|
|
@@ -58,15 +58,117 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
58
58
|
*/
|
|
59
59
|
selectedItem: Item | null;
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* accepts Clicks of with any mode, mouseButton and slot
|
|
63
|
+
* @param click click object to accept
|
|
64
|
+
* @param gamemode to know when certain clicks are allowed
|
|
65
|
+
*/
|
|
66
|
+
acceptClick(click: Click, gamemode: number): void;
|
|
67
|
+
|
|
68
|
+
/** @deprecated use {@link acceptClick} instead */
|
|
62
69
|
acceptOutsideWindowClick(click: Click): void;
|
|
70
|
+
|
|
71
|
+
/** @deprecated use {@link acceptClick} instead */
|
|
63
72
|
acceptInventoryClick(click: Click): void;
|
|
73
|
+
|
|
74
|
+
/** @deprecated use {@link acceptClick} instead */
|
|
64
75
|
acceptNonInventorySwapAreaClick(click: Click): void;
|
|
65
|
-
|
|
76
|
+
|
|
77
|
+
/** @deprecated use {@link acceptClick} instead */
|
|
66
78
|
acceptSwapAreaLeftClick(click: Click): void;
|
|
79
|
+
|
|
80
|
+
/** @deprecated use {@link acceptClick} instead */
|
|
67
81
|
acceptSwapAreaRightClick(click: Click): void;
|
|
82
|
+
|
|
83
|
+
/** @deprecated use {@link acceptClick} instead */
|
|
68
84
|
acceptCraftingClick(click: Click): void;
|
|
69
85
|
|
|
86
|
+
/**
|
|
87
|
+
* See click types here https://wiki.vg/Protocol#Click_Window
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Accepts click mode 0 with mouseButton 0 or 1
|
|
92
|
+
*/
|
|
93
|
+
mouseClick(click: Click): void;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Accepts click mode 1 with mouseButton 0 or 1 (identical behaviour)
|
|
97
|
+
*/
|
|
98
|
+
shiftClick(click: Click): void;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Accepts click mode 2 with mouseButton 0 (hotbarStart) to 8 (hotbarEnd) representing the hotbar slots
|
|
102
|
+
*/
|
|
103
|
+
numberClick(click: Click): void;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Accepts click mode 3 with mouseButton 2 (gets a stack of the item at the slot into the selectedItem)
|
|
107
|
+
*/
|
|
108
|
+
middleClick(click: Click, gamemode: number): void;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Accepts click mode 4 with mouseButton 0 (drops one of the item) or 1 (drops all of the item)
|
|
112
|
+
*/
|
|
113
|
+
dropClick(click: Click): void;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Fills within specified range with given item and dumps remaining items if present and possible
|
|
117
|
+
* @param item item used to fill slots
|
|
118
|
+
* @param start start slot to begin the search from
|
|
119
|
+
* @param end end slot to end the search
|
|
120
|
+
* @param lastToFirst if true the matching Slots will be filled from the back
|
|
121
|
+
*/
|
|
122
|
+
fillAndDump(item: Item, start: number, end: number, lastToFirst: boolean): void;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Fills slots with specified item
|
|
126
|
+
* @param slots slots to fill with the item
|
|
127
|
+
* @param lastToFirst if true the matching Slots will be filled from the back
|
|
128
|
+
*/
|
|
129
|
+
fillSlotsWithItem(slots: Array<Item>, item: Item, lastToFirst: boolean): void;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Fills slot with specified item
|
|
133
|
+
* @param itemToFill item of which the count should be increased
|
|
134
|
+
* @param itemToTake item of which the count should be decreased
|
|
135
|
+
*/
|
|
136
|
+
fillSlotWithItem(itemToFill: Item, itemToTake: Item): void;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Fills slot with selectedItem (the item held in mouse cursor)
|
|
140
|
+
* @param item item of which the count should be increased
|
|
141
|
+
* @param untilFull if true as many as possible will be transfered
|
|
142
|
+
*/
|
|
143
|
+
fillSlotWithSelectedItem (item: Item, untilFull: boolean): void;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Searches for empty slot to dump the specified item
|
|
147
|
+
* @param item item which should be dumped
|
|
148
|
+
* @param start start slot to begin the search from
|
|
149
|
+
* @param end end slot to end the search
|
|
150
|
+
* @param lastToFirst if true item slot will be searched from the back
|
|
151
|
+
*/
|
|
152
|
+
dumpItem(item: Item, start: number, end: number, lastToFirst: boolean): void;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Splits the slot in half and holds the split in mouse cursor
|
|
156
|
+
* @param item item to split
|
|
157
|
+
*/
|
|
158
|
+
splitSlot(item: Item): void;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Swaps item with the item in mouse cursor
|
|
162
|
+
* @param item item to swap with
|
|
163
|
+
*/
|
|
164
|
+
swapSelectedItem(item: Item): void;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Drops item held in mouse cursor
|
|
168
|
+
* @param untilEmpty if true whole item stack will be dropped (else just one)
|
|
169
|
+
*/
|
|
170
|
+
dropSelectedItem(untilEmpty: boolean): void;
|
|
171
|
+
|
|
70
172
|
/**
|
|
71
173
|
* Change the slot to contain the newItem. Emit the updateSlot events.
|
|
72
174
|
* @param slot {number}
|
|
@@ -75,6 +177,18 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
75
177
|
updateSlot(slot: number, newItem: Item): void;
|
|
76
178
|
|
|
77
179
|
/**
|
|
180
|
+
* Returns array of items in the given range matching the one specified
|
|
181
|
+
* @param start start slot to begin the search from
|
|
182
|
+
* @param end end slot to end the search
|
|
183
|
+
* @param itemType numerical id that you are looking for
|
|
184
|
+
* @param metadata metadata value that you are looking for. null means unspecified
|
|
185
|
+
* @param notFull (optional) - if true, means that the returned item should not be at its stackSize
|
|
186
|
+
* @param nbt nbt data for the item you are looking for. null means unspecified
|
|
187
|
+
*/
|
|
188
|
+
findItemsRange(start: number, end: number, itemType: number, metadata: number | null, notFull: boolean, nbt: any): Array<Item> | null;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Returns item in the given range matching the one specified
|
|
78
192
|
* @param start start slot to begin the search from
|
|
79
193
|
* @param end end slot to end the search
|
|
80
194
|
* @param itemType numerical id that you are looking for
|
|
@@ -99,7 +213,7 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
99
213
|
* @param metadata metadata value that you are looking for. null means unspecified
|
|
100
214
|
* @param notFull (optional) - if true, means that the returned item should not be at its stackSize
|
|
101
215
|
*/
|
|
102
|
-
findInventoryItem(itemType: number
|
|
216
|
+
findInventoryItem(itemType: number, metadata: number | null, notFull: boolean): Item | null;
|
|
103
217
|
|
|
104
218
|
/**
|
|
105
219
|
* Search in the container of the window
|
|
@@ -107,7 +221,7 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
107
221
|
* @param metadata metadata value that you are looking for. null means unspecified
|
|
108
222
|
* @param notFull (optional) - if true, means that the returned item should not be at its stackSize
|
|
109
223
|
*/
|
|
110
|
-
findContainerItem(itemType: number, metadata: number | null, notFull: boolean): Item | null
|
|
224
|
+
findContainerItem(itemType: number, metadata: number | null, notFull: boolean): Item | null;
|
|
111
225
|
|
|
112
226
|
/**
|
|
113
227
|
* Return the id of the first empty slot between start and end
|
|
@@ -132,6 +246,13 @@ export class Window<T = unknown> extends (EventEmitter as new <T>() => TypedEmit
|
|
|
132
246
|
*/
|
|
133
247
|
firstEmptyInventorySlot(hotbarFirst?: boolean): number | null;
|
|
134
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Returns how much items there are ignoring what the item is, between slots start and end
|
|
251
|
+
* @param start
|
|
252
|
+
* @param end
|
|
253
|
+
*/
|
|
254
|
+
sumRange(start: number, end: number): number;
|
|
255
|
+
|
|
135
256
|
/**
|
|
136
257
|
* Returns how many item you have of the given type, between slots start and end
|
|
137
258
|
* @param start
|
|
@@ -206,14 +327,13 @@ export interface WindowInfo {
|
|
|
206
327
|
export interface WindowsExports {
|
|
207
328
|
createWindow(id: number, type: number | string, title: string, slotCount?: number): Window;
|
|
208
329
|
Window: typeof Window;
|
|
209
|
-
windows: {[key
|
|
330
|
+
windows: {[key: string]: WindowInfo};
|
|
210
331
|
}
|
|
211
|
-
|
|
212
332
|
export declare function loader(mcVersion: string): WindowsExports;
|
|
213
333
|
|
|
214
334
|
export default loader;
|
|
215
335
|
|
|
216
|
-
export type WindowName =
|
|
336
|
+
export type WindowName =
|
|
217
337
|
'minecraft:inventory' |
|
|
218
338
|
'minecraft:generic_9x1' |
|
|
219
339
|
'minecraft:generic_9x2' |
|
package/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
function loader (registryOrVersion) {
|
|
2
2
|
const registry = typeof registryOrVersion === 'string' ? require('prismarine-registry')(registryOrVersion) : registryOrVersion
|
|
3
3
|
const Item = require('prismarine-item')(registry)
|
|
4
|
-
const Window = require('./lib/Window')(Item)
|
|
4
|
+
const Window = require('./lib/Window')(Item, registry)
|
|
5
5
|
|
|
6
6
|
let windows
|
|
7
|
-
|
|
8
|
-
if (registry.isNewerOrEqualTo('1.14')) { // this line should be discarded for the one above when the corresponding feature is added to minecraft-data
|
|
7
|
+
if (registry.supportFeature('village&pillageInventoryWindows')) {
|
|
9
8
|
// https://wiki.vg/Inventory
|
|
10
9
|
windows = {}
|
|
11
10
|
let protocolId = -1
|
|
@@ -30,8 +29,7 @@ function loader (registryOrVersion) {
|
|
|
30
29
|
windows['minecraft:loom'] = { type: protocolId++, inventory: { start: 4, end: 39 }, slots: 40, craft: 3, requireConfirmation: true }
|
|
31
30
|
windows['minecraft:merchant'] = { type: protocolId++, inventory: { start: 3, end: 38 }, slots: 39, craft: 2, requireConfirmation: true }
|
|
32
31
|
windows['minecraft:shulker_box'] = { type: protocolId++, inventory: { start: 27, end: 62 }, slots: 63, craft: -1, requireConfirmation: true }
|
|
33
|
-
|
|
34
|
-
if (registry.isNewerOrEqualTo('1.16')) { // this line should be discarded for the one above when the corresponding feature is added to minecraft-data
|
|
32
|
+
if (registry.supportFeature('netherUpdateInventoryWindows')) {
|
|
35
33
|
windows['minecraft:smithing'] = { type: protocolId++, inventory: { start: 3, end: 38 }, slots: 39, craft: 2, requireConfirmation: true }
|
|
36
34
|
}
|
|
37
35
|
windows['minecraft:smoker'] = { type: protocolId++, inventory: { start: 3, end: 38 }, slots: 39, craft: 2, requireConfirmation: true }
|
|
@@ -39,14 +37,13 @@ function loader (registryOrVersion) {
|
|
|
39
37
|
windows['minecraft:stonecutter'] = { type: protocolId++, inventory: { start: 2, end: 37 }, slots: 38, craft: 1, requireConfirmation: true }
|
|
40
38
|
} else {
|
|
41
39
|
// https://wiki.vg/index.php?title=Inventory&oldid=14093
|
|
42
|
-
|
|
43
|
-
const inventorySlots = registry.isNewerOrEqualTo('1.9') ? 46 : 45// this line should be discarded for the one above when the corresponding feature is added to minecraft-data
|
|
40
|
+
const inventorySlots = registry.supportFeature('shieldSlot') ? 46 : 45
|
|
44
41
|
windows = {
|
|
45
|
-
'minecraft:inventory': { type: 'minecraft:inventory', inventory: { start: 9, end: 44 }, slots: inventorySlots
|
|
42
|
+
'minecraft:inventory': { type: 'minecraft:inventory', inventory: { start: 9, end: 44 }, slots: inventorySlots, craft: 0, requireConfirmation: true },
|
|
46
43
|
'minecraft:chest': null,
|
|
47
44
|
'minecraft:crafting_table': { type: 'minecraft:crafting_table', inventory: { start: 10, end: 45 }, slots: 46, craft: 0, requireConfirmation: true },
|
|
48
45
|
'minecraft:furnace': { type: 'minecraft:furnace', inventory: { start: 3, end: 38 }, slots: 39, craft: 2, requireConfirmation: true },
|
|
49
|
-
'minecraft:dispenser': { type: 'minecraft:dispenser', inventory: { start:
|
|
46
|
+
'minecraft:dispenser': { type: 'minecraft:dispenser', inventory: { start: 3 * 3, end: 3 * 3 + 35 }, slots: 3 * 3 + 36, craft: -1, requireConfirmation: true },
|
|
50
47
|
'minecraft:enchanting_table': { type: 'minecraft:enchanting_table', inventory: { start: 2, end: 37 }, slots: 38, craft: -1, requireConfirmation: true },
|
|
51
48
|
'minecraft:brewing_stand': { type: 'minecraft:brewing_stand', inventory: { start: 5, end: 40 }, slots: 41, craft: -1, requireConfirmation: true },
|
|
52
49
|
'minecraft:container': null,
|
|
@@ -54,7 +51,7 @@ function loader (registryOrVersion) {
|
|
|
54
51
|
'minecraft:beacon': { type: 'minecraft:beacon', inventory: { start: 1, end: 36 }, slots: 37, craft: -1, requireConfirmation: true },
|
|
55
52
|
'minecraft:anvil': { type: 'minecraft:anvil', inventory: { start: 3, end: 38 }, slots: 39, craft: 2, requireConfirmation: true },
|
|
56
53
|
'minecraft:hopper': { type: 'minecraft:hopper', inventory: { start: 5, end: 40 }, slots: 41, craft: -1, requireConfirmation: true },
|
|
57
|
-
'minecraft:dropper': { type: 'minecraft:dropper', inventory: { start:
|
|
54
|
+
'minecraft:dropper': { type: 'minecraft:dropper', inventory: { start: 3 * 3, end: 3 * 3 + 35 }, slots: 3 * 3 + 36, craft: -1, requireConfirmation: true },
|
|
58
55
|
'minecraft:shulker_box': { type: 'minecraft:shulker_box', inventory: { start: 27, end: 62 }, slots: 63, craft: -1, requireConfirmation: true },
|
|
59
56
|
EntityHorse: null
|
|
60
57
|
}
|
|
@@ -71,8 +68,7 @@ function loader (registryOrVersion) {
|
|
|
71
68
|
|
|
72
69
|
return {
|
|
73
70
|
createWindow: (id, type, title, slotCount = undefined) => {
|
|
74
|
-
let winData = windowByType.get(type)
|
|
75
|
-
if (!winData) winData = windows[type]
|
|
71
|
+
let winData = windowByType.get(type) ?? windows[type]
|
|
76
72
|
if (!winData) {
|
|
77
73
|
if (slotCount === undefined) return null
|
|
78
74
|
winData = {
|
package/lib/Window.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const assert = require('assert')
|
|
2
2
|
const EventEmitter = require('events').EventEmitter
|
|
3
3
|
|
|
4
|
-
module.exports = (Item) => {
|
|
4
|
+
module.exports = (Item, registry) => {
|
|
5
5
|
return class Window extends EventEmitter {
|
|
6
6
|
constructor (id, type, title, slotCount,
|
|
7
7
|
inventorySlotsRange = { start: 27, end: 62 },
|
|
@@ -22,106 +22,224 @@ module.exports = (Item) => {
|
|
|
22
22
|
this.selectedItem = null
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
acceptClick (click) {
|
|
26
|
-
|
|
25
|
+
acceptClick (click, gamemode = 0) {
|
|
26
|
+
const { mode, slot, mouseButton } = click
|
|
27
|
+
assert.ok(
|
|
28
|
+
(mode >= 0 && mode <= 6) &&
|
|
29
|
+
(mouseButton >= 0 && mouseButton <= 8) &&
|
|
30
|
+
((slot >= 0 && slot < this.inventoryEnd) || slot === -999 ||
|
|
31
|
+
(this.type === 'minecraft:inventory' && slot === 45)),
|
|
32
|
+
'invalid operation')
|
|
33
|
+
|
|
34
|
+
// can not use structuredClone because of
|
|
35
|
+
// potentially incompatible node versions
|
|
36
|
+
const oldSlots = JSON.parse(JSON.stringify(this.slots))
|
|
37
|
+
|
|
38
|
+
switch (click.mode) {
|
|
39
|
+
case 0:
|
|
40
|
+
assert.ok(mouseButton <= 1, 'invalid operation')
|
|
41
|
+
this.mouseClick(click)
|
|
42
|
+
break
|
|
43
|
+
|
|
44
|
+
case 1:
|
|
45
|
+
assert.ok(mouseButton <= 1, 'invalid operation')
|
|
46
|
+
this.shiftClick(click)
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
case 2:
|
|
50
|
+
assert.ok(mouseButton <= 8, 'invalid operation')
|
|
51
|
+
this.numberClick(click)
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
case 3:
|
|
55
|
+
assert.ok(mouseButton === 2, 'invalid operation')
|
|
56
|
+
this.middleClick(click, gamemode)
|
|
57
|
+
break
|
|
58
|
+
|
|
59
|
+
case 4:
|
|
60
|
+
assert.ok(mouseButton <= 1, 'invalid operation')
|
|
61
|
+
this.dropClick(click)
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
case 5:
|
|
65
|
+
assert.ok([1, 5, 9, 2, 6, 10].includes(mouseButton), 'invalid operation')
|
|
66
|
+
this.dragClick(click, gamemode)
|
|
67
|
+
break
|
|
68
|
+
|
|
69
|
+
case 6:
|
|
70
|
+
assert.ok(mouseButton === 0, 'invalid operation')
|
|
71
|
+
this.doubleClick(click)
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// this is required to satisfy mc versions >= 1.17
|
|
76
|
+
return this.getChangedSlotsAsNotch(oldSlots, this.slots)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
mouseClick (click) {
|
|
27
80
|
if (click.slot === -999) {
|
|
28
|
-
|
|
29
|
-
} else if (click.slot >= this.inventoryStart && click.slot < this.inventoryEnd) {
|
|
30
|
-
return this.acceptInventoryClick(click)
|
|
31
|
-
} else if (click.slot === this.craftingResultSlot) {
|
|
32
|
-
return this.acceptCraftingClick(click)
|
|
81
|
+
this.dropSelectedItem(click.mouseButton === 0)
|
|
33
82
|
} else {
|
|
34
|
-
|
|
83
|
+
let { item } = click
|
|
84
|
+
if (click.mouseButton === 0) { // left click
|
|
85
|
+
if (item && this.selectedItem) {
|
|
86
|
+
if (Item.equal(item, this.selectedItem, false)) {
|
|
87
|
+
if (click.slot === this.craftingResultSlot) {
|
|
88
|
+
if (item.count + this.selectedItem.count > item.stackSize) {
|
|
89
|
+
this.selectedItem.count += item.count
|
|
90
|
+
this.updateSlot(item.slot, null)
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
this.fillSlotWithSelectedItem(item, true)
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
this.swapSelectedItem(click.slot, item)
|
|
97
|
+
}
|
|
98
|
+
} else if (this.selectedItem || item) {
|
|
99
|
+
this.swapSelectedItem(click.slot, item)
|
|
100
|
+
}
|
|
101
|
+
} else if (click.mouseButton === 1) { // right click
|
|
102
|
+
if (this.selectedItem) {
|
|
103
|
+
if (item) {
|
|
104
|
+
if (Item.equal(item, this.selectedItem, false)) {
|
|
105
|
+
this.fillSlotWithSelectedItem(item, false)
|
|
106
|
+
} else {
|
|
107
|
+
this.swapSelectedItem(click.slot, item)
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
item = new Item(this.selectedItem.type, 0, this.selectedItem.metadata, this.selectedItem.nbt)
|
|
111
|
+
this.updateSlot(click.slot, item)
|
|
112
|
+
this.fillSlotWithSelectedItem(item, false)
|
|
113
|
+
}
|
|
114
|
+
} else if (item) {
|
|
115
|
+
if (click.slot !== this.craftingResultSlot) {
|
|
116
|
+
this.splitSlot(item)
|
|
117
|
+
} else {
|
|
118
|
+
this.swapSelectedItem(click.slot, item)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
35
122
|
}
|
|
36
123
|
}
|
|
37
124
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
125
|
+
shiftClick (click) {
|
|
126
|
+
const { item } = click
|
|
127
|
+
if (!item) return
|
|
128
|
+
if (this.type === 'minecraft:inventory') {
|
|
129
|
+
if (click.slot < this.inventoryStart) {
|
|
130
|
+
this.fillAndDump(item, this.inventoryStart, this.inventoryEnd, click.slot === this.craftingResultSlot)
|
|
131
|
+
} else {
|
|
132
|
+
if (click.slot >= this.inventoryStart && click.slot < this.inventoryEnd - 10) {
|
|
133
|
+
this.fillAndDump(item, this.hotbarStart, this.inventoryEnd)
|
|
134
|
+
} else {
|
|
135
|
+
this.fillAndDump(item, this.inventoryStart, this.inventoryEnd)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
45
138
|
} else {
|
|
46
|
-
|
|
139
|
+
if (click.slot < this.inventoryStart) {
|
|
140
|
+
this.fillAndDump(item, this.inventoryStart, this.inventoryEnd, this.craftingResultSlot === -1 || click.slot === this.craftingResultSlot)
|
|
141
|
+
} else {
|
|
142
|
+
this.fillAndDump(item, 0, this.inventoryStart - 1)
|
|
143
|
+
}
|
|
47
144
|
}
|
|
48
|
-
return []
|
|
49
145
|
}
|
|
50
146
|
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
147
|
+
numberClick (click) {
|
|
148
|
+
if (this.selectedItem) return
|
|
149
|
+
const { item } = click
|
|
150
|
+
const hotbarSlot = this.hotbarStart + click.mouseButton
|
|
151
|
+
const itemAtHotbarSlot = this.slots[hotbarSlot]
|
|
152
|
+
if (item) {
|
|
153
|
+
if (itemAtHotbarSlot) {
|
|
154
|
+
if (this.type === 'minecraft:inventory' || registry.version['>=']('1.9')) {
|
|
155
|
+
this.updateSlot(click.slot, itemAtHotbarSlot)
|
|
156
|
+
this.updateSlot(hotbarSlot, item)
|
|
157
|
+
} else {
|
|
158
|
+
this.dumpItem(itemAtHotbarSlot, this.hotbarStart, this.inventoryEnd)
|
|
159
|
+
if (this.slots[hotbarSlot]) {
|
|
160
|
+
this.dumpItem(itemAtHotbarSlot, this.inventoryStart, this.hotbarStart - 1)
|
|
161
|
+
}
|
|
162
|
+
if (this.slots[hotbarSlot] === null) {
|
|
163
|
+
this.updateSlot(item.slot, null)
|
|
164
|
+
this.updateSlot(hotbarSlot, item)
|
|
165
|
+
let slots = this.findItemsRange(this.hotbarStart, this.inventoryEnd, itemAtHotbarSlot.type, itemAtHotbarSlot.metadata, true, itemAtHotbarSlot.nbt)
|
|
166
|
+
slots.push(...this.findItemsRange(this.inventoryStart, this.hotbarStart - 1, itemAtHotbarSlot.type, itemAtHotbarSlot.metadata, true, itemAtHotbarSlot.nbt))
|
|
167
|
+
slots = slots.filter(slot => slot.slot !== itemAtHotbarSlot.slot)
|
|
168
|
+
this.fillSlotsWithItem(slots, itemAtHotbarSlot)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
55
171
|
} else {
|
|
56
|
-
|
|
172
|
+
this.updateSlot(item.slot, null)
|
|
173
|
+
this.updateSlot(hotbarSlot, item)
|
|
57
174
|
}
|
|
58
|
-
} else if (click.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
assert.ok(false, 'unimplemented')
|
|
175
|
+
} else if (itemAtHotbarSlot && click.slot !== this.craftingResultSlot) {
|
|
176
|
+
this.updateSlot(click.slot, itemAtHotbarSlot)
|
|
177
|
+
this.updateSlot(hotbarSlot, null)
|
|
62
178
|
}
|
|
63
179
|
}
|
|
64
180
|
|
|
65
|
-
|
|
66
|
-
|
|
181
|
+
middleClick (click, gamemode) {
|
|
182
|
+
if (this.selectedItem) return
|
|
183
|
+
const { item } = click
|
|
184
|
+
if (gamemode === 1 && item) {
|
|
185
|
+
this.selectedItem = new Item(item.type, item.stackSize, item.metadata, item.nbt)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
dropClick (click) {
|
|
190
|
+
if (this.selectedItem) return
|
|
67
191
|
if (click.mouseButton === 0) {
|
|
68
|
-
|
|
192
|
+
if (--click.item.count === 0) this.updateSlot(click.slot, null)
|
|
69
193
|
} else if (click.mouseButton === 1) {
|
|
70
|
-
|
|
71
|
-
} else {
|
|
72
|
-
assert.ok(false, 'unimplemented')
|
|
194
|
+
this.updateSlot(click.slot, null)
|
|
73
195
|
}
|
|
74
196
|
}
|
|
75
197
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
assert.
|
|
198
|
+
dragClick (click, gamemode) {
|
|
199
|
+
// unimplemented
|
|
200
|
+
assert.ok(false, 'unimplemented')
|
|
201
|
+
}
|
|
79
202
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
this.selectedItem = null
|
|
97
|
-
} else {
|
|
98
|
-
this.updateSlot(click.slot, new Item(this.selectedItem.type, 1,
|
|
99
|
-
this.selectedItem.metadata, this.selectedItem.nbt))
|
|
100
|
-
this.selectedItem.count -= 1
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} else if (item) {
|
|
104
|
-
// grab 1/2 of item
|
|
105
|
-
this.selectedItem = new Item(item.type, Math.ceil(item.count / 2),
|
|
106
|
-
item.metadata, item.nbt)
|
|
107
|
-
item.count -= this.selectedItem.count
|
|
108
|
-
if (item.count === 0) this.updateSlot(item.slot, null)
|
|
203
|
+
doubleClick (click) {
|
|
204
|
+
// unimplemented
|
|
205
|
+
assert.ok(false, 'unimplemented')
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
acceptOutsideWindowClick = this.acceptClick
|
|
209
|
+
acceptInventoryClick = this.acceptClick
|
|
210
|
+
acceptNonInventorySwapAreaClick = this.acceptClick
|
|
211
|
+
acceptSwapAreaLeftClick = this.acceptClick
|
|
212
|
+
acceptSwapAreaRightClick = this.acceptClick
|
|
213
|
+
acceptCraftingClick = this.acceptClick
|
|
214
|
+
|
|
215
|
+
fillAndDump (item, start, end, lastToFirst = false) {
|
|
216
|
+
this.fillSlotsWithItem(this.findItemsRange(start, end, item.type, item.metadata, true, item.nbt, true), item, lastToFirst)
|
|
217
|
+
if (this.slots[item.slot]) {
|
|
218
|
+
this.dumpItem(item, start, end, lastToFirst)
|
|
109
219
|
}
|
|
110
|
-
return [{
|
|
111
|
-
location: click.slot,
|
|
112
|
-
item: Item.toNotch(this.slots[click.slot])
|
|
113
|
-
}]
|
|
114
220
|
}
|
|
115
221
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
222
|
+
fillSlotsWithItem (slots, item, lastToFirst = false) {
|
|
223
|
+
while (slots.length && item.count) {
|
|
224
|
+
this.fillSlotWithItem(lastToFirst ? slots.pop() : slots.shift(), item)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
119
227
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
228
|
+
fillSlotWithItem (itemToFill, itemToTake) {
|
|
229
|
+
const newCount = itemToFill.count + itemToTake.count
|
|
230
|
+
const leftover = newCount - itemToFill.stackSize
|
|
231
|
+
if (leftover <= 0) {
|
|
232
|
+
itemToFill.count = newCount
|
|
233
|
+
itemToTake.count = 0
|
|
234
|
+
this.updateSlot(itemToTake.slot, null)
|
|
235
|
+
} else {
|
|
236
|
+
itemToFill.count = itemToFill.stackSize
|
|
237
|
+
itemToTake.count = leftover
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fillSlotWithSelectedItem (item, untilFull) {
|
|
242
|
+
if (untilFull) {
|
|
125
243
|
const newCount = item.count + this.selectedItem.count
|
|
126
244
|
const leftover = newCount - item.stackSize
|
|
127
245
|
if (leftover <= 0) {
|
|
@@ -132,15 +250,36 @@ module.exports = (Item) => {
|
|
|
132
250
|
this.selectedItem.count = leftover
|
|
133
251
|
}
|
|
134
252
|
} else {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
253
|
+
if (item.count + 1 <= item.stackSize) {
|
|
254
|
+
item.count++
|
|
255
|
+
if (--this.selectedItem.count === 0) this.selectedItem = null
|
|
256
|
+
}
|
|
139
257
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
dumpItem (item, start, end, lastToFirst = false) {
|
|
261
|
+
const emptySlot = lastToFirst ? this.lastEmptySlotRange(start, end) : this.firstEmptySlotRange(start, end)
|
|
262
|
+
if (emptySlot !== null && emptySlot !== this.craftingResultSlot) {
|
|
263
|
+
const slot = item.slot
|
|
264
|
+
this.updateSlot(emptySlot, item)
|
|
265
|
+
this.updateSlot(slot, null)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
splitSlot (item) {
|
|
270
|
+
if (!item) return
|
|
271
|
+
this.selectedItem = new Item(item.type, Math.ceil(item.count / 2), item.metadata, item.nbt)
|
|
272
|
+
item.count -= this.selectedItem.count
|
|
273
|
+
if (item.count === 0) this.updateSlot(item.slot, null)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
swapSelectedItem (slot, item) {
|
|
277
|
+
this.updateSlot(slot, this.selectedItem)
|
|
278
|
+
this.selectedItem = item
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
dropSelectedItem (untilEmpty) {
|
|
282
|
+
if (untilEmpty || --this.selectedItem.count === 0) this.selectedItem = null
|
|
144
283
|
}
|
|
145
284
|
|
|
146
285
|
updateSlot (slot, newItem) {
|
|
@@ -152,7 +291,18 @@ module.exports = (Item) => {
|
|
|
152
291
|
this.emit(`updateSlot:${slot}`, oldItem, newItem)
|
|
153
292
|
}
|
|
154
293
|
|
|
155
|
-
|
|
294
|
+
findItemsRange (start, end, itemType, metadata, notFull, nbt, withoutCraftResultSlot = false) {
|
|
295
|
+
const items = []
|
|
296
|
+
while (start < end) {
|
|
297
|
+
const item = this.findItemRange(start, end, itemType, metadata, notFull, nbt, withoutCraftResultSlot)
|
|
298
|
+
if (!item) break
|
|
299
|
+
start = item.slot + 1
|
|
300
|
+
items.push(item)
|
|
301
|
+
}
|
|
302
|
+
return items
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
findItemRange (start, end, itemType, metadata, notFull, nbt, withoutCraftResultSlot = false) {
|
|
156
306
|
assert.notStrictEqual(itemType, null)
|
|
157
307
|
for (let i = start; i < end; ++i) {
|
|
158
308
|
const item = this.slots[i]
|
|
@@ -160,7 +310,8 @@ module.exports = (Item) => {
|
|
|
160
310
|
item && itemType === item.type &&
|
|
161
311
|
(metadata == null || metadata === item.metadata) &&
|
|
162
312
|
(!notFull || item.count < item.stackSize) &&
|
|
163
|
-
(nbt == null || JSON.stringify(nbt) === JSON.stringify(item.nbt))
|
|
313
|
+
(nbt == null || JSON.stringify(nbt) === JSON.stringify(item.nbt)) &&
|
|
314
|
+
!(item.slot === this.craftingResultSlot && withoutCraftResultSlot)) {
|
|
164
315
|
return item
|
|
165
316
|
}
|
|
166
317
|
}
|
|
@@ -196,7 +347,14 @@ module.exports = (Item) => {
|
|
|
196
347
|
|
|
197
348
|
firstEmptySlotRange (start, end) {
|
|
198
349
|
for (let i = start; i < end; ++i) {
|
|
199
|
-
if (
|
|
350
|
+
if (this.slots[i] === null) return i
|
|
351
|
+
}
|
|
352
|
+
return null
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
lastEmptySlotRange (start, end) {
|
|
356
|
+
for (let i = end; i >= start; i--) {
|
|
357
|
+
if (this.slots[i] === null) return i
|
|
200
358
|
}
|
|
201
359
|
return null
|
|
202
360
|
}
|
|
@@ -217,6 +375,15 @@ module.exports = (Item) => {
|
|
|
217
375
|
return this.firstEmptySlotRange(this.inventoryStart, this.inventoryEnd)
|
|
218
376
|
}
|
|
219
377
|
|
|
378
|
+
sumRange (start, end) {
|
|
379
|
+
let sum = 0
|
|
380
|
+
for (let i = start; i < end; i++) {
|
|
381
|
+
const item = this.slots[i]
|
|
382
|
+
if (item) sum += item.count
|
|
383
|
+
}
|
|
384
|
+
return sum
|
|
385
|
+
}
|
|
386
|
+
|
|
220
387
|
countRange (start, end, itemType, metadata) {
|
|
221
388
|
let sum = 0
|
|
222
389
|
for (let i = start; i < end; ++i) {
|
|
@@ -259,7 +426,7 @@ module.exports = (Item) => {
|
|
|
259
426
|
emptySlotCount () {
|
|
260
427
|
let count = 0
|
|
261
428
|
for (let i = this.inventoryStart; i < this.inventoryEnd; ++i) {
|
|
262
|
-
if (
|
|
429
|
+
if (this.slots[i] === null) count += 1
|
|
263
430
|
}
|
|
264
431
|
return count
|
|
265
432
|
}
|
|
@@ -268,11 +435,21 @@ module.exports = (Item) => {
|
|
|
268
435
|
return this.requiresConfirmation
|
|
269
436
|
}
|
|
270
437
|
|
|
271
|
-
|
|
272
|
-
assert.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
438
|
+
getChangedSlotsAsNotch (slots1, slots2) {
|
|
439
|
+
assert.equal(slots1.length, slots2.length)
|
|
440
|
+
|
|
441
|
+
const changedSlots = []
|
|
442
|
+
|
|
443
|
+
for (let i = 0; i < slots2.length; i++) {
|
|
444
|
+
if (!Item.equal(slots1[i], slots2[i])) {
|
|
445
|
+
changedSlots.push({
|
|
446
|
+
location: i,
|
|
447
|
+
item: Item.toNotch(slots2[i])
|
|
448
|
+
})
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return changedSlots
|
|
276
453
|
}
|
|
277
454
|
|
|
278
455
|
clear (blockId, count) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prismarine-windows",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Represent minecraft windows",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
7
|
+
"mocha_test": "mocha --reporter spec --exit",
|
|
8
|
+
"test": "npm run mocha_test",
|
|
9
|
+
"pretest": "npm run lint",
|
|
8
10
|
"lint": "standard",
|
|
9
11
|
"fix": "standard --fix"
|
|
10
12
|
},
|
|
@@ -13,13 +15,14 @@
|
|
|
13
15
|
"url": "git+ssh://git@github.com/PrismarineJS/prismarine-windows.git"
|
|
14
16
|
},
|
|
15
17
|
"dependencies": {
|
|
16
|
-
"prismarine-
|
|
17
|
-
"prismarine-
|
|
18
|
-
"typed-emitter": "^1.0
|
|
18
|
+
"prismarine-item": "^1.12.2",
|
|
19
|
+
"prismarine-registry": "^1.7.0",
|
|
20
|
+
"typed-emitter": "^2.1.0"
|
|
19
21
|
},
|
|
20
22
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^
|
|
22
|
-
"standard": "^17.0.0"
|
|
23
|
+
"@types/node": "^20.2.1",
|
|
24
|
+
"standard": "^17.0.0",
|
|
25
|
+
"mocha": "^10.0.0"
|
|
23
26
|
},
|
|
24
27
|
"keywords": [
|
|
25
28
|
"minecraft",
|
package/test/test.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
const { describe, it, afterEach } = require('mocha')
|
|
4
|
+
const assert = require('assert')
|
|
5
|
+
|
|
6
|
+
const mcVersion = '1.8'
|
|
7
|
+
const registry = require('prismarine-registry')(mcVersion)
|
|
8
|
+
const windows = require('..')(registry)
|
|
9
|
+
const Item = require('prismarine-item')(registry)
|
|
10
|
+
|
|
11
|
+
function getAssertFunctions (slot) {
|
|
12
|
+
return {
|
|
13
|
+
isEmpty: function () {
|
|
14
|
+
assert.equal(slot, null, 'slot is not empty')
|
|
15
|
+
return this
|
|
16
|
+
},
|
|
17
|
+
isNotEmpty: function () {
|
|
18
|
+
assert.notEqual(slot, null, 'slot is empty')
|
|
19
|
+
return this
|
|
20
|
+
},
|
|
21
|
+
hasCount: function (count) {
|
|
22
|
+
this.isNotEmpty()
|
|
23
|
+
assert.equal(slot.count, count, 'slot count doesn\'t match')
|
|
24
|
+
return this
|
|
25
|
+
},
|
|
26
|
+
hasMaxCount: function () {
|
|
27
|
+
this.isNotEmpty()
|
|
28
|
+
assert.equal(slot.count, slot.stackSize, 'slot doesn\'t have max count')
|
|
29
|
+
return this
|
|
30
|
+
},
|
|
31
|
+
hasType: function (type) {
|
|
32
|
+
this.isNotEmpty()
|
|
33
|
+
assert.equal(slot.type, type, `slot doesn't have type ${type}`)
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getActualSlot (slotShorthand, inventoryEnd) {
|
|
40
|
+
// negative values start from the end of the inventory
|
|
41
|
+
return slotShorthand < 0 ? inventoryEnd + slotShorthand : slotShorthand
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getSlotShorthand (actualSlot, inventoryEnd) {
|
|
45
|
+
const negativeSlotShorthand = actualSlot - inventoryEnd
|
|
46
|
+
return Math.abs(negativeSlotShorthand) < actualSlot ? negativeSlotShorthand : actualSlot
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function createTestWindow (type, slotCount = undefined) {
|
|
50
|
+
const testWindow = windows.createWindow(1, 'minecraft:' + type, type, slotCount ?? (type === 'chest' ? 27 : undefined))
|
|
51
|
+
|
|
52
|
+
testWindow.prepareSlot = function (slotShorthand, count, type) {
|
|
53
|
+
testWindow.updateSlot(getActualSlot(slotShorthand, testWindow.inventoryEnd), new Item(type, count))
|
|
54
|
+
|
|
55
|
+
return testWindow
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
testWindow.prepareSelectedItem = function (count, type) {
|
|
59
|
+
testWindow.selectedItem = new Item(type, count)
|
|
60
|
+
|
|
61
|
+
return testWindow
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
testWindow.executeClick = function (mode, mouseButton, slotShorthand, gamemode) {
|
|
65
|
+
const slot = getActualSlot(slotShorthand, testWindow.inventoryEnd)
|
|
66
|
+
const click = {
|
|
67
|
+
slot,
|
|
68
|
+
mouseButton,
|
|
69
|
+
mode,
|
|
70
|
+
windowId: testWindow.id,
|
|
71
|
+
item: slot === -999 ? null : testWindow.slots[slot]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
testWindow.updatedSlots = []
|
|
75
|
+
testWindow.assertedSlots = []
|
|
76
|
+
const onSlotUpdate = (slot) => {
|
|
77
|
+
slot = +slot
|
|
78
|
+
if (!testWindow.updatedSlots.includes(slot)) {
|
|
79
|
+
testWindow.updatedSlots.push(slot)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
testWindow.on('updateSlot', onSlotUpdate)
|
|
84
|
+
|
|
85
|
+
const changedSlots = testWindow.acceptClick(click, gamemode)
|
|
86
|
+
|
|
87
|
+
testWindow.removeListener('updateSlot', onSlotUpdate)
|
|
88
|
+
|
|
89
|
+
return changedSlots
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
testWindow.assertSlot = function (slotShorthand) {
|
|
93
|
+
let slot = getActualSlot(slotShorthand, testWindow.inventoryEnd)
|
|
94
|
+
if (!testWindow.assertedSlots.includes(slot)) {
|
|
95
|
+
testWindow.assertedSlots.push(slot)
|
|
96
|
+
}
|
|
97
|
+
slot = testWindow.slots[slot]
|
|
98
|
+
return getAssertFunctions(slot)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
testWindow.assertSelectedItem = function () {
|
|
102
|
+
return getAssertFunctions(testWindow.selectedItem)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return testWindow
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// item ids
|
|
109
|
+
const firstItem = 1
|
|
110
|
+
const secondItem = 2
|
|
111
|
+
|
|
112
|
+
let testWindow = null
|
|
113
|
+
|
|
114
|
+
afterEach(function () {
|
|
115
|
+
testWindow.updatedSlots.forEach((slot) => {
|
|
116
|
+
if (!testWindow.assertedSlots.includes(slot)) {
|
|
117
|
+
const slotShorthand = getSlotShorthand(slot, testWindow.inventoryEnd)
|
|
118
|
+
assert.fail(`slot ${slotShorthand}${slotShorthand !== slot ? ` (actual: ${slot})` : ''} updated, but has not been asserted`)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('mode 0 | normal click', () => {
|
|
124
|
+
describe('mouseButton 0', () => {
|
|
125
|
+
it('pickup item', () => {
|
|
126
|
+
testWindow = createTestWindow('chest')
|
|
127
|
+
.prepareSlot(0, 64, firstItem)
|
|
128
|
+
|
|
129
|
+
testWindow.executeClick(0, 0, 0)
|
|
130
|
+
|
|
131
|
+
testWindow.assertSlot(0).isEmpty()
|
|
132
|
+
testWindow.assertSelectedItem().hasCount(64).hasType(firstItem)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
describe('mouseButton 1', () => {
|
|
137
|
+
it('drop one of selected Item into a slot (same item)', () => {
|
|
138
|
+
testWindow = createTestWindow('chest')
|
|
139
|
+
.prepareSelectedItem(64, firstItem)
|
|
140
|
+
.prepareSlot(0, 1, firstItem)
|
|
141
|
+
|
|
142
|
+
testWindow.executeClick(0, 1, 0)
|
|
143
|
+
|
|
144
|
+
testWindow.assertSelectedItem().hasCount(63)
|
|
145
|
+
testWindow.assertSlot(0).hasCount(2)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('drop one of selected Item into a slot (empty slot)', () => {
|
|
149
|
+
testWindow = createTestWindow('chest')
|
|
150
|
+
.prepareSelectedItem(64, firstItem)
|
|
151
|
+
|
|
152
|
+
testWindow.executeClick(0, 1, 0)
|
|
153
|
+
|
|
154
|
+
testWindow.assertSelectedItem().hasCount(63)
|
|
155
|
+
testWindow.assertSlot(0).hasCount(1)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('drop selected Item into a slot (empty slot)', () => {
|
|
159
|
+
testWindow = createTestWindow('chest')
|
|
160
|
+
.prepareSelectedItem(1, firstItem)
|
|
161
|
+
|
|
162
|
+
testWindow.executeClick(0, 1, 0)
|
|
163
|
+
|
|
164
|
+
testWindow.assertSelectedItem().isEmpty()
|
|
165
|
+
testWindow.assertSlot(0).hasCount(1)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe('mouseButton 0', () => {
|
|
170
|
+
it('drop all of selected Item into a slot (almost full with same item)', () => {
|
|
171
|
+
testWindow = createTestWindow('chest')
|
|
172
|
+
.prepareSelectedItem(64, firstItem)
|
|
173
|
+
.prepareSlot(0, 60, firstItem)
|
|
174
|
+
|
|
175
|
+
testWindow.executeClick(0, 0, 0)
|
|
176
|
+
|
|
177
|
+
testWindow.assertSelectedItem().hasCount(60)
|
|
178
|
+
testWindow.assertSlot(0).hasCount(64)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('drop selected Item into empty slot', () => {
|
|
182
|
+
testWindow = createTestWindow('chest')
|
|
183
|
+
.prepareSelectedItem(1, firstItem)
|
|
184
|
+
|
|
185
|
+
testWindow.executeClick(0, 0, 0)
|
|
186
|
+
|
|
187
|
+
testWindow.assertSelectedItem().isEmpty()
|
|
188
|
+
testWindow.assertSlot(0).isNotEmpty().hasCount(1).hasType(firstItem)
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe('mode 1 | shift click', () => {
|
|
194
|
+
describe('mouseButton 0', () => {
|
|
195
|
+
it('shift out of chest into inventory', () => {
|
|
196
|
+
testWindow = createTestWindow('chest')
|
|
197
|
+
.prepareSlot(0, 64, firstItem)
|
|
198
|
+
|
|
199
|
+
testWindow.executeClick(1, 0, 0)
|
|
200
|
+
|
|
201
|
+
testWindow.assertSlot(0).isEmpty()
|
|
202
|
+
testWindow.assertSlot(-1).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('shift out of inventory into chest', () => {
|
|
206
|
+
testWindow = createTestWindow('chest')
|
|
207
|
+
.prepareSlot(-1, 64, firstItem)
|
|
208
|
+
|
|
209
|
+
testWindow.executeClick(1, 0, -1)
|
|
210
|
+
|
|
211
|
+
testWindow.assertSlot(0).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
212
|
+
testWindow.assertSlot(-1).isEmpty()
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it.skip('shift out of inventory into armor slot (unimplemented)', () => {
|
|
216
|
+
const someBoots = registry.itemsByName.leather_boots.id
|
|
217
|
+
testWindow = createTestWindow('inventory')
|
|
218
|
+
.prepareSlot(-1, 1, someBoots)
|
|
219
|
+
|
|
220
|
+
testWindow.executeClick(1, 0, -1)
|
|
221
|
+
|
|
222
|
+
testWindow.assertSlot(-1).isEmpty()
|
|
223
|
+
// 8 is the slot for boots
|
|
224
|
+
testWindow.assertSlot(8).isNotEmpty().hasType(someBoots).hasCount(1)
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
describe('mode 2 | number click', () => {
|
|
230
|
+
describe('mouseButton 0', () => {
|
|
231
|
+
it('from full slot into empty slot', () => {
|
|
232
|
+
testWindow = createTestWindow('chest')
|
|
233
|
+
.prepareSlot(0, 64, firstItem)
|
|
234
|
+
|
|
235
|
+
// mouseButton 0 = hotbarStart
|
|
236
|
+
// slot 0 = windowStart
|
|
237
|
+
testWindow.executeClick(2, 0, 0)
|
|
238
|
+
|
|
239
|
+
testWindow.assertSlot(-9).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
240
|
+
testWindow.assertSlot(0).isEmpty()
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('from empty slot into full slot', () => {
|
|
244
|
+
testWindow = createTestWindow('chest')
|
|
245
|
+
// -9 = hotbarStart
|
|
246
|
+
.prepareSlot(-9, 64, firstItem)
|
|
247
|
+
|
|
248
|
+
// mouseButton 0 = hotbarStart
|
|
249
|
+
// slot 0 = windowStart
|
|
250
|
+
testWindow.executeClick(2, 0, 0)
|
|
251
|
+
|
|
252
|
+
testWindow.assertSlot(-9).isEmpty()
|
|
253
|
+
testWindow.assertSlot(0).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('from slot with item to slot with same item', () => {
|
|
257
|
+
testWindow = createTestWindow('chest')
|
|
258
|
+
.prepareSlot(-9, 32, firstItem)
|
|
259
|
+
.prepareSlot(0, 32, firstItem)
|
|
260
|
+
|
|
261
|
+
testWindow.executeClick(2, 0, 0)
|
|
262
|
+
|
|
263
|
+
if (registry.version['>=']('1.9')) {
|
|
264
|
+
// expect nothing to happen
|
|
265
|
+
testWindow.assertSlot(0).hasCount(32)
|
|
266
|
+
testWindow.assertSlot(-9).hasCount(32)
|
|
267
|
+
} else {
|
|
268
|
+
testWindow.assertSlot(0).isEmpty()
|
|
269
|
+
testWindow.assertSlot(-9).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
270
|
+
testWindow.assertSlot(-8).isEmpty()
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('from slot with item to slot with different item', () => {
|
|
275
|
+
testWindow = createTestWindow('chest')
|
|
276
|
+
.prepareSlot(0, 64, firstItem)
|
|
277
|
+
.prepareSlot(-1, 64, secondItem)
|
|
278
|
+
|
|
279
|
+
// mouseButton 8 = hotbarEnd
|
|
280
|
+
// slot 0 = windowStart
|
|
281
|
+
testWindow.executeClick(2, 8, 0)
|
|
282
|
+
|
|
283
|
+
if (registry.version['>=']('1.9')) {
|
|
284
|
+
testWindow.assertSlot(0).isNotEmpty().hasCount(64).hasType(secondItem)
|
|
285
|
+
testWindow.assertSlot(-1).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
286
|
+
} else {
|
|
287
|
+
testWindow.assertSlot(0).isEmpty()
|
|
288
|
+
testWindow.assertSlot(-1).isNotEmpty().hasCount(64).hasType(firstItem)
|
|
289
|
+
testWindow.assertSlot(-9).isNotEmpty().hasCount(64).hasType(secondItem)
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
describe('mode 3 | middle click', () => {
|
|
296
|
+
describe('mouseButton 2', () => {
|
|
297
|
+
it('get stack into selectedItem (gamemode 0)', () => {
|
|
298
|
+
testWindow = createTestWindow('chest')
|
|
299
|
+
.prepareSlot(0, 1, firstItem)
|
|
300
|
+
|
|
301
|
+
testWindow.executeClick(3, 2, 0)
|
|
302
|
+
|
|
303
|
+
// expect no change
|
|
304
|
+
testWindow.assertSelectedItem().isEmpty()
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
it('get stack into selectedItem (gamemode 1)', () => {
|
|
308
|
+
testWindow = createTestWindow('chest')
|
|
309
|
+
.prepareSlot(0, 1, firstItem)
|
|
310
|
+
|
|
311
|
+
testWindow.executeClick(3, 2, 0, /* gamemode = */1)
|
|
312
|
+
|
|
313
|
+
testWindow.assertSelectedItem().isNotEmpty().hasCount(64).hasType(firstItem)
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
describe('mode 4 | drop click', () => {
|
|
319
|
+
describe('mouseButton 0', () => {
|
|
320
|
+
it('drop 1 of stack', () => {
|
|
321
|
+
testWindow = createTestWindow('chest')
|
|
322
|
+
.prepareSlot(0, 64, firstItem)
|
|
323
|
+
|
|
324
|
+
testWindow.executeClick(4, 0, 0)
|
|
325
|
+
|
|
326
|
+
testWindow.assertSlot(0).isNotEmpty().hasCount(64 - 1).hasType(firstItem)
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
describe('mouseButton 1', () => {
|
|
331
|
+
it('drop full stack', () => {
|
|
332
|
+
testWindow = createTestWindow('chest')
|
|
333
|
+
.prepareSlot(0, 64, firstItem)
|
|
334
|
+
|
|
335
|
+
testWindow.executeClick(4, 1, 0)
|
|
336
|
+
|
|
337
|
+
testWindow.assertSlot(0).isEmpty()
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it('returning changed slots works', () => {
|
|
343
|
+
testWindow = createTestWindow('chest')
|
|
344
|
+
.prepareSlot(0, 64, firstItem)
|
|
345
|
+
|
|
346
|
+
const changedSlots = testWindow.executeClick(0, 0, 0)
|
|
347
|
+
|
|
348
|
+
testWindow.assertSlot(0).isEmpty()
|
|
349
|
+
testWindow.assertSelectedItem().isNotEmpty()
|
|
350
|
+
|
|
351
|
+
assert.ok(
|
|
352
|
+
changedSlots.find(changedSlot => changedSlot.location === 0) !== undefined &&
|
|
353
|
+
Item.fromNotch(changedSlots.find(changedSlot => changedSlot.location === 0)?.item) === null
|
|
354
|
+
// selectedItem isn't included in changedSlots
|
|
355
|
+
)
|
|
356
|
+
})
|