@doodle-engine/cli 0.0.16 â 0.0.17
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/CHANGELOG.md +9 -0
- package/dist/cli.js +293 -232
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command as
|
|
3
|
-
import { crayon as
|
|
4
|
-
import { createServer as
|
|
2
|
+
import { Command as x } from "commander";
|
|
3
|
+
import { crayon as s } from "crayon.js";
|
|
4
|
+
import { createServer as q, build as $ } from "vite";
|
|
5
5
|
import S from "@vitejs/plugin-react";
|
|
6
6
|
import { watch as H } from "chokidar";
|
|
7
|
-
import { readdir as p, readFile as f, mkdir as D, writeFile as
|
|
8
|
-
import { join as h, extname as g, relative as
|
|
9
|
-
import { parse as
|
|
10
|
-
import { parseDialogue as
|
|
7
|
+
import { readdir as p, readFile as f, mkdir as D, writeFile as w } from "fs/promises";
|
|
8
|
+
import { join as h, extname as g, relative as y, dirname as M } from "path";
|
|
9
|
+
import { parse as b } from "yaml";
|
|
10
|
+
import { parseDialogue as k } from "@doodle-engine/core";
|
|
11
11
|
import F from "prompts";
|
|
12
|
-
function
|
|
12
|
+
function N(n, a) {
|
|
13
13
|
const r = [];
|
|
14
|
-
for (const o of Object.values(
|
|
15
|
-
const
|
|
16
|
-
r.push(...
|
|
14
|
+
for (const o of Object.values(n.dialogues)) {
|
|
15
|
+
const t = a.get(o.id) || `dialogue:${o.id}`;
|
|
16
|
+
r.push(...B(o, t));
|
|
17
17
|
}
|
|
18
|
-
for (const o of Object.values(
|
|
19
|
-
if (o.dialogue && !
|
|
20
|
-
const
|
|
18
|
+
for (const o of Object.values(n.characters))
|
|
19
|
+
if (o.dialogue && !n.dialogues[o.dialogue]) {
|
|
20
|
+
const t = a.get(o.id) || `character:${o.id}`;
|
|
21
21
|
r.push({
|
|
22
|
-
file:
|
|
22
|
+
file: t,
|
|
23
23
|
message: `Character "${o.id}" references non-existent dialogue "${o.dialogue}"`,
|
|
24
24
|
suggestion: `Create dialogue "${o.dialogue}" or fix the reference`
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
return r.push(...W(
|
|
27
|
+
return r.push(...W(n, a)), r;
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function B(n, a) {
|
|
30
30
|
const r = [], o = /* @__PURE__ */ new Set();
|
|
31
|
-
for (const
|
|
32
|
-
o.has(
|
|
31
|
+
for (const t of n.nodes)
|
|
32
|
+
o.has(t.id) && r.push({
|
|
33
33
|
file: a,
|
|
34
|
-
message: `Duplicate node ID "${
|
|
34
|
+
message: `Duplicate node ID "${t.id}"`,
|
|
35
35
|
suggestion: "Node IDs must be unique within a dialogue"
|
|
36
|
-
}), o.add(
|
|
37
|
-
o.has(
|
|
36
|
+
}), o.add(t.id);
|
|
37
|
+
o.has(n.startNode) || r.push({
|
|
38
38
|
file: a,
|
|
39
|
-
message: `Start node "${
|
|
40
|
-
suggestion: `Add a NODE ${
|
|
39
|
+
message: `Start node "${n.startNode}" not found`,
|
|
40
|
+
suggestion: `Add a NODE ${n.startNode} or fix the startNode reference`
|
|
41
41
|
});
|
|
42
|
-
for (const
|
|
43
|
-
r.push(...
|
|
42
|
+
for (const t of n.nodes)
|
|
43
|
+
r.push(...L(t, o, a));
|
|
44
44
|
return r;
|
|
45
45
|
}
|
|
46
|
-
function
|
|
46
|
+
function L(n, a, r) {
|
|
47
47
|
const o = [];
|
|
48
|
-
if (
|
|
48
|
+
if (n.next && !a.has(n.next) && o.push({
|
|
49
49
|
file: r,
|
|
50
|
-
message: `Node "${
|
|
51
|
-
suggestion: `Add NODE ${
|
|
52
|
-
}),
|
|
53
|
-
for (const
|
|
54
|
-
a.has(
|
|
50
|
+
message: `Node "${n.id}" GOTO "${n.next}" points to non-existent node`,
|
|
51
|
+
suggestion: `Add NODE ${n.next} or fix the GOTO target`
|
|
52
|
+
}), n.conditionalNext)
|
|
53
|
+
for (const t of n.conditionalNext)
|
|
54
|
+
a.has(t.next) || o.push({
|
|
55
55
|
file: r,
|
|
56
|
-
message: `Node "${
|
|
57
|
-
suggestion: `Add NODE ${
|
|
58
|
-
}), o.push(...v(
|
|
59
|
-
for (const
|
|
60
|
-
if (!
|
|
56
|
+
message: `Node "${n.id}" IF block GOTO "${t.next}" points to non-existent node`,
|
|
57
|
+
suggestion: `Add NODE ${t.next} or fix the GOTO target`
|
|
58
|
+
}), o.push(...v(t.condition, n.id, r));
|
|
59
|
+
for (const t of n.choices) {
|
|
60
|
+
if (!t.effects?.some(
|
|
61
61
|
(e) => e.type === "endDialogue" || e.type === "goToLocation" || e.type === "startDialogue"
|
|
62
|
-
) &&
|
|
62
|
+
) && t.next && !a.has(t.next) && o.push({
|
|
63
63
|
file: r,
|
|
64
|
-
message: `Node "${
|
|
65
|
-
suggestion: `Add NODE ${
|
|
66
|
-
}),
|
|
67
|
-
for (const e of
|
|
68
|
-
o.push(...v(e,
|
|
69
|
-
if (
|
|
70
|
-
for (const e of
|
|
71
|
-
o.push(...C(e,
|
|
64
|
+
message: `Node "${n.id}" choice "${t.id}" GOTO "${t.next}" points to non-existent node`,
|
|
65
|
+
suggestion: `Add NODE ${t.next} or fix the GOTO target`
|
|
66
|
+
}), t.conditions)
|
|
67
|
+
for (const e of t.conditions)
|
|
68
|
+
o.push(...v(e, n.id, r));
|
|
69
|
+
if (t.effects)
|
|
70
|
+
for (const e of t.effects)
|
|
71
|
+
o.push(...C(e, n.id, r));
|
|
72
72
|
}
|
|
73
|
-
if (
|
|
74
|
-
for (const
|
|
75
|
-
o.push(...v(
|
|
76
|
-
if (
|
|
77
|
-
for (const
|
|
78
|
-
o.push(...C(
|
|
73
|
+
if (n.conditions)
|
|
74
|
+
for (const t of n.conditions)
|
|
75
|
+
o.push(...v(t, n.id, r));
|
|
76
|
+
if (n.effects)
|
|
77
|
+
for (const t of n.effects)
|
|
78
|
+
o.push(...C(t, n.id, r));
|
|
79
79
|
return o;
|
|
80
80
|
}
|
|
81
|
-
const
|
|
81
|
+
const P = {
|
|
82
82
|
hasFlag: ["flag"],
|
|
83
83
|
notFlag: ["flag"],
|
|
84
84
|
hasItem: ["itemId"],
|
|
@@ -123,51 +123,51 @@ const L = {
|
|
|
123
123
|
showInterlude: ["interludeId"],
|
|
124
124
|
roll: ["variable", "min", "max"]
|
|
125
125
|
};
|
|
126
|
-
function v(
|
|
126
|
+
function v(n, a, r) {
|
|
127
127
|
const o = [];
|
|
128
|
-
if (!
|
|
128
|
+
if (!n.type)
|
|
129
129
|
return o.push({
|
|
130
130
|
file: r,
|
|
131
131
|
message: `Node "${a}" has condition with missing type`
|
|
132
132
|
}), o;
|
|
133
|
-
if (
|
|
134
|
-
return
|
|
133
|
+
if (n.type === "timeIs")
|
|
134
|
+
return n.hour === void 0 && n.day === void 0 && o.push({
|
|
135
135
|
file: r,
|
|
136
136
|
message: `Node "${a}" condition "timeIs" must have at least one of "hour" or "day" argument`
|
|
137
137
|
}), o;
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
138
|
+
const t = P[n.type];
|
|
139
|
+
if (!t)
|
|
140
140
|
return o;
|
|
141
|
-
for (const
|
|
142
|
-
(
|
|
141
|
+
for (const i of t)
|
|
142
|
+
(n[i] === void 0 || n[i] === null || n[i] === "") && o.push({
|
|
143
143
|
file: r,
|
|
144
|
-
message: `Node "${a}" condition "${
|
|
144
|
+
message: `Node "${a}" condition "${n.type}" missing required "${i}" argument`
|
|
145
145
|
});
|
|
146
146
|
return o;
|
|
147
147
|
}
|
|
148
|
-
function C(
|
|
148
|
+
function C(n, a, r) {
|
|
149
149
|
const o = [];
|
|
150
|
-
if (!
|
|
150
|
+
if (!n.type)
|
|
151
151
|
return o.push({
|
|
152
152
|
file: r,
|
|
153
153
|
message: `Node "${a}" has effect with missing type`
|
|
154
154
|
}), o;
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
155
|
+
const t = U[n.type];
|
|
156
|
+
if (!t)
|
|
157
157
|
return o;
|
|
158
|
-
for (const
|
|
159
|
-
(
|
|
158
|
+
for (const i of t)
|
|
159
|
+
(n[i] === void 0 || n[i] === null || n[i] === "") && o.push({
|
|
160
160
|
file: r,
|
|
161
|
-
message: `Node "${a}" effect "${
|
|
161
|
+
message: `Node "${a}" effect "${n.type}" missing required "${i}" argument`
|
|
162
162
|
});
|
|
163
163
|
return o;
|
|
164
164
|
}
|
|
165
|
-
function W(
|
|
165
|
+
function W(n, a) {
|
|
166
166
|
const r = [], o = /* @__PURE__ */ new Set();
|
|
167
|
-
for (const e of Object.values(
|
|
167
|
+
for (const e of Object.values(n.locales))
|
|
168
168
|
for (const c of Object.keys(e))
|
|
169
169
|
o.add(c);
|
|
170
|
-
const
|
|
170
|
+
const t = (e) => e.startsWith("@"), i = (e, c, l) => {
|
|
171
171
|
const d = e.slice(1);
|
|
172
172
|
if (!o.has(d)) {
|
|
173
173
|
const u = a.get(c) || `${l}:${c}`;
|
|
@@ -178,86 +178,86 @@ function W(t, a) {
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
|
-
for (const e of Object.values(
|
|
182
|
-
|
|
183
|
-
for (const e of Object.values(
|
|
184
|
-
|
|
185
|
-
for (const e of Object.values(
|
|
186
|
-
|
|
187
|
-
for (const e of Object.values(
|
|
188
|
-
|
|
181
|
+
for (const e of Object.values(n.locations))
|
|
182
|
+
t(e.name) && i(e.name, e.id, "location"), t(e.description) && i(e.description, e.id, "location");
|
|
183
|
+
for (const e of Object.values(n.characters))
|
|
184
|
+
t(e.name) && i(e.name, e.id, "character"), t(e.biography) && i(e.biography, e.id, "character");
|
|
185
|
+
for (const e of Object.values(n.items))
|
|
186
|
+
t(e.name) && i(e.name, e.id, "item"), t(e.description) && i(e.description, e.id, "item");
|
|
187
|
+
for (const e of Object.values(n.quests)) {
|
|
188
|
+
t(e.name) && i(e.name, e.id, "quest"), t(e.description) && i(e.description, e.id, "quest");
|
|
189
189
|
for (const c of e.stages)
|
|
190
|
-
|
|
190
|
+
t(c.description) && i(c.description, e.id, "quest");
|
|
191
191
|
}
|
|
192
|
-
for (const e of Object.values(
|
|
193
|
-
|
|
194
|
-
for (const e of Object.values(
|
|
192
|
+
for (const e of Object.values(n.journalEntries))
|
|
193
|
+
t(e.title) && i(e.title, e.id, "journal"), t(e.text) && i(e.text, e.id, "journal");
|
|
194
|
+
for (const e of Object.values(n.dialogues))
|
|
195
195
|
for (const c of e.nodes) {
|
|
196
|
-
|
|
196
|
+
t(c.text) && i(c.text, e.id, "dialogue");
|
|
197
197
|
for (const l of c.choices)
|
|
198
|
-
|
|
198
|
+
t(l.text) && i(l.text, e.id, "dialogue");
|
|
199
199
|
}
|
|
200
|
-
for (const e of Object.values(
|
|
201
|
-
|
|
200
|
+
for (const e of Object.values(n.interludes))
|
|
201
|
+
t(e.text) && i(e.text, e.id, "interlude");
|
|
202
202
|
return r;
|
|
203
203
|
}
|
|
204
|
-
function
|
|
205
|
-
if (
|
|
206
|
-
console.log(
|
|
204
|
+
function I(n) {
|
|
205
|
+
if (n.length === 0) {
|
|
206
|
+
console.log(s.green("â No validation errors"));
|
|
207
207
|
return;
|
|
208
208
|
}
|
|
209
|
-
console.log(
|
|
210
|
-
â Found ${
|
|
209
|
+
console.log(s.red(`
|
|
210
|
+
â Found ${n.length} validation error${n.length === 1 ? "" : "s"}:
|
|
211
211
|
`));
|
|
212
|
-
for (const a of
|
|
213
|
-
console.log(
|
|
212
|
+
for (const a of n)
|
|
213
|
+
console.log(s.bold(a.file) + (a.line ? `:${a.line}` : "")), console.log(" " + s.red(a.message)), a.suggestion && console.log(" " + s.dim(a.suggestion)), console.log();
|
|
214
214
|
}
|
|
215
215
|
const R = "ðū", J = "âĻ", Q = "âïļ", Y = "â";
|
|
216
216
|
async function V() {
|
|
217
|
-
const
|
|
218
|
-
console.log(""), console.log(
|
|
217
|
+
const n = process.cwd(), a = h(n, "content");
|
|
218
|
+
console.log(""), console.log(s.bold.magenta(` ${R} Doodle Engine Dev Server ${R}`)), console.log("");
|
|
219
219
|
const r = {
|
|
220
220
|
name: "doodle-content-loader",
|
|
221
|
-
configureServer(
|
|
222
|
-
|
|
221
|
+
configureServer(t) {
|
|
222
|
+
t.middlewares.use("/api/content", async (d, u) => {
|
|
223
223
|
try {
|
|
224
224
|
const m = await K(a);
|
|
225
225
|
u.setHeader("Content-Type", "application/json"), u.end(JSON.stringify(m));
|
|
226
226
|
} catch (m) {
|
|
227
|
-
console.error(
|
|
227
|
+
console.error(s.red(" Error loading content:"), m), u.statusCode = 500, u.end(JSON.stringify({ error: "Failed to load content" }));
|
|
228
228
|
}
|
|
229
229
|
});
|
|
230
|
-
const
|
|
230
|
+
const i = H(a, {
|
|
231
231
|
ignored: /(^|[\/\\])\../,
|
|
232
232
|
persistent: !0
|
|
233
233
|
});
|
|
234
234
|
let e = !1;
|
|
235
|
-
|
|
235
|
+
i.on("ready", () => {
|
|
236
236
|
e = !0;
|
|
237
237
|
});
|
|
238
238
|
let c = null;
|
|
239
239
|
const l = (d) => {
|
|
240
240
|
c && clearTimeout(c), c = setTimeout(async () => {
|
|
241
|
-
c = null, console.log(d), await z(a),
|
|
241
|
+
c = null, console.log(d), await z(a), t.ws.send({ type: "full-reload", path: "*" });
|
|
242
242
|
}, 50);
|
|
243
243
|
};
|
|
244
|
-
|
|
245
|
-
l(
|
|
246
|
-
}),
|
|
247
|
-
e && l(
|
|
244
|
+
i.on("change", (d) => {
|
|
245
|
+
l(s.yellow(` ${Q} Content changed: ${d}`));
|
|
246
|
+
}), i.on("add", (d) => {
|
|
247
|
+
e && l(s.green(` ${Y} Content added: ${d}`));
|
|
248
248
|
});
|
|
249
249
|
}
|
|
250
|
-
}, o = await
|
|
251
|
-
root:
|
|
250
|
+
}, o = await q({
|
|
251
|
+
root: n,
|
|
252
252
|
plugins: [S(), r],
|
|
253
253
|
server: {
|
|
254
254
|
port: 3e3,
|
|
255
255
|
open: !0
|
|
256
256
|
}
|
|
257
257
|
});
|
|
258
|
-
await o.listen(), o.printUrls(), console.log(""), console.log(
|
|
258
|
+
await o.listen(), o.printUrls(), console.log(""), console.log(s.dim(` ${J} Watching content files for changes...`)), console.log("");
|
|
259
259
|
}
|
|
260
|
-
async function K(
|
|
260
|
+
async function K(n) {
|
|
261
261
|
const a = {
|
|
262
262
|
locations: {},
|
|
263
263
|
characters: {},
|
|
@@ -279,41 +279,41 @@ async function K(t) {
|
|
|
279
279
|
{ dir: "journal", key: "journalEntries" },
|
|
280
280
|
{ dir: "interludes", key: "interludes" }
|
|
281
281
|
];
|
|
282
|
-
for (const { dir:
|
|
283
|
-
const e = h(
|
|
282
|
+
for (const { dir: t, key: i } of o) {
|
|
283
|
+
const e = h(n, t);
|
|
284
284
|
try {
|
|
285
285
|
const c = await p(e);
|
|
286
286
|
for (const l of c)
|
|
287
287
|
if (g(l) === ".yaml" || g(l) === ".yml") {
|
|
288
|
-
const d = h(e, l), u = await f(d, "utf-8"), m =
|
|
289
|
-
m && m.id && (a[
|
|
288
|
+
const d = h(e, l), u = await f(d, "utf-8"), m = b(u);
|
|
289
|
+
m && m.id && (a[i][m.id] = m);
|
|
290
290
|
}
|
|
291
291
|
} catch {
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
try {
|
|
295
|
-
const
|
|
296
|
-
for (const e of
|
|
295
|
+
const t = h(n, "locales"), i = await p(t);
|
|
296
|
+
for (const e of i)
|
|
297
297
|
if (g(e) === ".yaml" || g(e) === ".yml") {
|
|
298
|
-
const c = h(
|
|
298
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
299
299
|
a.locales[u] = d ?? {};
|
|
300
300
|
}
|
|
301
301
|
} catch {
|
|
302
302
|
}
|
|
303
303
|
try {
|
|
304
|
-
const
|
|
305
|
-
for (const e of
|
|
304
|
+
const t = h(n, "dialogues"), i = await p(t);
|
|
305
|
+
for (const e of i)
|
|
306
306
|
if (g(e) === ".dlg") {
|
|
307
|
-
const c = h(
|
|
307
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
308
308
|
a.dialogues[u.id] = u;
|
|
309
309
|
}
|
|
310
310
|
} catch {
|
|
311
311
|
}
|
|
312
312
|
try {
|
|
313
|
-
const
|
|
314
|
-
r =
|
|
313
|
+
const t = h(n, "game.yaml"), i = await f(t, "utf-8");
|
|
314
|
+
r = b(i);
|
|
315
315
|
} catch {
|
|
316
|
-
console.warn(
|
|
316
|
+
console.warn(s.yellow(" No game.yaml found, using defaults")), r = {
|
|
317
317
|
startLocation: "tavern",
|
|
318
318
|
startTime: { day: 1, hour: 8 },
|
|
319
319
|
startFlags: {},
|
|
@@ -323,15 +323,15 @@ async function K(t) {
|
|
|
323
323
|
}
|
|
324
324
|
return { registry: a, config: r };
|
|
325
325
|
}
|
|
326
|
-
async function z(
|
|
326
|
+
async function z(n) {
|
|
327
327
|
try {
|
|
328
|
-
const { registry: a, fileMap: r } = await X(
|
|
329
|
-
o.length > 0 && (console.log(""),
|
|
328
|
+
const { registry: a, fileMap: r } = await X(n), o = N(a, r);
|
|
329
|
+
o.length > 0 && (console.log(""), I(o), console.log(""));
|
|
330
330
|
} catch (a) {
|
|
331
|
-
console.error(
|
|
331
|
+
console.error(s.red(" Error running validation:"), a);
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
|
-
async function X(
|
|
334
|
+
async function X(n) {
|
|
335
335
|
const a = {
|
|
336
336
|
locations: {},
|
|
337
337
|
characters: {},
|
|
@@ -351,65 +351,65 @@ async function X(t) {
|
|
|
351
351
|
{ dir: "journal", key: "journalEntries" },
|
|
352
352
|
{ dir: "interludes", key: "interludes" }
|
|
353
353
|
];
|
|
354
|
-
for (const { dir:
|
|
355
|
-
const e = h(
|
|
354
|
+
for (const { dir: t, key: i } of o) {
|
|
355
|
+
const e = h(n, t);
|
|
356
356
|
try {
|
|
357
357
|
const c = await p(e);
|
|
358
358
|
for (const l of c)
|
|
359
359
|
if (g(l) === ".yaml" || g(l) === ".yml") {
|
|
360
|
-
const d = h(e, l), u = await f(d, "utf-8"), m =
|
|
361
|
-
m && m.id && (a[
|
|
360
|
+
const d = h(e, l), u = await f(d, "utf-8"), m = b(u);
|
|
361
|
+
m && m.id && (a[i][m.id] = m, r.set(m.id, y(process.cwd(), d)));
|
|
362
362
|
}
|
|
363
363
|
} catch {
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
try {
|
|
367
|
-
const
|
|
368
|
-
for (const e of
|
|
367
|
+
const t = h(n, "locales"), i = await p(t);
|
|
368
|
+
for (const e of i)
|
|
369
369
|
if (g(e) === ".yaml" || g(e) === ".yml") {
|
|
370
|
-
const c = h(
|
|
370
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
371
371
|
a.locales[u] = d ?? {};
|
|
372
372
|
}
|
|
373
373
|
} catch {
|
|
374
374
|
}
|
|
375
375
|
try {
|
|
376
|
-
const
|
|
377
|
-
for (const e of
|
|
376
|
+
const t = h(n, "dialogues"), i = await p(t);
|
|
377
|
+
for (const e of i)
|
|
378
378
|
if (g(e) === ".dlg") {
|
|
379
|
-
const c = h(
|
|
380
|
-
a.dialogues[u.id] = u, r.set(u.id,
|
|
379
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
380
|
+
a.dialogues[u.id] = u, r.set(u.id, y(process.cwd(), c));
|
|
381
381
|
}
|
|
382
382
|
} catch {
|
|
383
383
|
}
|
|
384
384
|
return { registry: a, fileMap: r };
|
|
385
385
|
}
|
|
386
386
|
async function Z() {
|
|
387
|
-
const
|
|
388
|
-
console.log(""), console.log(
|
|
387
|
+
const n = process.cwd(), a = h(n, "content");
|
|
388
|
+
console.log(""), console.log(s.bold.magenta("ð Building Doodle Engine game...")), console.log(""), console.log(s.dim("Validating content..."));
|
|
389
389
|
let r;
|
|
390
390
|
try {
|
|
391
|
-
const { registry: o, fileMap:
|
|
392
|
-
|
|
391
|
+
const { registry: o, fileMap: t, config: i } = await ee(a), e = N(o, t);
|
|
392
|
+
I(e), e.length > 0 && (console.log(s.red("Build failed due to validation errors.")), console.log(""), process.exit(1)), r = { registry: o, config: i };
|
|
393
393
|
} catch (o) {
|
|
394
|
-
console.error(
|
|
394
|
+
console.error(s.red("Error loading content:"), o), process.exit(1);
|
|
395
395
|
}
|
|
396
396
|
console.log("");
|
|
397
397
|
try {
|
|
398
398
|
await $({
|
|
399
|
-
root:
|
|
399
|
+
root: n,
|
|
400
400
|
plugins: [S()],
|
|
401
401
|
build: {
|
|
402
402
|
outDir: "dist",
|
|
403
403
|
emptyOutDir: !0
|
|
404
404
|
}
|
|
405
405
|
});
|
|
406
|
-
const o = h(
|
|
407
|
-
await D(o, { recursive: !0 }), await
|
|
406
|
+
const o = h(n, "dist", "api");
|
|
407
|
+
await D(o, { recursive: !0 }), await w(h(o, "content"), JSON.stringify(r)), console.log(""), console.log(s.green("â
Build complete! Output in dist/")), console.log(""), console.log("To preview the build:"), console.log(s.dim(" yarn preview")), console.log("");
|
|
408
408
|
} catch (o) {
|
|
409
|
-
console.error(
|
|
409
|
+
console.error(s.red("Build failed:"), o), process.exit(1);
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
|
-
async function ee(
|
|
412
|
+
async function ee(n) {
|
|
413
413
|
const a = {
|
|
414
414
|
locations: {},
|
|
415
415
|
characters: {},
|
|
@@ -429,56 +429,56 @@ async function ee(t) {
|
|
|
429
429
|
{ dir: "journal", key: "journalEntries" },
|
|
430
430
|
{ dir: "interludes", key: "interludes" }
|
|
431
431
|
];
|
|
432
|
-
for (const { dir:
|
|
433
|
-
const c = h(
|
|
432
|
+
for (const { dir: i, key: e } of o) {
|
|
433
|
+
const c = h(n, i);
|
|
434
434
|
try {
|
|
435
435
|
const l = await p(c);
|
|
436
436
|
for (const d of l)
|
|
437
437
|
if (g(d) === ".yaml" || g(d) === ".yml") {
|
|
438
|
-
const u = h(c, d), m = await f(u, "utf-8"),
|
|
439
|
-
|
|
438
|
+
const u = h(c, d), m = await f(u, "utf-8"), E = b(m);
|
|
439
|
+
E && E.id && (a[e][E.id] = E, r.set(E.id, y(process.cwd(), u)));
|
|
440
440
|
}
|
|
441
441
|
} catch {
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
444
|
try {
|
|
445
|
-
const
|
|
445
|
+
const i = h(n, "locales"), e = await p(i);
|
|
446
446
|
for (const c of e)
|
|
447
447
|
if (g(c) === ".yaml" || g(c) === ".yml") {
|
|
448
|
-
const l = h(
|
|
448
|
+
const l = h(i, c), d = await f(l, "utf-8"), u = b(d), m = c.replace(/\.(yaml|yml)$/, "");
|
|
449
449
|
a.locales[m] = u ?? {};
|
|
450
450
|
}
|
|
451
451
|
} catch {
|
|
452
452
|
}
|
|
453
453
|
try {
|
|
454
|
-
const
|
|
454
|
+
const i = h(n, "dialogues"), e = await p(i);
|
|
455
455
|
for (const c of e)
|
|
456
456
|
if (g(c) === ".dlg") {
|
|
457
|
-
const l = h(
|
|
458
|
-
a.dialogues[m.id] = m, r.set(m.id,
|
|
457
|
+
const l = h(i, c), d = await f(l, "utf-8"), u = c.replace(".dlg", ""), m = k(d, u);
|
|
458
|
+
a.dialogues[m.id] = m, r.set(m.id, y(process.cwd(), l));
|
|
459
459
|
}
|
|
460
460
|
} catch {
|
|
461
461
|
}
|
|
462
|
-
let
|
|
462
|
+
let t = null;
|
|
463
463
|
try {
|
|
464
|
-
const
|
|
465
|
-
|
|
464
|
+
const i = h(n, "game.yaml"), e = await f(i, "utf-8");
|
|
465
|
+
t = b(e);
|
|
466
466
|
} catch {
|
|
467
|
-
|
|
467
|
+
t = { id: "game", startLocation: "", startTime: { day: 1, hour: 8 }, startFlags: {}, startVariables: {}, startInventory: [] };
|
|
468
468
|
}
|
|
469
|
-
return { registry: a, fileMap: r, config:
|
|
469
|
+
return { registry: a, fileMap: r, config: t };
|
|
470
470
|
}
|
|
471
|
-
async function
|
|
472
|
-
const
|
|
473
|
-
console.log(""), console.log(
|
|
471
|
+
async function ne() {
|
|
472
|
+
const n = process.cwd(), a = h(n, "content");
|
|
473
|
+
console.log(""), console.log(s.bold.magenta("ðū Validating Doodle Engine content...")), console.log("");
|
|
474
474
|
try {
|
|
475
|
-
const { registry: r, fileMap: o } = await
|
|
476
|
-
|
|
475
|
+
const { registry: r, fileMap: o } = await te(a), t = N(r, o);
|
|
476
|
+
I(t), t.length > 0 && process.exit(1);
|
|
477
477
|
} catch (r) {
|
|
478
|
-
console.error(
|
|
478
|
+
console.error(s.red("Error loading content:"), r), process.exit(1);
|
|
479
479
|
}
|
|
480
480
|
}
|
|
481
|
-
async function
|
|
481
|
+
async function te(n) {
|
|
482
482
|
const a = {
|
|
483
483
|
locations: {},
|
|
484
484
|
characters: {},
|
|
@@ -496,33 +496,33 @@ async function ne(t) {
|
|
|
496
496
|
{ dir: "quests", key: "quests" },
|
|
497
497
|
{ dir: "journal", key: "journalEntries" }
|
|
498
498
|
];
|
|
499
|
-
for (const { dir:
|
|
500
|
-
const e = h(
|
|
499
|
+
for (const { dir: t, key: i } of o) {
|
|
500
|
+
const e = h(n, t);
|
|
501
501
|
try {
|
|
502
502
|
const c = await p(e);
|
|
503
503
|
for (const l of c)
|
|
504
504
|
if (g(l) === ".yaml" || g(l) === ".yml") {
|
|
505
|
-
const d = h(e, l), u = await f(d, "utf-8"), m =
|
|
506
|
-
m && m.id && (a[
|
|
505
|
+
const d = h(e, l), u = await f(d, "utf-8"), m = b(u);
|
|
506
|
+
m && m.id && (a[i][m.id] = m, r.set(m.id, y(process.cwd(), d)));
|
|
507
507
|
}
|
|
508
508
|
} catch {
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
try {
|
|
512
|
-
const
|
|
513
|
-
for (const e of
|
|
512
|
+
const t = h(n, "locales"), i = await p(t);
|
|
513
|
+
for (const e of i)
|
|
514
514
|
if (g(e) === ".yaml" || g(e) === ".yml") {
|
|
515
|
-
const c = h(
|
|
515
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
516
516
|
a.locales[u] = d ?? {};
|
|
517
517
|
}
|
|
518
518
|
} catch {
|
|
519
519
|
}
|
|
520
520
|
try {
|
|
521
|
-
const
|
|
522
|
-
for (const e of
|
|
521
|
+
const t = h(n, "dialogues"), i = await p(t);
|
|
522
|
+
for (const e of i)
|
|
523
523
|
if (g(e) === ".dlg") {
|
|
524
|
-
const c = h(
|
|
525
|
-
a.dialogues[u.id] = u, r.set(u.id,
|
|
524
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
525
|
+
a.dialogues[u.id] = u, r.set(u.id, y(process.cwd(), c));
|
|
526
526
|
}
|
|
527
527
|
} catch {
|
|
528
528
|
}
|
|
@@ -561,14 +561,14 @@ dist
|
|
|
561
561
|
},
|
|
562
562
|
"include": ["src"]
|
|
563
563
|
}
|
|
564
|
-
`,
|
|
564
|
+
`, ie = `id: bartender
|
|
565
565
|
name: "@character.bartender.name"
|
|
566
566
|
biography: "@character.bartender.bio"
|
|
567
567
|
portrait: ""
|
|
568
568
|
location: tavern
|
|
569
569
|
dialogue: bartender_greeting
|
|
570
570
|
stats: {}
|
|
571
|
-
`,
|
|
571
|
+
`, se = `id: merchant
|
|
572
572
|
name: "@character.merchant.name"
|
|
573
573
|
biography: "@character.merchant.bio"
|
|
574
574
|
portrait: ""
|
|
@@ -652,9 +652,9 @@ NODE order_drink
|
|
|
652
652
|
GOTO after_drink
|
|
653
653
|
END
|
|
654
654
|
|
|
655
|
-
# Try to bluff a free drink â
|
|
655
|
+
# Try to bluff a free drink â rolls dice inline (demonstrates: ROLL + GOTO within one dialogue)
|
|
656
656
|
CHOICE @bartender.choice.try_bluff
|
|
657
|
-
|
|
657
|
+
GOTO bluff_attempt
|
|
658
658
|
END
|
|
659
659
|
|
|
660
660
|
# Always available as an out
|
|
@@ -662,6 +662,41 @@ NODE order_drink
|
|
|
662
662
|
GOTO start
|
|
663
663
|
END
|
|
664
664
|
|
|
665
|
+
NODE bluff_attempt
|
|
666
|
+
# Roll happens immediately on entering this node â auto-advances to success or failure
|
|
667
|
+
ROLL bluffRoll 1 20
|
|
668
|
+
IF variableGreaterThan bluffRoll 14
|
|
669
|
+
GOTO bluff_success
|
|
670
|
+
END
|
|
671
|
+
GOTO bluff_failure
|
|
672
|
+
|
|
673
|
+
NODE bluff_success
|
|
674
|
+
BARTENDER: @bluff.success.bartender
|
|
675
|
+
ADD relationship bartender 2
|
|
676
|
+
SET flag bluffedMarcus
|
|
677
|
+
|
|
678
|
+
CHOICE @bluff.choice.cheers
|
|
679
|
+
GOTO start
|
|
680
|
+
END
|
|
681
|
+
|
|
682
|
+
NODE bluff_failure
|
|
683
|
+
BARTENDER: @bluff.failure.bartender
|
|
684
|
+
|
|
685
|
+
CHOICE @bluff.choice.fine_pay
|
|
686
|
+
REQUIRE variableGreaterThan gold 4
|
|
687
|
+
ADD variable gold -5
|
|
688
|
+
ADD variable _drinksBought 1
|
|
689
|
+
ADD variable reputation 1
|
|
690
|
+
SET flag hadDrink
|
|
691
|
+
ADD relationship bartender 1
|
|
692
|
+
NOTIFY @notification.bought_drink
|
|
693
|
+
GOTO start
|
|
694
|
+
END
|
|
695
|
+
|
|
696
|
+
CHOICE @bluff.choice.walk_away
|
|
697
|
+
GOTO start
|
|
698
|
+
END
|
|
699
|
+
|
|
665
700
|
NODE after_drink
|
|
666
701
|
BARTENDER: @bartender.after_drink
|
|
667
702
|
|
|
@@ -722,13 +757,17 @@ NODE work_done
|
|
|
722
757
|
NODE farewell
|
|
723
758
|
BARTENDER: @bartender.farewell
|
|
724
759
|
END dialogue
|
|
725
|
-
`, le = `#
|
|
760
|
+
`, le = `# Standalone skill-check demo â demonstrates dice syntax in isolation.
|
|
726
761
|
#
|
|
727
762
|
# ROLL <variable> <min> <max> â rolls a random integer and stores it in a variable.
|
|
728
763
|
# {varName} â in dialogue text, replaced with the variable's value.
|
|
729
764
|
# roll <min> <max> <threshold> â condition: rolls and returns true if result >= threshold.
|
|
730
765
|
#
|
|
731
|
-
#
|
|
766
|
+
# The actual bluff check in this scaffold is inlined into bartender_greeting.dlg
|
|
767
|
+
# so it can flow back into the bartender conversation (GOTO after_drink, GOTO start).
|
|
768
|
+
#
|
|
769
|
+
# Use START dialogue to launch a self-contained dialogue that doesn't need to
|
|
770
|
+
# return to the caller. For example, from a CHOICE in another dialogue:
|
|
732
771
|
# START dialogue bluff_check
|
|
733
772
|
|
|
734
773
|
NODE start
|
|
@@ -757,14 +796,31 @@ NODE resolve
|
|
|
757
796
|
GOTO failure
|
|
758
797
|
|
|
759
798
|
NODE success
|
|
760
|
-
NARRATOR: @bluff.success
|
|
799
|
+
NARRATOR: @bluff.success.narration
|
|
800
|
+
BARTENDER: @bluff.success.bartender
|
|
761
801
|
ADD relationship bartender 2
|
|
762
802
|
SET flag bluffedMarcus
|
|
763
|
-
|
|
803
|
+
|
|
804
|
+
CHOICE @bluff.choice.cheers
|
|
805
|
+
END dialogue
|
|
806
|
+
END
|
|
764
807
|
|
|
765
808
|
NODE failure
|
|
766
|
-
NARRATOR: @bluff.failure
|
|
767
|
-
|
|
809
|
+
NARRATOR: @bluff.failure.narration
|
|
810
|
+
BARTENDER: @bluff.failure.bartender
|
|
811
|
+
|
|
812
|
+
# Still have to pay â or just walk away
|
|
813
|
+
CHOICE @bluff.choice.fine_pay
|
|
814
|
+
REQUIRE variableGreaterThan gold 4
|
|
815
|
+
ADD variable gold -5
|
|
816
|
+
SET flag hadDrink
|
|
817
|
+
NOTIFY @notification.bought_drink
|
|
818
|
+
END dialogue
|
|
819
|
+
END
|
|
820
|
+
|
|
821
|
+
CHOICE @bluff.choice.walk_away
|
|
822
|
+
END dialogue
|
|
823
|
+
END
|
|
768
824
|
`, de = `# One-time narrator intro for the market. Same pattern as tavern_intro.dlg.
|
|
769
825
|
|
|
770
826
|
TRIGGER market
|
|
@@ -962,11 +1018,11 @@ stats: {}
|
|
|
962
1018
|
title: "@journal.market_square.title"
|
|
963
1019
|
text: "@journal.market_square.text"
|
|
964
1020
|
category: places
|
|
965
|
-
`,
|
|
1021
|
+
`, be = `id: odd_jobs_accepted
|
|
966
1022
|
title: "@journal.odd_jobs_accepted.title"
|
|
967
1023
|
text: "@journal.odd_jobs_accepted.text"
|
|
968
1024
|
category: quests
|
|
969
|
-
`,
|
|
1025
|
+
`, ye = `id: tavern_discovery
|
|
970
1026
|
title: "@journal.tavern_discovery.title"
|
|
971
1027
|
text: "@journal.tavern_discovery.text"
|
|
972
1028
|
category: places
|
|
@@ -1108,21 +1164,26 @@ bluff.choice.attempt: "Try to bluff him."
|
|
|
1108
1164
|
bluff.choice.back_down: "Actually, never mind."
|
|
1109
1165
|
bluff.backed_down: "Some fights aren't worth picking."
|
|
1110
1166
|
bluff.rolled: "You spin the tale with {bluffRoll} on your roll â and Marcus listens carefully."
|
|
1111
|
-
bluff.success: "The story lands perfectly. Marcus laughs and slides a free drink across the bar.
|
|
1112
|
-
bluff.
|
|
1113
|
-
|
|
1167
|
+
bluff.success.narration: "The story lands perfectly. Marcus laughs and slides a free drink across the bar."
|
|
1168
|
+
bluff.success.bartender: "Sure, this one's on the house."
|
|
1169
|
+
bluff.failure.narration: "Marcus raises an eyebrow, entirely unconvinced."
|
|
1170
|
+
bluff.failure.bartender: "Nope. That'll be five gold."
|
|
1171
|
+
bluff.choice.cheers: "Cheers!"
|
|
1172
|
+
bluff.choice.fine_pay: "Fine. Here's five gold."
|
|
1173
|
+
bluff.choice.walk_away: "Never mind then."
|
|
1174
|
+
`, Ee = `id: market
|
|
1114
1175
|
name: "@location.market.name"
|
|
1115
1176
|
description: "@location.market.description"
|
|
1116
1177
|
banner: ""
|
|
1117
1178
|
music: ""
|
|
1118
1179
|
ambient: ""
|
|
1119
|
-
`,
|
|
1180
|
+
`, we = `id: tavern
|
|
1120
1181
|
name: "@location.tavern.name"
|
|
1121
1182
|
description: "@location.tavern.description"
|
|
1122
1183
|
banner: ""
|
|
1123
1184
|
music: ""
|
|
1124
1185
|
ambient: ""
|
|
1125
|
-
`,
|
|
1186
|
+
`, ke = `id: town
|
|
1126
1187
|
name: "@map.town.name"
|
|
1127
1188
|
image: ""
|
|
1128
1189
|
scale: 1
|
|
@@ -1272,7 +1333,7 @@ export function App() {
|
|
|
1272
1333
|
-webkit-font-smoothing: antialiased;
|
|
1273
1334
|
-moz-osx-font-smoothing: grayscale;
|
|
1274
1335
|
}
|
|
1275
|
-
`,
|
|
1336
|
+
`, Ne = `import { StrictMode } from 'react'
|
|
1276
1337
|
import { createRoot } from 'react-dom/client'
|
|
1277
1338
|
import { App } from './App'
|
|
1278
1339
|
import './index.css'
|
|
@@ -1282,12 +1343,12 @@ createRoot(document.getElementById('root')!).render(
|
|
|
1282
1343
|
<App />
|
|
1283
1344
|
</StrictMode>,
|
|
1284
1345
|
)
|
|
1285
|
-
`, O = "ðū",
|
|
1346
|
+
`, O = "ðū", Ie = "ð", j = "ðĶī", Ce = "âĻ", G = "ð", T = "â
", Re = "ð", A = /* @__PURE__ */ Object.assign({
|
|
1286
1347
|
"./templates/_root/_gitignore": oe,
|
|
1287
1348
|
"./templates/_root/index.html": ae,
|
|
1288
1349
|
"./templates/_root/tsconfig.json": re,
|
|
1289
|
-
"./templates/content/characters/bartender.yaml":
|
|
1290
|
-
"./templates/content/characters/merchant.yaml":
|
|
1350
|
+
"./templates/content/characters/bartender.yaml": ie,
|
|
1351
|
+
"./templates/content/characters/merchant.yaml": se,
|
|
1291
1352
|
"./templates/content/dialogues/bartender_greeting.dlg": ce,
|
|
1292
1353
|
"./templates/content/dialogues/bluff_check.dlg": le,
|
|
1293
1354
|
"./templates/content/dialogues/market_intro.dlg": de,
|
|
@@ -1297,20 +1358,20 @@ createRoot(document.getElementById('root')!).render(
|
|
|
1297
1358
|
"./templates/content/interludes/chapter_one.yaml": ge,
|
|
1298
1359
|
"./templates/content/items/old_coin.yaml": fe,
|
|
1299
1360
|
"./templates/content/journal/market_square.yaml": pe,
|
|
1300
|
-
"./templates/content/journal/odd_jobs_accepted.yaml":
|
|
1301
|
-
"./templates/content/journal/tavern_discovery.yaml":
|
|
1361
|
+
"./templates/content/journal/odd_jobs_accepted.yaml": be,
|
|
1362
|
+
"./templates/content/journal/tavern_discovery.yaml": ye,
|
|
1302
1363
|
"./templates/content/locales/en.yaml": _e,
|
|
1303
|
-
"./templates/content/locations/market.yaml":
|
|
1304
|
-
"./templates/content/locations/tavern.yaml":
|
|
1305
|
-
"./templates/content/maps/town.yaml":
|
|
1364
|
+
"./templates/content/locations/market.yaml": Ee,
|
|
1365
|
+
"./templates/content/locations/tavern.yaml": we,
|
|
1366
|
+
"./templates/content/maps/town.yaml": ke,
|
|
1306
1367
|
"./templates/content/quests/odd_jobs.yaml": ve,
|
|
1307
1368
|
"./templates/src/App.custom.tsx": Oe,
|
|
1308
1369
|
"./templates/src/App.default.tsx": De,
|
|
1309
1370
|
"./templates/src/index.css": Te,
|
|
1310
|
-
"./templates/src/main.tsx":
|
|
1371
|
+
"./templates/src/main.tsx": Ne
|
|
1311
1372
|
});
|
|
1312
|
-
function Ae(
|
|
1313
|
-
const a =
|
|
1373
|
+
function Ae(n) {
|
|
1374
|
+
const a = n.replace("./templates/", "");
|
|
1314
1375
|
if (a === "src/App.default.tsx" || a === "src/App.custom.tsx") return null;
|
|
1315
1376
|
if (a.startsWith("_root/")) {
|
|
1316
1377
|
const r = a.slice(6);
|
|
@@ -1318,19 +1379,19 @@ function Ae(t) {
|
|
|
1318
1379
|
}
|
|
1319
1380
|
return a;
|
|
1320
1381
|
}
|
|
1321
|
-
async function Se(
|
|
1322
|
-
const a = h(process.cwd(),
|
|
1323
|
-
console.log(""), console.log(
|
|
1382
|
+
async function Se(n) {
|
|
1383
|
+
const a = h(process.cwd(), n);
|
|
1384
|
+
console.log(""), console.log(s.bold.magenta(` ${O} Doodle Engine ${O}`)), console.log(s.dim(" Text-based RPG and Adventure Game Scaffolder")), console.log(""), console.log(` ${Ie} Creating new game: ${s.bold.cyan(n)}`), console.log("");
|
|
1324
1385
|
const { useDefaultRenderer: r } = await F({
|
|
1325
1386
|
type: "confirm",
|
|
1326
1387
|
name: "useDefaultRenderer",
|
|
1327
1388
|
message: "Use default renderer?",
|
|
1328
1389
|
initial: !0
|
|
1329
1390
|
});
|
|
1330
|
-
r === void 0 && (console.log(
|
|
1331
|
-
${j} No worries, maybe next time! Woof!`)), process.exit(0)), console.log(""), await je(a,
|
|
1391
|
+
r === void 0 && (console.log(s.yellow(`
|
|
1392
|
+
${j} No worries, maybe next time! Woof!`)), process.exit(0)), console.log(""), await je(a, n, r), console.log(""), console.log(s.bold.green(` ${T} Project created successfully!`)), console.log(""), console.log(s.dim(` ${G} ${a}`)), console.log(""), console.log(s.bold(" Next steps:")), console.log(s.cyan(` cd ${n}`)), console.log(s.cyan(" npm install ") + s.dim("# or: yarn install / pnpm install")), console.log(s.cyan(" npm run dev ") + s.dim("# or: yarn dev / pnpm dev")), console.log(""), console.log(s.dim(` ${Re} Happy game making! ${O}`)), console.log("");
|
|
1332
1393
|
}
|
|
1333
|
-
async function je(
|
|
1394
|
+
async function je(n, a, r) {
|
|
1334
1395
|
const o = [
|
|
1335
1396
|
"content/locations",
|
|
1336
1397
|
"content/characters",
|
|
@@ -1350,11 +1411,11 @@ async function je(t, a, r) {
|
|
|
1350
1411
|
"assets/audio/voice",
|
|
1351
1412
|
"src"
|
|
1352
1413
|
];
|
|
1353
|
-
console.log(` ${
|
|
1414
|
+
console.log(` ${G} ${s.bold("Creating directories...")}`);
|
|
1354
1415
|
for (const e of o)
|
|
1355
|
-
await D(h(
|
|
1356
|
-
console.log(
|
|
1357
|
-
const
|
|
1416
|
+
await D(h(n, e), { recursive: !0 });
|
|
1417
|
+
console.log(s.green(` ${T} Directories created`)), console.log("");
|
|
1418
|
+
const t = {
|
|
1358
1419
|
name: a,
|
|
1359
1420
|
version: "0.1.0",
|
|
1360
1421
|
type: "module",
|
|
@@ -1378,20 +1439,20 @@ async function je(t, a, r) {
|
|
|
1378
1439
|
vite: "^6.0.0"
|
|
1379
1440
|
}
|
|
1380
1441
|
};
|
|
1381
|
-
console.log(` ${Ce} ${
|
|
1442
|
+
console.log(` ${Ce} ${s.bold("Writing project files...")}`), await w(h(n, "package.json"), JSON.stringify(t, null, 2));
|
|
1382
1443
|
for (const [e, c] of Object.entries(A)) {
|
|
1383
1444
|
const l = Ae(e);
|
|
1384
1445
|
if (l === null) continue;
|
|
1385
|
-
const d = h(
|
|
1386
|
-
await D(M(d), { recursive: !0 }), await
|
|
1446
|
+
const d = h(n, l);
|
|
1447
|
+
await D(M(d), { recursive: !0 }), await w(d, c);
|
|
1387
1448
|
}
|
|
1388
|
-
const
|
|
1389
|
-
await
|
|
1449
|
+
const i = r ? "./templates/src/App.default.tsx" : "./templates/src/App.custom.tsx";
|
|
1450
|
+
await w(h(n, "src/App.tsx"), A[i]), console.log(s.green(` ${T} Source files created`)), console.log(""), console.log(` ${j} ${s.bold("Starter content written")}`), console.log(""), console.log(s.dim(" Content includes:")), console.log(s.dim(" 2 locations (tavern, market)")), console.log(s.dim(" 2 characters (bartender, merchant)")), console.log(s.dim(" 1 item (old coin)")), console.log(s.dim(" 1 map (town with 2 locations)")), console.log(s.dim(" 1 quest (odd jobs, 3 stages)")), console.log(s.dim(" 3 journal entries")), console.log(s.dim(" 1 interlude (chapter one, auto-triggers at tavern)")), console.log(s.dim(" 5 dialogues (2 narrator intros, 2 NPC conversations, 1 skill check)")), console.log(s.dim(" English locale with all strings"));
|
|
1390
1451
|
}
|
|
1391
|
-
const _ = new
|
|
1392
|
-
_.name("doodle").description(
|
|
1393
|
-
_.command("create <project-name>").description("Scaffold a new Doodle Engine game project").action(async (
|
|
1394
|
-
await Se(
|
|
1452
|
+
const _ = new x();
|
|
1453
|
+
_.name("doodle").description(s.magenta("ðū Doodle Engine") + s.dim(" â Narrative RPG development tools")).version("0.0.1");
|
|
1454
|
+
_.command("create <project-name>").description("Scaffold a new Doodle Engine game project").action(async (n) => {
|
|
1455
|
+
await Se(n);
|
|
1395
1456
|
});
|
|
1396
1457
|
_.command("dev").description("Start development server with hot reload").action(async () => {
|
|
1397
1458
|
await V();
|
|
@@ -1400,6 +1461,6 @@ _.command("build").description("Build game for production").action(async () => {
|
|
|
1400
1461
|
await Z();
|
|
1401
1462
|
});
|
|
1402
1463
|
_.command("validate").description("Validate game content").action(async () => {
|
|
1403
|
-
await
|
|
1464
|
+
await ne();
|
|
1404
1465
|
});
|
|
1405
1466
|
_.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doodle-engine/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI tools for Doodle Engine game development",
|
|
6
6
|
"bin": {
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"test": "echo no tests"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@doodle-engine/core": "0.0.
|
|
19
|
-
"@doodle-engine/react": "0.0.
|
|
18
|
+
"@doodle-engine/core": "0.0.17",
|
|
19
|
+
"@doodle-engine/react": "0.0.17",
|
|
20
20
|
"@vitejs/plugin-react": "^4.3.0",
|
|
21
21
|
"chokidar": "^4.0.0",
|
|
22
22
|
"commander": "^12.0.0",
|