prismarine-windows 2.6.1 → 2.8.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 +16 -0
- package/index.d.ts +129 -9
- package/index.js +7 -11
- package/lib/Window.js +257 -96
- package/package.json +10 -7
- package/test/test.js +367 -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,19 @@
|
|
|
1
|
+
### 2.8.0
|
|
2
|
+
* [Export changedSlots computation to mineflayer (#102)](https://github.com/PrismarineJS/prismarine-windows/commit/98c3d66ae6aca733bb234d487686e2e534d1924d) (thanks @kaduvert)
|
|
3
|
+
* [Ensure numberClick with same slots doesn't do anything (#103)](https://github.com/PrismarineJS/prismarine-windows/commit/50e6bfbdb9c3bc26f06ea45760527b2da53eb1f3) (thanks @kaduvert)
|
|
4
|
+
* [Fix broken behaviour with craftingResultSlots (#101)](https://github.com/PrismarineJS/prismarine-windows/commit/51cc06e0f7dd39ddbb617962dfc1bb659fc172ce) (thanks @kaduvert)
|
|
5
|
+
|
|
6
|
+
### 2.7.0
|
|
7
|
+
* [More click modes (with tests!) (#74)](https://github.com/PrismarineJS/prismarine-windows/commit/9f19fb357b2a96e09060fa5c2393b9041cc869fe) (thanks @kaduvert)
|
|
8
|
+
* [Add command gh workflow allowing to use release command in comments (#98)](https://github.com/PrismarineJS/prismarine-windows/commit/6ddc90092d63f3838136d83e583d7c1d7ace3ae2) (thanks @rom1504)
|
|
9
|
+
* [fix](https://github.com/PrismarineJS/prismarine-windows/commit/8ab955e465ce9586bfba0286ef1340b86dc914e4) (thanks @rom1504)
|
|
10
|
+
* [Update to node 18.0.0 (#96)](https://github.com/PrismarineJS/prismarine-windows/commit/98912036737aa4c2e2f743b12bdb66eac5073889) (thanks @rom1504)
|
|
11
|
+
* [Bump @types/node from 18.16.13 to 20.2.1 (#95)](https://github.com/PrismarineJS/prismarine-windows/commit/cf7401e3caf1ff2a5f582338b3815eb1f80187b2) (thanks @dependabot[bot])
|
|
12
|
+
* [Fix slot layout for dispencer and droppers (#86)](https://github.com/PrismarineJS/prismarine-windows/commit/b42576a0e3eac7b42c843fa96c80191218efe97a) (thanks @IceTank)
|
|
13
|
+
* [Change slots from Array<Item> to Array<Item | null> (#85)](https://github.com/PrismarineJS/prismarine-windows/commit/652cd992b27254dcc7173e93ec8a112fd56cb459) (thanks @yurei-dll)
|
|
14
|
+
* [use registry.supportFeature (#82)](https://github.com/PrismarineJS/prismarine-windows/commit/3d4514ab56274cfe1fc5d578e3e101d6e41391f7) (thanks @Epirito)
|
|
15
|
+
* [Bump typed-emitter from 1.4.0 to 2.1.0 (#76)](https://github.com/PrismarineJS/prismarine-windows/commit/86e6aa0727c7e6a4ffa0602bf1aba7fd9d70f71f) (thanks @dependabot[bot])
|
|
16
|
+
|
|
1
17
|
## 2.6.1
|
|
2
18
|
|
|
3
19
|
* fix typo
|
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
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,225 @@ module.exports = (Item) => {
|
|
|
22
22
|
this.selectedItem = null
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
acceptClick (click) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
switch (click.mode) {
|
|
35
|
+
case 0:
|
|
36
|
+
assert.ok(mouseButton <= 1, 'invalid operation')
|
|
37
|
+
return this.mouseClick(click)
|
|
38
|
+
|
|
39
|
+
case 1:
|
|
40
|
+
assert.ok(mouseButton <= 1, 'invalid operation')
|
|
41
|
+
return this.shiftClick(click)
|
|
42
|
+
|
|
43
|
+
case 2:
|
|
44
|
+
assert.ok(mouseButton <= 8, 'invalid operation')
|
|
45
|
+
return this.numberClick(click)
|
|
46
|
+
|
|
47
|
+
case 3:
|
|
48
|
+
assert.ok(mouseButton === 2, 'invalid operation')
|
|
49
|
+
return this.middleClick(click, gamemode)
|
|
50
|
+
|
|
51
|
+
case 4:
|
|
52
|
+
assert.ok(mouseButton <= 1, 'invalid operation')
|
|
53
|
+
return this.dropClick(click)
|
|
54
|
+
|
|
55
|
+
case 5:
|
|
56
|
+
assert.ok([1, 5, 9, 2, 6, 10].includes(mouseButton), 'invalid operation')
|
|
57
|
+
return this.dragClick(click, gamemode)
|
|
58
|
+
|
|
59
|
+
case 6:
|
|
60
|
+
assert.ok(mouseButton === 0, 'invalid operation')
|
|
61
|
+
return this.doubleClick(click)
|
|
35
62
|
}
|
|
36
63
|
}
|
|
37
64
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.selectedItem = null
|
|
42
|
-
} else if (click.mouseButton === 1) {
|
|
43
|
-
this.selectedItem.count -= 1
|
|
44
|
-
if (!this.selectedItem.count) this.selectedItem = null
|
|
65
|
+
mouseClick (click) {
|
|
66
|
+
if (click.slot === -999) {
|
|
67
|
+
this.dropSelectedItem(click.mouseButton === 0)
|
|
45
68
|
} else {
|
|
46
|
-
|
|
69
|
+
let { item } = click
|
|
70
|
+
if (click.mouseButton === 0) { // left click
|
|
71
|
+
if (item && this.selectedItem) {
|
|
72
|
+
if (Item.equal(item, this.selectedItem, false)) {
|
|
73
|
+
if (click.slot === this.craftingResultSlot) {
|
|
74
|
+
const maxTransferrable = this.selectedItem.stackSize - this.selectedItem.count
|
|
75
|
+
if (item.count > maxTransferrable) {
|
|
76
|
+
this.selectedItem.count += maxTransferrable
|
|
77
|
+
item.count -= maxTransferrable
|
|
78
|
+
} else if (item.count <= maxTransferrable) {
|
|
79
|
+
this.selectedItem.count += item.count
|
|
80
|
+
this.updateSlot(item.slot, null)
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
this.fillSlotWithSelectedItem(item, true)
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
this.swapSelectedItem(click.slot, item)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return [click.slot]
|
|
90
|
+
} else if (this.selectedItem || item) {
|
|
91
|
+
this.swapSelectedItem(click.slot, item)
|
|
92
|
+
|
|
93
|
+
return [click.slot]
|
|
94
|
+
}
|
|
95
|
+
} else if (click.mouseButton === 1) { // right click
|
|
96
|
+
if (this.selectedItem) {
|
|
97
|
+
if (item) {
|
|
98
|
+
if (Item.equal(item, this.selectedItem, false)) {
|
|
99
|
+
this.fillSlotWithSelectedItem(item, false)
|
|
100
|
+
} else {
|
|
101
|
+
this.swapSelectedItem(click.slot, item)
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
item = new Item(this.selectedItem.type, 0, this.selectedItem.metadata, this.selectedItem.nbt)
|
|
105
|
+
this.updateSlot(click.slot, item)
|
|
106
|
+
this.fillSlotWithSelectedItem(item, false)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return [click.slot]
|
|
110
|
+
} else if (item && click.slot !== this.craftingResultSlot) {
|
|
111
|
+
this.splitSlot(item)
|
|
112
|
+
|
|
113
|
+
return [click.slot]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
47
116
|
}
|
|
117
|
+
|
|
48
118
|
return []
|
|
49
119
|
}
|
|
50
120
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
121
|
+
shiftClick (click) {
|
|
122
|
+
const { item } = click
|
|
123
|
+
if (!item) return
|
|
124
|
+
if (this.type === 'minecraft:inventory') {
|
|
125
|
+
if (click.slot < this.inventoryStart) {
|
|
126
|
+
this.fillAndDump(item, this.inventoryStart, this.inventoryEnd, click.slot === this.craftingResultSlot)
|
|
55
127
|
} else {
|
|
56
|
-
|
|
128
|
+
if (click.slot >= this.inventoryStart && click.slot < this.inventoryEnd - 10) {
|
|
129
|
+
this.fillAndDump(item, this.hotbarStart, this.inventoryEnd)
|
|
130
|
+
} else {
|
|
131
|
+
this.fillAndDump(item, this.inventoryStart, this.inventoryEnd)
|
|
132
|
+
}
|
|
57
133
|
}
|
|
58
|
-
} else if (click.mouseButton === 1) {
|
|
59
|
-
return this.acceptSwapAreaRightClick(click)
|
|
60
134
|
} else {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
assert.strictEqual(click.mode, 0, 'unimplemented')
|
|
67
|
-
if (click.mouseButton === 0) {
|
|
68
|
-
return this.acceptSwapAreaLeftClick(click)
|
|
69
|
-
} else if (click.mouseButton === 1) {
|
|
70
|
-
return this.acceptSwapAreaRightClick(click)
|
|
71
|
-
} else {
|
|
72
|
-
assert.ok(false, 'unimplemented')
|
|
135
|
+
if (click.slot < this.inventoryStart) {
|
|
136
|
+
this.fillAndDump(item, this.inventoryStart, this.inventoryEnd, this.craftingResultSlot === -1 || click.slot === this.craftingResultSlot)
|
|
137
|
+
} else {
|
|
138
|
+
this.fillAndDump(item, 0, this.inventoryStart - 1)
|
|
139
|
+
}
|
|
73
140
|
}
|
|
74
141
|
}
|
|
75
142
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
assert.strictEqual(click.mode, 0)
|
|
79
|
-
|
|
143
|
+
numberClick (click) {
|
|
144
|
+
if (this.selectedItem) return
|
|
80
145
|
const { item } = click
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
146
|
+
const hotbarSlot = this.hotbarStart + click.mouseButton
|
|
147
|
+
const itemAtHotbarSlot = this.slots[hotbarSlot]
|
|
148
|
+
if (Item.equal(item, itemAtHotbarSlot) && item?.slot === hotbarSlot) return
|
|
149
|
+
if (item) {
|
|
150
|
+
if (itemAtHotbarSlot) {
|
|
151
|
+
if ((this.type === 'minecraft:inventory' || registry.version['>=']('1.9')) && click.slot !== this.craftingResultSlot) {
|
|
152
|
+
this.updateSlot(click.slot, itemAtHotbarSlot)
|
|
153
|
+
this.updateSlot(hotbarSlot, item)
|
|
88
154
|
} else {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
155
|
+
this.dumpItem(itemAtHotbarSlot, this.hotbarStart, this.inventoryEnd)
|
|
156
|
+
if (this.slots[hotbarSlot]) {
|
|
157
|
+
this.dumpItem(itemAtHotbarSlot, this.inventoryStart, this.hotbarStart - 1)
|
|
158
|
+
}
|
|
159
|
+
if (this.slots[hotbarSlot] === null) {
|
|
160
|
+
this.updateSlot(item.slot, null)
|
|
161
|
+
this.updateSlot(hotbarSlot, item)
|
|
162
|
+
let slots = this.findItemsRange(this.hotbarStart, this.inventoryEnd, itemAtHotbarSlot.type, itemAtHotbarSlot.metadata, true, itemAtHotbarSlot.nbt)
|
|
163
|
+
slots.push(...this.findItemsRange(this.inventoryStart, this.hotbarStart - 1, itemAtHotbarSlot.type, itemAtHotbarSlot.metadata, true, itemAtHotbarSlot.nbt))
|
|
164
|
+
slots = slots.filter(slot => slot.slot !== itemAtHotbarSlot.slot)
|
|
165
|
+
this.fillSlotsWithItem(slots, itemAtHotbarSlot)
|
|
166
|
+
}
|
|
92
167
|
}
|
|
93
168
|
} else {
|
|
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
|
-
}
|
|
169
|
+
this.updateSlot(item.slot, null)
|
|
170
|
+
this.updateSlot(hotbarSlot, item)
|
|
102
171
|
}
|
|
103
|
-
} else if (
|
|
104
|
-
|
|
105
|
-
this.
|
|
106
|
-
item.metadata, item.nbt)
|
|
107
|
-
item.count -= this.selectedItem.count
|
|
108
|
-
if (item.count === 0) this.updateSlot(item.slot, null)
|
|
172
|
+
} else if (itemAtHotbarSlot && click.slot !== this.craftingResultSlot) {
|
|
173
|
+
this.updateSlot(click.slot, itemAtHotbarSlot)
|
|
174
|
+
this.updateSlot(hotbarSlot, null)
|
|
109
175
|
}
|
|
110
|
-
return [{
|
|
111
|
-
location: click.slot,
|
|
112
|
-
item: Item.toNotch(this.slots[click.slot])
|
|
113
|
-
}]
|
|
114
176
|
}
|
|
115
177
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
178
|
+
middleClick (click, gamemode) {
|
|
179
|
+
if (this.selectedItem) return []
|
|
180
|
+
const { item } = click
|
|
181
|
+
if (gamemode === 1 && item) {
|
|
182
|
+
this.selectedItem = new Item(item.type, item.stackSize, item.metadata, item.nbt)
|
|
183
|
+
}
|
|
184
|
+
return []
|
|
185
|
+
}
|
|
119
186
|
|
|
187
|
+
dropClick (click) {
|
|
120
188
|
const { item } = click
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
item.
|
|
124
|
-
|
|
189
|
+
if (this.selectedItem || item === null) return []
|
|
190
|
+
if (click.mouseButton === 0) {
|
|
191
|
+
if (--click.item.count === 0) this.updateSlot(click.slot, null)
|
|
192
|
+
return [click.slot]
|
|
193
|
+
} else if (click.mouseButton === 1) {
|
|
194
|
+
this.updateSlot(click.slot, null)
|
|
195
|
+
return [click.slot]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
dragClick (click, gamemode) {
|
|
200
|
+
// unimplemented
|
|
201
|
+
assert.ok(false, 'unimplemented')
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
doubleClick (click) {
|
|
205
|
+
// unimplemented
|
|
206
|
+
assert.ok(false, 'unimplemented')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
acceptOutsideWindowClick = this.acceptClick
|
|
210
|
+
acceptInventoryClick = this.acceptClick
|
|
211
|
+
acceptNonInventorySwapAreaClick = this.acceptClick
|
|
212
|
+
acceptSwapAreaLeftClick = this.acceptClick
|
|
213
|
+
acceptSwapAreaRightClick = this.acceptClick
|
|
214
|
+
acceptCraftingClick = this.acceptClick
|
|
215
|
+
|
|
216
|
+
fillAndDump (item, start, end, lastToFirst = false) {
|
|
217
|
+
this.fillSlotsWithItem(this.findItemsRange(start, end, item.type, item.metadata, true, item.nbt, true), item, lastToFirst)
|
|
218
|
+
if (this.slots[item.slot]) {
|
|
219
|
+
this.dumpItem(item, start, end, lastToFirst)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fillSlotsWithItem (slots, item, lastToFirst = false) {
|
|
224
|
+
while (slots.length && item.count) {
|
|
225
|
+
this.fillSlotWithItem(lastToFirst ? slots.pop() : slots.shift(), item)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
fillSlotWithItem (itemToFill, itemToTake) {
|
|
230
|
+
const newCount = itemToFill.count + itemToTake.count
|
|
231
|
+
const leftover = newCount - itemToFill.stackSize
|
|
232
|
+
if (leftover <= 0) {
|
|
233
|
+
itemToFill.count = newCount
|
|
234
|
+
itemToTake.count = 0
|
|
235
|
+
this.updateSlot(itemToTake.slot, null)
|
|
236
|
+
} else {
|
|
237
|
+
itemToFill.count = itemToFill.stackSize
|
|
238
|
+
itemToTake.count = leftover
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fillSlotWithSelectedItem (item, untilFull) {
|
|
243
|
+
if (untilFull) {
|
|
125
244
|
const newCount = item.count + this.selectedItem.count
|
|
126
245
|
const leftover = newCount - item.stackSize
|
|
127
246
|
if (leftover <= 0) {
|
|
@@ -132,15 +251,36 @@ module.exports = (Item) => {
|
|
|
132
251
|
this.selectedItem.count = leftover
|
|
133
252
|
}
|
|
134
253
|
} else {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
254
|
+
if (item.count + 1 <= item.stackSize) {
|
|
255
|
+
item.count++
|
|
256
|
+
if (--this.selectedItem.count === 0) this.selectedItem = null
|
|
257
|
+
}
|
|
139
258
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
dumpItem (item, start, end, lastToFirst = false) {
|
|
262
|
+
const emptySlot = lastToFirst ? this.lastEmptySlotRange(start, end) : this.firstEmptySlotRange(start, end)
|
|
263
|
+
if (emptySlot !== null && emptySlot !== this.craftingResultSlot) {
|
|
264
|
+
const slot = item.slot
|
|
265
|
+
this.updateSlot(emptySlot, item)
|
|
266
|
+
this.updateSlot(slot, null)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
splitSlot (item) {
|
|
271
|
+
if (!item) return
|
|
272
|
+
this.selectedItem = new Item(item.type, Math.ceil(item.count / 2), item.metadata, item.nbt)
|
|
273
|
+
item.count -= this.selectedItem.count
|
|
274
|
+
if (item.count === 0) this.updateSlot(item.slot, null)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
swapSelectedItem (slot, item) {
|
|
278
|
+
this.updateSlot(slot, this.selectedItem)
|
|
279
|
+
this.selectedItem = item
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
dropSelectedItem (untilEmpty) {
|
|
283
|
+
if (untilEmpty || --this.selectedItem.count === 0) this.selectedItem = null
|
|
144
284
|
}
|
|
145
285
|
|
|
146
286
|
updateSlot (slot, newItem) {
|
|
@@ -152,7 +292,18 @@ module.exports = (Item) => {
|
|
|
152
292
|
this.emit(`updateSlot:${slot}`, oldItem, newItem)
|
|
153
293
|
}
|
|
154
294
|
|
|
155
|
-
|
|
295
|
+
findItemsRange (start, end, itemType, metadata, notFull, nbt, withoutCraftResultSlot = false) {
|
|
296
|
+
const items = []
|
|
297
|
+
while (start < end) {
|
|
298
|
+
const item = this.findItemRange(start, end, itemType, metadata, notFull, nbt, withoutCraftResultSlot)
|
|
299
|
+
if (!item) break
|
|
300
|
+
start = item.slot + 1
|
|
301
|
+
items.push(item)
|
|
302
|
+
}
|
|
303
|
+
return items
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
findItemRange (start, end, itemType, metadata, notFull, nbt, withoutCraftResultSlot = false) {
|
|
156
307
|
assert.notStrictEqual(itemType, null)
|
|
157
308
|
for (let i = start; i < end; ++i) {
|
|
158
309
|
const item = this.slots[i]
|
|
@@ -160,7 +311,8 @@ module.exports = (Item) => {
|
|
|
160
311
|
item && itemType === item.type &&
|
|
161
312
|
(metadata == null || metadata === item.metadata) &&
|
|
162
313
|
(!notFull || item.count < item.stackSize) &&
|
|
163
|
-
(nbt == null || JSON.stringify(nbt) === JSON.stringify(item.nbt))
|
|
314
|
+
(nbt == null || JSON.stringify(nbt) === JSON.stringify(item.nbt)) &&
|
|
315
|
+
!(item.slot === this.craftingResultSlot && withoutCraftResultSlot)) {
|
|
164
316
|
return item
|
|
165
317
|
}
|
|
166
318
|
}
|
|
@@ -196,7 +348,14 @@ module.exports = (Item) => {
|
|
|
196
348
|
|
|
197
349
|
firstEmptySlotRange (start, end) {
|
|
198
350
|
for (let i = start; i < end; ++i) {
|
|
199
|
-
if (
|
|
351
|
+
if (this.slots[i] === null) return i
|
|
352
|
+
}
|
|
353
|
+
return null
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
lastEmptySlotRange (start, end) {
|
|
357
|
+
for (let i = end; i >= start; i--) {
|
|
358
|
+
if (this.slots[i] === null) return i
|
|
200
359
|
}
|
|
201
360
|
return null
|
|
202
361
|
}
|
|
@@ -217,6 +376,15 @@ module.exports = (Item) => {
|
|
|
217
376
|
return this.firstEmptySlotRange(this.inventoryStart, this.inventoryEnd)
|
|
218
377
|
}
|
|
219
378
|
|
|
379
|
+
sumRange (start, end) {
|
|
380
|
+
let sum = 0
|
|
381
|
+
for (let i = start; i < end; i++) {
|
|
382
|
+
const item = this.slots[i]
|
|
383
|
+
if (item) sum += item.count
|
|
384
|
+
}
|
|
385
|
+
return sum
|
|
386
|
+
}
|
|
387
|
+
|
|
220
388
|
countRange (start, end, itemType, metadata) {
|
|
221
389
|
let sum = 0
|
|
222
390
|
for (let i = start; i < end; ++i) {
|
|
@@ -259,7 +427,7 @@ module.exports = (Item) => {
|
|
|
259
427
|
emptySlotCount () {
|
|
260
428
|
let count = 0
|
|
261
429
|
for (let i = this.inventoryStart; i < this.inventoryEnd; ++i) {
|
|
262
|
-
if (
|
|
430
|
+
if (this.slots[i] === null) count += 1
|
|
263
431
|
}
|
|
264
432
|
return count
|
|
265
433
|
}
|
|
@@ -268,13 +436,6 @@ module.exports = (Item) => {
|
|
|
268
436
|
return this.requiresConfirmation
|
|
269
437
|
}
|
|
270
438
|
|
|
271
|
-
acceptCraftingClick (click) {
|
|
272
|
-
assert.strictEqual(click.mouseButton, 0)
|
|
273
|
-
assert.strictEqual(click.mode, 0)
|
|
274
|
-
assert.strictEqual(this.selectedItem, null)
|
|
275
|
-
return this.acceptNonInventorySwapAreaClick(click)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
439
|
clear (blockId, count) {
|
|
279
440
|
let clearedCount = 0
|
|
280
441
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prismarine-windows",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.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,367 @@
|
|
|
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
|
+
it('same slot click does nothing', () => {
|
|
294
|
+
testWindow = createTestWindow('chest')
|
|
295
|
+
.prepareSlot(62, 64, firstItem)
|
|
296
|
+
|
|
297
|
+
// slot 62 = hotbarEnd
|
|
298
|
+
// mouseButton 8 = hotbarEnd
|
|
299
|
+
// slot 0 = windowStart
|
|
300
|
+
testWindow.executeClick(2, 8, 62)
|
|
301
|
+
|
|
302
|
+
// no asserts, test would fail regardless
|
|
303
|
+
// if something did change
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
describe('mode 3 | middle click', () => {
|
|
309
|
+
describe('mouseButton 2', () => {
|
|
310
|
+
it('get stack into selectedItem (gamemode 0)', () => {
|
|
311
|
+
testWindow = createTestWindow('chest')
|
|
312
|
+
.prepareSlot(0, 1, firstItem)
|
|
313
|
+
|
|
314
|
+
testWindow.executeClick(3, 2, 0)
|
|
315
|
+
|
|
316
|
+
// expect no change
|
|
317
|
+
testWindow.assertSelectedItem().isEmpty()
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('get stack into selectedItem (gamemode 1)', () => {
|
|
321
|
+
testWindow = createTestWindow('chest')
|
|
322
|
+
.prepareSlot(0, 1, firstItem)
|
|
323
|
+
|
|
324
|
+
testWindow.executeClick(3, 2, 0, /* gamemode = */1)
|
|
325
|
+
|
|
326
|
+
testWindow.assertSelectedItem().isNotEmpty().hasCount(64).hasType(firstItem)
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
describe('mode 4 | drop click', () => {
|
|
332
|
+
describe('mouseButton 0', () => {
|
|
333
|
+
it('drop 1 of stack', () => {
|
|
334
|
+
testWindow = createTestWindow('chest')
|
|
335
|
+
.prepareSlot(0, 64, firstItem)
|
|
336
|
+
|
|
337
|
+
testWindow.executeClick(4, 0, 0)
|
|
338
|
+
|
|
339
|
+
testWindow.assertSlot(0).isNotEmpty().hasCount(64 - 1).hasType(firstItem)
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
describe('mouseButton 1', () => {
|
|
344
|
+
it('drop full stack', () => {
|
|
345
|
+
testWindow = createTestWindow('chest')
|
|
346
|
+
.prepareSlot(0, 64, firstItem)
|
|
347
|
+
|
|
348
|
+
testWindow.executeClick(4, 1, 0)
|
|
349
|
+
|
|
350
|
+
testWindow.assertSlot(0).isEmpty()
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
it('returning changed slots works', () => {
|
|
356
|
+
testWindow = createTestWindow('chest')
|
|
357
|
+
.prepareSlot(0, 64, firstItem)
|
|
358
|
+
|
|
359
|
+
const changedSlots = testWindow.executeClick(0, 0, 0)
|
|
360
|
+
|
|
361
|
+
testWindow.assertSlot(0).isEmpty()
|
|
362
|
+
testWindow.assertSelectedItem().isNotEmpty()
|
|
363
|
+
|
|
364
|
+
assert.equal(changedSlots.length, 1)
|
|
365
|
+
assert.equal(changedSlots[0], 0)
|
|
366
|
+
// selectedItem isn't included in changedSlots
|
|
367
|
+
})
|