itemscore-helper 1.2.0 → 1.2.1
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/README.md +15 -7
- package/bin/cli.js +7 -13
- package/bin/mcp.js +1 -1
- package/data/itemscore-api.json +2 -2
- package/lib/manifest.js +131 -2
- package/package.json +2 -2
- package/skill/ITEM_FORMAT.md +5 -0
- package/skill/SKILL.md +2 -2
- /package/skill/examples/{healers_touch.item → healers_touch.import} +0 -0
- /package/skill/examples/{storm_blade.item → storm_blade.import} +0 -0
package/README.md
CHANGED
|
@@ -2,15 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
Set up **any** AI assistant to build and edit custom Minecraft items for the [ItemsCore](https://www.coredevelopment.shop/plugins/itemscore) plugin. Works with Claude, Codex, Cursor, Gemini, and anything else that supports MCP or custom instructions. You do not need to know how to code.
|
|
4
4
|
|
|
5
|
-
## Easiest:
|
|
5
|
+
## Easiest: paste one prompt into your AI
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Copy the prompt below and paste it into your AI (Claude, Cursor, Gemini, Codex, anything). It downloads and connects the ItemsCore tools itself, then asks what item you want - no terminal knowledge needed:
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
|
|
9
|
+
```text
|
|
10
|
+
You are my coding agent and I do not know how to code, so do the whole setup yourself - actually run the commands, do not just tell me what to run.
|
|
11
|
+
|
|
12
|
+
1. In my project folder, run this to download and connect the ItemsCore tools:
|
|
13
|
+
npx -y itemscore-helper install
|
|
14
|
+
It installs a local helper and wires an "itemscore" MCP server into this project. When it finishes, reload your MCP servers (or ask me to restart this app) so the itemscore tools load. If you truly cannot run commands, add this to your MCP config instead:
|
|
15
|
+
{"mcpServers":{"itemscore":{"command":"npx","args":["-y","itemscore-helper","serve"]}}}
|
|
16
|
+
|
|
17
|
+
2. Read https://www.coredevelopment.shop/llms.txt so you know the ItemsCore item format and every method available.
|
|
18
|
+
|
|
19
|
+
3. Tell me you are ready, then ask what item I want. Build a valid .import file (clean item JSON), validate it, and tell me to drop it in plugins/ItemsCore/imports/ and run /ic import in-game. The item stays fully editable in the in-game editor.
|
|
11
20
|
```
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
The same prompt is on the [docs page](https://www.coredevelopment.shop/docs/itemscore) with a one-click Copy button.
|
|
14
23
|
|
|
15
24
|
## Or set it up in one command
|
|
16
25
|
|
|
@@ -32,7 +41,6 @@ The MCP server is `npx -y itemscore-helper serve`. It speaks the standard stdio
|
|
|
32
41
|
| Command | What it does |
|
|
33
42
|
|---|---|
|
|
34
43
|
| `npx itemscore-helper` | Auto-detect your AI tools and connect the local MCP server |
|
|
35
|
-
| `npx itemscore-helper prompt` | Print a prompt to paste into your AI so it sets itself up |
|
|
36
44
|
| `npx itemscore-helper --dry-run` | Show what would change, without writing anything |
|
|
37
45
|
| `npx itemscore-helper serve` | Run the local MCP server (this is what your AI runs) |
|
|
38
46
|
| `npx itemscore-helper print` | Print the skill instructions to stdout |
|
|
@@ -81,7 +89,7 @@ After connecting, give your AI the `SKILL.md` file as its instructions (paste it
|
|
|
81
89
|
## How you use it
|
|
82
90
|
|
|
83
91
|
1. Ask your AI for an item.
|
|
84
|
-
2. It writes a small `.
|
|
92
|
+
2. It writes a small `.import` (JSON) file and validates it.
|
|
85
93
|
3. Put that file in `plugins/ItemsCore/imports/` on your server.
|
|
86
94
|
4. Run `/ic import <name>` in-game. The item is live and stays editable in the in-game editor.
|
|
87
95
|
|
package/bin/cli.js
CHANGED
|
@@ -11,16 +11,16 @@ const DOCS_URL = "https://www.coredevelopment.shop/docs/itemscore"
|
|
|
11
11
|
const SKILL_DIR = path.join(__dirname, "..", "skill")
|
|
12
12
|
|
|
13
13
|
const SETUP_PROMPT = [
|
|
14
|
-
"
|
|
14
|
+
"You are my coding agent and I do not know how to code, so do the whole setup yourself - actually run the commands, do not just tell me what to run.",
|
|
15
15
|
"",
|
|
16
|
-
"1.
|
|
17
|
-
"",
|
|
18
|
-
"2. Set up the ItemsCore tools so you can look up methods and validate items for me. In my project folder, run:",
|
|
16
|
+
"1. In my project folder, run this to download and connect the ItemsCore tools:",
|
|
19
17
|
" npx -y itemscore-helper install",
|
|
20
|
-
'
|
|
18
|
+
' It installs a local helper and wires an "itemscore" MCP server into this project. When it finishes, reload your MCP servers (or ask me to restart this app) so the itemscore tools load. If you truly cannot run commands, add this to your MCP config instead:',
|
|
21
19
|
' {"mcpServers":{"itemscore":{"command":"npx","args":["-y","itemscore-helper","serve"]}}}',
|
|
22
20
|
"",
|
|
23
|
-
"
|
|
21
|
+
"2. Read " + LLMS_URL + " so you know the ItemsCore item format and every method available.",
|
|
22
|
+
"",
|
|
23
|
+
"3. Tell me you are ready, then ask what item I want. Build a valid .import file (clean item JSON), validate it, and tell me to drop it in plugins/ItemsCore/imports/ and run /ic import in-game. The item stays fully editable in the in-game editor.",
|
|
24
24
|
].join("\n")
|
|
25
25
|
|
|
26
26
|
function copyDir(src, dest) {
|
|
@@ -52,14 +52,13 @@ function printHelp() {
|
|
|
52
52
|
"",
|
|
53
53
|
"Usage",
|
|
54
54
|
" npx itemscore-helper Auto-detect your AI tools and connect the local MCP server",
|
|
55
|
-
" npx itemscore-helper prompt Print a prompt to paste into your AI so it sets itself up",
|
|
56
55
|
" npx itemscore-helper --dry-run Show what would be changed, without writing anything",
|
|
57
56
|
" npx itemscore-helper serve Run the local MCP server (this is what your AI runs)",
|
|
58
57
|
" npx itemscore-helper print Print the skill instructions (SKILL.md) to stdout",
|
|
59
58
|
" npx itemscore-helper mcp Print the MCP server config",
|
|
60
59
|
" npx itemscore-helper help Show this help",
|
|
61
60
|
"",
|
|
62
|
-
"
|
|
61
|
+
"Don't want to use a terminal? Copy the setup prompt from " + DOCS_URL + " and paste it into your AI - it does the rest.",
|
|
63
62
|
"Supports Claude (Code & Desktop), Cursor, Gemini CLI, Codex, Windsurf and any MCP client.",
|
|
64
63
|
"Docs: " + DOCS_URL,
|
|
65
64
|
"",
|
|
@@ -76,10 +75,6 @@ function printSkill() {
|
|
|
76
75
|
process.stdout.write(fs.readFileSync(path.join(SKILL_DIR, "SKILL.md"), "utf8"))
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
function printPrompt() {
|
|
80
|
-
console.log(SETUP_PROMPT)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
78
|
function manualLines() {
|
|
84
79
|
return [
|
|
85
80
|
" Add this to your AI client's MCP config (same for Claude, Cursor, Gemini):",
|
|
@@ -139,7 +134,6 @@ function main() {
|
|
|
139
134
|
}
|
|
140
135
|
if (args.cmd === "help") return printHelp()
|
|
141
136
|
if (args.cmd === "mcp") return printMcp()
|
|
142
|
-
if (args.cmd === "prompt") return printPrompt()
|
|
143
137
|
if (args.cmd === "print") return printSkill()
|
|
144
138
|
if (args.cmd !== "install") {
|
|
145
139
|
console.error("Unknown command: " + args.cmd + "\nRun: npx itemscore-helper help")
|
package/bin/mcp.js
CHANGED
|
@@ -106,7 +106,7 @@ async function main() {
|
|
|
106
106
|
{
|
|
107
107
|
title: "Validate an ItemsCore item",
|
|
108
108
|
description:
|
|
109
|
-
"Validate a clean item JSON object against the ItemsCore schema. Reports errors (must fix) and warnings (likely fine). Run this before giving the user a .
|
|
109
|
+
"Validate a clean item JSON object against the ItemsCore schema. Reports errors (must fix) and warnings (likely fine). Run this before giving the user a .import file.",
|
|
110
110
|
inputSchema: { item: z.unknown().describe("The clean item JSON object to validate") },
|
|
111
111
|
},
|
|
112
112
|
async ({ item }) => jsonResult(M.validateItem(idx, item))
|
package/data/itemscore-api.json
CHANGED
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
"returnsValue": true,
|
|
353
353
|
"useless": false,
|
|
354
354
|
"documented": true,
|
|
355
|
-
"description": "Builds a movement equation (using 't' for time)
|
|
355
|
+
"description": "Builds a movement equation (using 't' for time in ticks) that shapes a custom projectile path. Axes are relative to the shooter's facing: X = forward (the way they look), Y = up, Z = left. A normal forward-flying projectile puts the motion on X, e.g. createEquationVector(\"t * 0.4\", \"0\", \"0\"); use Y for arc and Z to curve sideways.",
|
|
356
356
|
"category": "Projectiles",
|
|
357
357
|
"example": "core.createEquationVector(\"cos(t)\", \"0\", \"sin(t)\")"
|
|
358
358
|
},
|
|
@@ -2509,7 +2509,7 @@
|
|
|
2509
2509
|
"returnsValue": false,
|
|
2510
2510
|
"useless": false,
|
|
2511
2511
|
"documented": true,
|
|
2512
|
-
"description": "Fires a custom ItemsCore projectile from a player, following the given path equation for a set number of ticks.",
|
|
2512
|
+
"description": "Fires a custom ItemsCore projectile from a player, following the given path equation for a set number of ticks. The equation axes are relative to where the player looks: X is forward, Y is up, Z is left - so a straight forward shot puts the motion on X.",
|
|
2513
2513
|
"category": "Projectiles",
|
|
2514
2514
|
"example": "core.shootProjectile(player, player.getEyeLocation(), \"FIREBALL\", null, 60, true, core.createEquationVector(\"t\", \"0\", \"t\"))"
|
|
2515
2515
|
},
|
package/lib/manifest.js
CHANGED
|
@@ -88,6 +88,123 @@ function getMethod(idx, name, binding) {
|
|
|
88
88
|
return out
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
const NUMERIC_TYPES = new Set([
|
|
92
|
+
"int", "long", "double", "float", "short", "byte",
|
|
93
|
+
"Integer", "Long", "Double", "Float", "Short", "Byte", "Number",
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
// Object types that cause a hard ClassCastException when mismatched (cannot be coerced at runtime).
|
|
97
|
+
// Mismatching any of these against each other, or against a primitive/literal, is a real bug.
|
|
98
|
+
const HARD_OBJECT_TYPES = new Set(["Location", "ParticleDisplay", "Vector", "World"])
|
|
99
|
+
|
|
100
|
+
// Return types of the few bukkit getters agents use constantly. Lets us catch a swap even when one
|
|
101
|
+
// side is a raw Spigot call (e.g. player.getLocation()) that is not in the manifest.
|
|
102
|
+
const BUKKIT_GETTER_RETURNS = {
|
|
103
|
+
getlocation: "Location",
|
|
104
|
+
geteyelocation: "Location",
|
|
105
|
+
getworld: "World",
|
|
106
|
+
getdirection: "Vector",
|
|
107
|
+
getvelocity: "Vector",
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// A handful of action variables whose type we know for sure.
|
|
111
|
+
const KNOWN_VARIABLE_TYPES = {
|
|
112
|
+
landlocation: "Location",
|
|
113
|
+
lastlocation: "Location",
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function simpleType(t) {
|
|
117
|
+
if (!t) return ""
|
|
118
|
+
const dot = t.lastIndexOf(".")
|
|
119
|
+
return dot >= 0 ? t.slice(dot + 1) : t
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Returns a description of what an argument produces, as { kind, type? }.
|
|
123
|
+
// kind is one of: "unknown", "string", "number", "boolean", "type".
|
|
124
|
+
function producedType(idx, arg) {
|
|
125
|
+
if (arg === null) return { kind: "unknown" }
|
|
126
|
+
if (typeof arg === "string") return { kind: "string" }
|
|
127
|
+
if (typeof arg === "number") return { kind: "number" }
|
|
128
|
+
if (typeof arg === "boolean") return { kind: "boolean" }
|
|
129
|
+
if (typeof arg !== "object") return { kind: "unknown" }
|
|
130
|
+
|
|
131
|
+
if (typeof arg.var === "string") {
|
|
132
|
+
const known = KNOWN_VARIABLE_TYPES[arg.var.toLowerCase()]
|
|
133
|
+
return known ? asProduced(known) : { kind: "unknown" }
|
|
134
|
+
}
|
|
135
|
+
if (typeof arg.call === "string") {
|
|
136
|
+
const call = arg.call
|
|
137
|
+
if (call.includes(".")) {
|
|
138
|
+
const receiver = call.slice(0, call.indexOf("."))
|
|
139
|
+
const method = call.slice(call.indexOf(".") + 1)
|
|
140
|
+
if (idx.BINDING_NAMES.includes(receiver)) {
|
|
141
|
+
const found = getMethod(idx, method, receiver)
|
|
142
|
+
if (found.length === 1 && found[0].returns && found[0].returns !== "void") {
|
|
143
|
+
return asProduced(found[0].returns)
|
|
144
|
+
}
|
|
145
|
+
return { kind: "unknown" }
|
|
146
|
+
}
|
|
147
|
+
const getter = BUKKIT_GETTER_RETURNS[method.toLowerCase()]
|
|
148
|
+
return getter ? asProduced(getter) : { kind: "unknown" }
|
|
149
|
+
}
|
|
150
|
+
return { kind: "unknown" }
|
|
151
|
+
}
|
|
152
|
+
return { kind: "unknown" }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function asProduced(simpleName) {
|
|
156
|
+
const s = simpleType(simpleName)
|
|
157
|
+
if (NUMERIC_TYPES.has(s)) return { kind: "number" }
|
|
158
|
+
if (s === "boolean" || s === "Boolean") return { kind: "boolean" }
|
|
159
|
+
if (s === "String" || s === "CharSequence") return { kind: "string" }
|
|
160
|
+
return { kind: "type", type: s }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Conservative: returns true unless the arg is a definite, uncoercible mismatch.
|
|
164
|
+
function argCompatible(expectedRaw, produced) {
|
|
165
|
+
const expected = simpleType(expectedRaw)
|
|
166
|
+
if (!expected || expected === "Object") return true
|
|
167
|
+
if (produced.kind === "unknown") return true
|
|
168
|
+
|
|
169
|
+
if (expected === "String" || expected === "CharSequence") return true
|
|
170
|
+
if (NUMERIC_TYPES.has(expected)) {
|
|
171
|
+
if (produced.kind === "type") return NUMERIC_TYPES.has(produced.type)
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
if (expected === "boolean" || expected === "Boolean") {
|
|
175
|
+
if (produced.kind === "type") return false
|
|
176
|
+
return true
|
|
177
|
+
}
|
|
178
|
+
if (HARD_OBJECT_TYPES.has(expected)) {
|
|
179
|
+
if (produced.kind === "type") return produced.type === expected || !HARD_OBJECT_TYPES.has(produced.type)
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
return true
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function describeProduced(produced) {
|
|
186
|
+
if (produced.kind === "type") return produced.type
|
|
187
|
+
if (produced.kind === "string") return "string"
|
|
188
|
+
if (produced.kind === "number") return "number"
|
|
189
|
+
if (produced.kind === "boolean") return "boolean"
|
|
190
|
+
return "value"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function validateArgs(idx, method, args, p, errors) {
|
|
194
|
+
if (!method || !Array.isArray(method.params) || !Array.isArray(args)) return
|
|
195
|
+
for (let i = 0; i < args.length && i < method.params.length; i++) {
|
|
196
|
+
const param = method.params[i]
|
|
197
|
+
const expected = param.type || param.jvmType
|
|
198
|
+
const produced = producedType(idx, args[i])
|
|
199
|
+
if (!argCompatible(expected, produced)) {
|
|
200
|
+
errors.push(
|
|
201
|
+
p + '.args[' + i + '] (parameter "' + param.name + '") expects ' + simpleType(expected) +
|
|
202
|
+
" but a " + describeProduced(produced) + " was passed. Check the argument order against the method signature: " + method.signature
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
91
208
|
function validateStep(idx, step, p, errors, warnings) {
|
|
92
209
|
if (typeof step !== "object" || step === null) {
|
|
93
210
|
errors.push(p + " must be an object")
|
|
@@ -105,8 +222,14 @@ function validateStep(idx, step, p, errors, warnings) {
|
|
|
105
222
|
const found = getMethod(idx, method, receiver)
|
|
106
223
|
if (found.length === 0) {
|
|
107
224
|
errors.push(p + '.call "' + call + '" references unknown method "' + method + '" on binding "' + receiver + '"')
|
|
108
|
-
} else
|
|
109
|
-
|
|
225
|
+
} else {
|
|
226
|
+
if (found[0].useless) {
|
|
227
|
+
warnings.push(p + '.call "' + call + '" is flagged useless (no real effect)')
|
|
228
|
+
}
|
|
229
|
+
// Only type-check when the method is unambiguous (one overload) so we never guess wrong.
|
|
230
|
+
if (found.length === 1 && Array.isArray(step.args)) {
|
|
231
|
+
validateArgs(idx, found[0], step.args, p, errors)
|
|
232
|
+
}
|
|
110
233
|
}
|
|
111
234
|
} else if (!idx.KNOWN_RECEIVERS.has(receiver)) {
|
|
112
235
|
warnings.push(p + '.call receiver "' + receiver + '" is not a known variable; make sure it is defined earlier or exists at runtime')
|
|
@@ -144,6 +267,9 @@ function validateItem(idx, item) {
|
|
|
144
267
|
if (action.needBlock !== undefined && (typeof action.needBlock !== "string" || !NEED_BLOCK_VALUES.includes(action.needBlock))) {
|
|
145
268
|
errors.push(ap + ".needBlock must be one of " + NEED_BLOCK_VALUES.join(", "))
|
|
146
269
|
}
|
|
270
|
+
if (action.cooldown !== undefined && typeof action.cooldown !== "string" && typeof action.cooldown !== "number") {
|
|
271
|
+
warnings.push(ap + '.cooldown should be a duration like "5s", "1m", "1h" or a number of seconds')
|
|
272
|
+
}
|
|
147
273
|
if (!Array.isArray(action.steps)) errors.push(ap + ".steps must be an array")
|
|
148
274
|
else action.steps.forEach((s, si) => validateStep(idx, s, ap + ".steps[" + si + "]", errors, warnings))
|
|
149
275
|
})
|
|
@@ -181,6 +307,7 @@ function itemSchema(idx) {
|
|
|
181
307
|
action: {
|
|
182
308
|
trigger: "string (required) - one of: " + idx.TRIGGER_NAMES.join(", "),
|
|
183
309
|
needBlock: "BOTH | AIR | BLOCK - optional per-action override",
|
|
310
|
+
cooldown: 'optional - per-player reuse delay for this action. Any duration: a number plus a unit s/m/h, e.g. "5s", "45s", "2m", "90s", "1h" (a bare number means seconds). Omit or "0" for no cooldown.',
|
|
184
311
|
steps: "Step[] - executed in order, combined by operatorToNext",
|
|
185
312
|
},
|
|
186
313
|
step: {
|
|
@@ -193,6 +320,8 @@ function itemSchema(idx) {
|
|
|
193
320
|
},
|
|
194
321
|
bukkitObjects:
|
|
195
322
|
"player, shooter, victim, arrow, event, and any entity, block, world, location, or ItemStack returned by a method are real Bukkit/Spigot objects. You can call any standard Spigot method on them in a step (for example player.sendMessage, player.getHealth, player.getWorld, victim.setFireTicks), not only the ItemsCore methods. These are not in the method list below; see https://hub.spigotmc.org/javadocs/spigot/ for the full Spigot API. Prefer a core method when one exists.",
|
|
323
|
+
projectiles:
|
|
324
|
+
'For core.shootProjectile + core.createEquationVector the equation axes are relative to where the player looks: X = forward, Y = up, Z = left. A straight forward-flying projectile puts the motion on X, e.g. core.createEquationVector("t * 0.4", "0", "0"). Use Y for arc/gravity and Z to curve sideways.',
|
|
196
325
|
bindings: idx.bindings.map((b) => ({ name: b.name, description: b.description || "", methodCount: (b.methods || []).length })),
|
|
197
326
|
variables: idx.variables,
|
|
198
327
|
triggers: idx.triggers,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "itemscore-helper",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "One command sets up any AI (Claude, Codex, Cursor, Gemini, and others) to build and edit custom Minecraft items for the ItemsCore plugin. Auto-detects your AI tools and connects a local MCP server that runs entirely on your machine.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"itemscore-helper": "bin/cli.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"homepage": "https://www.coredevelopment.shop/docs/itemscore",
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/Core-Pluginss/ItemsCore-Helper.git"
|
|
35
35
|
},
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"author": "TastyCake",
|
package/skill/ITEM_FORMAT.md
CHANGED
|
@@ -31,6 +31,7 @@ This is the offline reference for the item format ItemsCore imports. When the li
|
|
|
31
31
|
|---|---|---|
|
|
32
32
|
| `trigger` | string, required | One of the triggers below |
|
|
33
33
|
| `needBlock` | `BOTH` \| `AIR` \| `BLOCK` | Optional per-action override of the item default |
|
|
34
|
+
| `cooldown` | duration string | Optional per-player reuse delay. Any duration: a number plus a unit s/m/h, e.g. `"5s"`, `"45s"`, `"2m"`, `"90s"`, `"1h"` (a bare number means seconds) |
|
|
34
35
|
| `steps` | Step[] | Run in order, combined by each step's `operatorToNext` |
|
|
35
36
|
|
|
36
37
|
### Triggers
|
|
@@ -131,6 +132,10 @@ These are not in the `core` / `particles` / `values` / `api` method list. For th
|
|
|
131
132
|
|
|
132
133
|
Always confirm a method's exact name and parameters with `get_method` / `search_methods` before using it. Do not guess.
|
|
133
134
|
|
|
135
|
+
## Projectiles
|
|
136
|
+
|
|
137
|
+
`core.shootProjectile` and `core.createEquationVector` use a movement equation whose axes are relative to where the player looks: **X = forward, Y = up, Z = left**. A straight forward-flying projectile puts the motion on X, e.g. `core.createEquationVector("t * 0.4", "0", "0")`. Use Y for arc/gravity and Z to curve left/right.
|
|
138
|
+
|
|
134
139
|
## Worked example: a healing ability wand
|
|
135
140
|
|
|
136
141
|
```json
|
package/skill/SKILL.md
CHANGED
|
@@ -9,7 +9,7 @@ ItemsCore is a Minecraft (Bukkit/Spigot) plugin that lets a server owner create
|
|
|
9
9
|
|
|
10
10
|
## The one rule that matters
|
|
11
11
|
|
|
12
|
-
Always produce a **clean item JSON** (the format described below). Never hand-write the plugin's internal
|
|
12
|
+
Always produce a **clean item JSON** (the format described below). Never hand-write the plugin's internal item YAML or its generated JavaScript `code`. When the user imports your JSON, the plugin builds **both** the runnable code **and** the in-game GUI action graph from it. That means an item you create this way still works **and** stays fully editable in the in-game editor. Hand-writing raw code produces an item the GUI cannot open, which is exactly the problem this format avoids.
|
|
13
13
|
|
|
14
14
|
## Step 1: Get the API (do this first)
|
|
15
15
|
|
|
@@ -86,7 +86,7 @@ Run `validate_item` on your JSON. Fix every entry under `errors` before continui
|
|
|
86
86
|
|
|
87
87
|
## Step 4: Hand it to the user
|
|
88
88
|
|
|
89
|
-
1. Save the JSON as `<name>.
|
|
89
|
+
1. Save the JSON as `<name>.import` (or `<name>.json`).
|
|
90
90
|
2. Tell the user to put the file in `plugins/ItemsCore/imports/` on their server.
|
|
91
91
|
3. Tell them to run `/ic import <name>` in-game.
|
|
92
92
|
|
|
File without changes
|
|
File without changes
|