@spyglassmc/java-edition 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/binder/index.d.ts +1 -1
- package/lib/binder/index.js +94 -19
- package/lib/common/index.js +10 -7
- package/lib/dependency/index.js +11 -9
- package/lib/dependency/mcmeta.d.ts +1 -1
- package/lib/dependency/mcmeta.js +27 -13
- package/lib/index.js +15 -6
- package/lib/json/checker/data/advancement.js +24 -13
- package/lib/json/checker/data/biome.js +3 -3
- package/lib/json/checker/data/common.js +15 -27
- package/lib/json/checker/data/dimension.js +7 -18
- package/lib/json/checker/data/feature.js +41 -27
- package/lib/json/checker/data/index.d.ts +1 -1
- package/lib/json/checker/data/index.js +3 -3
- package/lib/json/checker/data/loot_table.js +16 -17
- package/lib/json/checker/data/recipe.js +1 -1
- package/lib/json/checker/data/structure.js +20 -12
- package/lib/json/checker/data/tag.js +2 -2
- package/lib/json/checker/data/text_component.js +21 -12
- package/lib/json/checker/index.d.ts +10 -3
- package/lib/json/checker/index.js +230 -3
- package/lib/json/checker/util/advancement.js +3 -3
- package/lib/json/checker/util/block_states.d.ts +2 -2
- package/lib/json/checker/util/block_states.js +7 -5
- package/lib/json/checker/util/color.js +8 -2
- package/lib/json/checker/util/nbt.js +3 -2
- package/lib/json/checker/util/recipe.js +7 -6
- package/lib/json/checker/util/version.js +2 -2
- package/lib/mcfunction/checker/index.js +10 -7
- package/lib/mcfunction/colorizer/index.js +2 -4
- package/lib/mcfunction/common/index.js +46 -15
- package/lib/mcfunction/completer/argument.js +53 -24
- package/lib/mcfunction/inlayHintProvider.js +8 -3
- package/lib/mcfunction/node/argument.d.ts +3 -3
- package/lib/mcfunction/node/argument.js +57 -17
- package/lib/mcfunction/parser/argument.js +124 -52
- package/lib/mcfunction/signatureHelpProvider.js +6 -3
- package/lib/mcfunction/tree/patch.js +132 -126
- package/package.json +7 -7
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { AstNode, BooleanNode, BrigadierStringOptions, completer, CompletionItem, FloatNode, getStates, InsertTextBuilder, IntegerNode, LiteralNode, Range, ResourceLocation, ResourceLocationNode, StringNode, SymbolNode } from '@spyglassmc/core';
|
|
1
|
+
import { AstNode, BooleanNode, BrigadierStringOptions, completer, CompletionItem, FloatNode, getStates, InsertTextBuilder, IntegerNode, LiteralNode, Range, ResourceLocation, ResourceLocationNode, StringNode, SymbolNode, } from '@spyglassmc/core';
|
|
2
2
|
import * as json from '@spyglassmc/json';
|
|
3
3
|
import { localeQuote, localize } from '@spyglassmc/locales';
|
|
4
4
|
import { getTagValues } from '../../common/index.js';
|
|
5
|
-
import { ColorArgumentValues, EntityAnchorArgumentValues, ItemSlotArgumentValues, OperationArgumentValues, ScoreboardSlotArgumentValues, SwizzleArgumentValues } from '../common/index.js';
|
|
6
|
-
import { BlockNode, CoordinateNode, EntitySelectorNode, IntRangeNode, ItemNode, ObjectiveCriteriaNode, ParticleNode, ScoreHolderNode, VectorNode } from '../node/index.js';
|
|
5
|
+
import { ColorArgumentValues, EntityAnchorArgumentValues, ItemSlotArgumentValues, OperationArgumentValues, ScoreboardSlotArgumentValues, SwizzleArgumentValues, } from '../common/index.js';
|
|
6
|
+
import { BlockNode, CoordinateNode, EntitySelectorNode, IntRangeNode, ItemNode, ObjectiveCriteriaNode, ParticleNode, ScoreHolderNode, VectorNode, } from '../node/index.js';
|
|
7
7
|
export const getMockNodes = (rawTreeNode, range) => {
|
|
8
8
|
const treeNode = rawTreeNode;
|
|
9
9
|
switch (treeNode.parser) {
|
|
@@ -68,7 +68,10 @@ export const getMockNodes = (rawTreeNode, range) => {
|
|
|
68
68
|
case 'minecraft:objective_criteria':
|
|
69
69
|
return ObjectiveCriteriaNode.mock(range);
|
|
70
70
|
case 'minecraft:operation':
|
|
71
|
-
return LiteralNode.mock(range, {
|
|
71
|
+
return LiteralNode.mock(range, {
|
|
72
|
+
pool: OperationArgumentValues,
|
|
73
|
+
colorTokenType: 'operator',
|
|
74
|
+
});
|
|
72
75
|
case 'minecraft:particle':
|
|
73
76
|
return ParticleNode.mock(range);
|
|
74
77
|
case 'minecraft:resource':
|
|
@@ -109,7 +112,8 @@ const block = (node, ctx) => {
|
|
|
109
112
|
if (Range.contains(node.id, ctx.offset, true)) {
|
|
110
113
|
ans.push(...completer.resourceLocation(node.id, ctx));
|
|
111
114
|
}
|
|
112
|
-
if (node.states &&
|
|
115
|
+
if (node.states &&
|
|
116
|
+
Range.contains(Range.translate(node.states, 1, -1), ctx.offset, true)) {
|
|
113
117
|
ans.push(...blockStates(node.states, ctx));
|
|
114
118
|
}
|
|
115
119
|
return ans;
|
|
@@ -125,20 +129,21 @@ const blockStates = (node, ctx) => {
|
|
|
125
129
|
return completer.record({
|
|
126
130
|
key: (_record, pair, _ctx, range, insertValue, insertComma, existingKeys) => {
|
|
127
131
|
return Object.keys(states)
|
|
128
|
-
.filter(k => pair?.key?.value === k ||
|
|
129
|
-
.
|
|
132
|
+
.filter((k) => pair?.key?.value === k ||
|
|
133
|
+
!existingKeys.some((ek) => ek.value === k))
|
|
134
|
+
.map((k) => CompletionItem.create(k, range, {
|
|
130
135
|
kind: 10 /* CompletionKind.Property */,
|
|
131
136
|
detail: localize('mcfunction.completer.block.states.default-value', localeQuote(states[k][0])),
|
|
132
137
|
insertText: new InsertTextBuilder()
|
|
133
138
|
.literal(k)
|
|
134
|
-
.if(insertValue, b => b.literal('=').placeholder(...states[k]))
|
|
135
|
-
.if(insertComma, b => b.literal(','))
|
|
139
|
+
.if(insertValue, (b) => b.literal('=').placeholder(...states[k]))
|
|
140
|
+
.if(insertComma, (b) => b.literal(','))
|
|
136
141
|
.build(),
|
|
137
142
|
}));
|
|
138
143
|
},
|
|
139
144
|
value: (_record, pair, ctx) => {
|
|
140
145
|
if (pair.key && states[pair.key.value]) {
|
|
141
|
-
return states[pair.key.value].map(v => CompletionItem.create(v, pair.value ?? ctx.offset, {
|
|
146
|
+
return states[pair.key.value].map((v) => CompletionItem.create(v, pair.value ?? ctx.offset, {
|
|
142
147
|
kind: 12 /* CompletionKind.Value */,
|
|
143
148
|
}));
|
|
144
149
|
}
|
|
@@ -157,11 +162,17 @@ const item = (node, ctx) => {
|
|
|
157
162
|
return ans;
|
|
158
163
|
};
|
|
159
164
|
const objectiveCriteria = (node, ctx) => {
|
|
160
|
-
const ans = ObjectiveCriteriaNode.SimpleValues.map(v => CompletionItem.create(v, node));
|
|
161
|
-
if (!node.children?.[0] ||
|
|
162
|
-
|
|
165
|
+
const ans = ObjectiveCriteriaNode.SimpleValues.map((v) => CompletionItem.create(v, node));
|
|
166
|
+
if (!node.children?.[0] ||
|
|
167
|
+
Range.contains(node.children[0], ctx.offset, true)) {
|
|
168
|
+
ans.push(...completer.resourceLocation(node.children?.[0] ??
|
|
169
|
+
ResourceLocationNode.mock(node, {
|
|
170
|
+
category: 'stat_type',
|
|
171
|
+
namespacePathSep: '.',
|
|
172
|
+
}), ctx));
|
|
163
173
|
}
|
|
164
|
-
if (node.children?.[1] &&
|
|
174
|
+
if (node.children?.[1] &&
|
|
175
|
+
Range.contains(node.children[1], ctx.offset, true)) {
|
|
165
176
|
ans.push(...completer.resourceLocation(node.children[1], ctx));
|
|
166
177
|
}
|
|
167
178
|
return ans;
|
|
@@ -175,16 +186,28 @@ const particle = (node, ctx) => {
|
|
|
175
186
|
const map = {
|
|
176
187
|
block: [BlockNode.mock(ctx.offset, false)],
|
|
177
188
|
block_marker: [BlockNode.mock(ctx.offset, false)],
|
|
178
|
-
dust: [
|
|
179
|
-
|
|
189
|
+
dust: [
|
|
190
|
+
VectorNode.mock(ctx.offset, { dimension: 3 }),
|
|
191
|
+
FloatNode.mock(ctx.offset),
|
|
192
|
+
],
|
|
193
|
+
dust_color_transition: [
|
|
194
|
+
VectorNode.mock(ctx.offset, { dimension: 3 }),
|
|
195
|
+
FloatNode.mock(ctx.offset),
|
|
196
|
+
VectorNode.mock(ctx.offset, { dimension: 3 }),
|
|
197
|
+
],
|
|
180
198
|
falling_dust: [BlockNode.mock(ctx.offset, false)],
|
|
181
199
|
item: [ItemNode.mock(ctx.offset, false)],
|
|
182
200
|
sculk_charge: [FloatNode.mock(ctx.offset)],
|
|
183
201
|
shriek: [IntegerNode.mock(ctx.offset)],
|
|
184
|
-
vibration: [
|
|
202
|
+
vibration: [
|
|
203
|
+
VectorNode.mock(ctx.offset, { dimension: 3 }),
|
|
204
|
+
VectorNode.mock(ctx.offset, { dimension: 3 }),
|
|
205
|
+
IntegerNode.mock(ctx.offset),
|
|
206
|
+
],
|
|
185
207
|
};
|
|
186
208
|
if (ParticleNode.isSpecialType(id)) {
|
|
187
|
-
const numParamsBefore = node.children?.slice(1).filter(n => n.range.end < ctx.offset).length ??
|
|
209
|
+
const numParamsBefore = node.children?.slice(1).filter((n) => n.range.end < ctx.offset).length ??
|
|
210
|
+
0;
|
|
188
211
|
const mock = map[id][numParamsBefore];
|
|
189
212
|
if (mock) {
|
|
190
213
|
return completer.dispatch(mock, ctx);
|
|
@@ -210,7 +233,8 @@ const selector = (node, ctx) => {
|
|
|
210
233
|
if (Range.contains(node.children[0], ctx.offset, true)) {
|
|
211
234
|
return completer.literal(node.children[0], ctx);
|
|
212
235
|
}
|
|
213
|
-
if (node.arguments &&
|
|
236
|
+
if (node.arguments &&
|
|
237
|
+
Range.contains(Range.translate(node.arguments, 1, -1), ctx.offset, true)) {
|
|
214
238
|
return selectorArguments(node.arguments, ctx);
|
|
215
239
|
}
|
|
216
240
|
return [];
|
|
@@ -223,13 +247,14 @@ const selectorArguments = (node, ctx) => {
|
|
|
223
247
|
return completer.record({
|
|
224
248
|
key: (record, pair, _ctx, range, insertValue, insertComma) => {
|
|
225
249
|
return [...EntitySelectorNode.ArgumentKeys]
|
|
226
|
-
.filter(k => EntitySelectorNode.canKeyExist(selector, record, k) ===
|
|
227
|
-
|
|
250
|
+
.filter((k) => EntitySelectorNode.canKeyExist(selector, record, k) ===
|
|
251
|
+
0 /* EntitySelectorNode.Result.Ok */)
|
|
252
|
+
.map((k) => CompletionItem.create(k, range, {
|
|
228
253
|
kind: 10 /* CompletionKind.Property */,
|
|
229
254
|
insertText: new InsertTextBuilder()
|
|
230
255
|
.literal(k)
|
|
231
|
-
.if(insertValue, b => b.literal('=').placeholder()) // TODO
|
|
232
|
-
.if(insertComma, b => b.literal(','))
|
|
256
|
+
.if(insertValue, (b) => b.literal('=').placeholder()) // TODO
|
|
257
|
+
.if(insertComma, (b) => b.literal(','))
|
|
233
258
|
.build(),
|
|
234
259
|
}));
|
|
235
260
|
},
|
|
@@ -242,7 +267,11 @@ const selectorArguments = (node, ctx) => {
|
|
|
242
267
|
})(node, ctx);
|
|
243
268
|
};
|
|
244
269
|
const intRange = (node, _ctx) => {
|
|
245
|
-
return [
|
|
270
|
+
return [
|
|
271
|
+
CompletionItem.create('-2147483648..2147483647', node, {
|
|
272
|
+
kind: 21 /* CompletionKind.Constant */,
|
|
273
|
+
}),
|
|
274
|
+
];
|
|
246
275
|
};
|
|
247
276
|
const vector = (node, _ctx) => {
|
|
248
277
|
const createCompletion = (coordinate, sortText) => CompletionItem.create(new Array(node.options.dimension).fill(coordinate).join(' '), node, { sortText });
|
|
@@ -5,11 +5,16 @@ export const inlayHintProvider = (node, ctx) => {
|
|
|
5
5
|
return [];
|
|
6
6
|
}
|
|
7
7
|
const ans = [];
|
|
8
|
-
core.traversePreOrder(node, _ => true, mcf.CommandChildNode.is, n => {
|
|
8
|
+
core.traversePreOrder(node, (_) => true, mcf.CommandChildNode.is, (n) => {
|
|
9
9
|
const node = n;
|
|
10
10
|
const config = ctx.config.env.feature.inlayHint;
|
|
11
|
-
if (config === true ||
|
|
12
|
-
|
|
11
|
+
if (config === true ||
|
|
12
|
+
(typeof config === 'object' &&
|
|
13
|
+
config.enabledNodes.includes(node.children[0].type))) {
|
|
14
|
+
ans.push({
|
|
15
|
+
offset: node.range.start,
|
|
16
|
+
text: `${node.path[node.path.length - 1]}:`,
|
|
17
|
+
});
|
|
13
18
|
}
|
|
14
19
|
});
|
|
15
20
|
return ans;
|
|
@@ -59,14 +59,14 @@ export declare type EntitySelectorVariable = typeof EntitySelectorVariables[numb
|
|
|
59
59
|
export declare namespace EntitySelectorVariable {
|
|
60
60
|
function is(value: string): value is EntitySelectorVariable;
|
|
61
61
|
}
|
|
62
|
-
export declare const EntitySelectorAtVariables: ("@
|
|
62
|
+
export declare const EntitySelectorAtVariables: ("@a" | "@e" | "@p" | "@r" | "@s")[];
|
|
63
63
|
export declare type EntitySelectorAtVariable = typeof EntitySelectorAtVariables[number];
|
|
64
64
|
export declare namespace EntitySelectorAtVariable {
|
|
65
65
|
function is(value: string): value is EntitySelectorAtVariable;
|
|
66
66
|
}
|
|
67
67
|
export interface EntitySelectorNode extends core.AstNode {
|
|
68
68
|
type: 'mcfunction:entity_selector';
|
|
69
|
-
children: [core.LiteralNode, ...[] | [EntitySelectorArgumentsNode]];
|
|
69
|
+
children: [core.LiteralNode, ...([] | [EntitySelectorArgumentsNode])];
|
|
70
70
|
variable: EntitySelectorVariable;
|
|
71
71
|
arguments?: EntitySelectorArgumentsNode;
|
|
72
72
|
currentEntity?: boolean;
|
|
@@ -80,7 +80,7 @@ export interface EntitySelectorNode extends core.AstNode {
|
|
|
80
80
|
export declare namespace EntitySelectorNode {
|
|
81
81
|
function is<T extends core.DeepReadonly<core.AstNode> | undefined>(node: T): node is core.NodeIsHelper<EntitySelectorNode, T>;
|
|
82
82
|
function mock(range: core.RangeLike): EntitySelectorNode;
|
|
83
|
-
const ArgumentKeys: Set<"predicate" | "tag" | "team" | "type" | "level" | "advancements" | "nbt" | "name" | "
|
|
83
|
+
const ArgumentKeys: Set<"predicate" | "tag" | "team" | "type" | "level" | "advancements" | "nbt" | "name" | "distance" | "gamemode" | "limit" | "scores" | "sort" | "x" | "y" | "z" | "dx" | "dy" | "dz" | "x_rotation" | "y_rotation">;
|
|
84
84
|
type ArgumentKey = typeof ArgumentKeys extends Set<infer T> ? T : undefined;
|
|
85
85
|
const enum Result {
|
|
86
86
|
Ok = 0,
|
|
@@ -14,7 +14,10 @@ export var BlockNode;
|
|
|
14
14
|
}
|
|
15
15
|
BlockNode.is = is;
|
|
16
16
|
function mock(range, isPredicate) {
|
|
17
|
-
const id = core.ResourceLocationNode.mock(range, {
|
|
17
|
+
const id = core.ResourceLocationNode.mock(range, {
|
|
18
|
+
category: 'block',
|
|
19
|
+
allowTag: isPredicate,
|
|
20
|
+
});
|
|
18
21
|
return {
|
|
19
22
|
type: 'mcfunction:block',
|
|
20
23
|
range: core.Range.get(range),
|
|
@@ -42,9 +45,7 @@ export var CoordinateNode;
|
|
|
42
45
|
function toDegree(node) {
|
|
43
46
|
// TODO: For relative coordinates, const value = (node.value + baseCoordinate) % 360
|
|
44
47
|
const value = node.value % 360;
|
|
45
|
-
return value >= 180
|
|
46
|
-
? value - 360
|
|
47
|
-
: value < -180 ? value + 360 : value;
|
|
48
|
+
return value >= 180 ? value - 360 : value < -180 ? value + 360 : value;
|
|
48
49
|
}
|
|
49
50
|
CoordinateNode.toDegree = toDegree;
|
|
50
51
|
})(CoordinateNode || (CoordinateNode = {}));
|
|
@@ -52,7 +53,8 @@ export var EntitySelectorArgumentsNode;
|
|
|
52
53
|
(function (EntitySelectorArgumentsNode) {
|
|
53
54
|
/* istanbul ignore next */
|
|
54
55
|
function is(node) {
|
|
55
|
-
return node.type ===
|
|
56
|
+
return (node.type ===
|
|
57
|
+
'mcfunction:entity_selector/arguments');
|
|
56
58
|
}
|
|
57
59
|
EntitySelectorArgumentsNode.is = is;
|
|
58
60
|
})(EntitySelectorArgumentsNode || (EntitySelectorArgumentsNode = {}));
|
|
@@ -65,7 +67,7 @@ export var EntitySelectorVariable;
|
|
|
65
67
|
}
|
|
66
68
|
EntitySelectorVariable.is = is;
|
|
67
69
|
})(EntitySelectorVariable || (EntitySelectorVariable = {}));
|
|
68
|
-
export const EntitySelectorAtVariables = EntitySelectorVariables.map(v => `@${v}`);
|
|
70
|
+
export const EntitySelectorAtVariables = EntitySelectorVariables.map((v) => `@${v}`);
|
|
69
71
|
export var EntitySelectorAtVariable;
|
|
70
72
|
(function (EntitySelectorAtVariable) {
|
|
71
73
|
/* istanbul ignore next */
|
|
@@ -78,11 +80,15 @@ export var EntitySelectorNode;
|
|
|
78
80
|
(function (EntitySelectorNode) {
|
|
79
81
|
/* istanbul ignore next */
|
|
80
82
|
function is(node) {
|
|
81
|
-
return node?.type ===
|
|
83
|
+
return (node?.type ===
|
|
84
|
+
'mcfunction:entity_selector');
|
|
82
85
|
}
|
|
83
86
|
EntitySelectorNode.is = is;
|
|
84
87
|
function mock(range) {
|
|
85
|
-
const literal = core.LiteralNode.mock(range, {
|
|
88
|
+
const literal = core.LiteralNode.mock(range, {
|
|
89
|
+
pool: EntitySelectorAtVariables,
|
|
90
|
+
colorTokenType: 'keyword',
|
|
91
|
+
});
|
|
86
92
|
return {
|
|
87
93
|
type: 'mcfunction:entity_selector',
|
|
88
94
|
range: core.Range.get(range),
|
|
@@ -92,13 +98,33 @@ export var EntitySelectorNode;
|
|
|
92
98
|
}
|
|
93
99
|
EntitySelectorNode.mock = mock;
|
|
94
100
|
EntitySelectorNode.ArgumentKeys = new Set([
|
|
95
|
-
'advancements',
|
|
96
|
-
'
|
|
97
|
-
'
|
|
101
|
+
'advancements',
|
|
102
|
+
'distance',
|
|
103
|
+
'gamemode',
|
|
104
|
+
'level',
|
|
105
|
+
'limit',
|
|
106
|
+
'name',
|
|
107
|
+
'nbt',
|
|
108
|
+
'predicate',
|
|
109
|
+
'scores',
|
|
110
|
+
'sort',
|
|
111
|
+
'tag',
|
|
112
|
+
'team',
|
|
113
|
+
'type',
|
|
114
|
+
'x',
|
|
115
|
+
'y',
|
|
116
|
+
'z',
|
|
117
|
+
'dx',
|
|
118
|
+
'dy',
|
|
119
|
+
'dz',
|
|
120
|
+
'x_rotation',
|
|
121
|
+
'y_rotation',
|
|
98
122
|
]);
|
|
99
123
|
function canKeyExist(selector, argument, key) {
|
|
100
|
-
const hasKey = (key) => !!argument.children.find(p => p.key?.value === key);
|
|
101
|
-
const hasNonInvertedKey = (key) => !!argument.children.find(p => p.key?.value === key &&
|
|
124
|
+
const hasKey = (key) => !!argument.children.find((p) => p.key?.value === key);
|
|
125
|
+
const hasNonInvertedKey = (key) => !!argument.children.find((p) => p.key?.value === key &&
|
|
126
|
+
!p.value
|
|
127
|
+
?.inverted);
|
|
102
128
|
switch (key) {
|
|
103
129
|
case 'advancements':
|
|
104
130
|
case 'distance':
|
|
@@ -149,7 +175,10 @@ export var ItemNode;
|
|
|
149
175
|
}
|
|
150
176
|
ItemNode.is = is;
|
|
151
177
|
function mock(range, isPredicate) {
|
|
152
|
-
const id = core.ResourceLocationNode.mock(range, {
|
|
178
|
+
const id = core.ResourceLocationNode.mock(range, {
|
|
179
|
+
category: 'item',
|
|
180
|
+
allowTag: isPredicate,
|
|
181
|
+
});
|
|
153
182
|
return {
|
|
154
183
|
type: 'mcfunction:item',
|
|
155
184
|
range: core.Range.get(range),
|
|
@@ -174,8 +203,17 @@ export var IntRangeNode;
|
|
|
174
203
|
export var ObjectiveCriteriaNode;
|
|
175
204
|
(function (ObjectiveCriteriaNode) {
|
|
176
205
|
ObjectiveCriteriaNode.SimpleValues = [
|
|
177
|
-
'air',
|
|
178
|
-
'
|
|
206
|
+
'air',
|
|
207
|
+
'armor',
|
|
208
|
+
'deathCount',
|
|
209
|
+
'dummy',
|
|
210
|
+
'food',
|
|
211
|
+
'health',
|
|
212
|
+
'level',
|
|
213
|
+
'playerKillCount',
|
|
214
|
+
'totalKillCount',
|
|
215
|
+
'trigger',
|
|
216
|
+
'xp',
|
|
179
217
|
];
|
|
180
218
|
ObjectiveCriteriaNode.ComplexCategories = new Map([
|
|
181
219
|
['broken', 'item'],
|
|
@@ -219,7 +257,9 @@ export var ParticleNode;
|
|
|
219
257
|
}
|
|
220
258
|
ParticleNode.is = is;
|
|
221
259
|
function mock(range) {
|
|
222
|
-
const id = core.ResourceLocationNode.mock(range, {
|
|
260
|
+
const id = core.ResourceLocationNode.mock(range, {
|
|
261
|
+
category: 'particle_type',
|
|
262
|
+
});
|
|
223
263
|
return {
|
|
224
264
|
type: 'mcfunction:particle',
|
|
225
265
|
range: core.Range.get(range),
|