@doodle-engine/cli 0.0.15 â 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 +18 -0
- package/dist/cli.js +309 -244
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
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
|
|
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,137 +123,141 @@ 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
|
-
const
|
|
173
|
+
const u = a.get(c) || `${l}:${c}`;
|
|
174
174
|
r.push({
|
|
175
|
-
file:
|
|
175
|
+
file: u,
|
|
176
176
|
message: `Localization key "${e}" not found in any locale file`,
|
|
177
177
|
suggestion: `Add "${d}: ..." to your locale files`
|
|
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
|
-
const
|
|
225
|
-
|
|
226
|
-
} catch (
|
|
227
|
-
console.error(
|
|
224
|
+
const m = await K(a);
|
|
225
|
+
u.setHeader("Content-Type", "application/json"), u.end(JSON.stringify(m));
|
|
226
|
+
} catch (m) {
|
|
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
|
-
let e =
|
|
235
|
-
|
|
236
|
-
e
|
|
237
|
-
|
|
234
|
+
let e = !1;
|
|
235
|
+
i.on("ready", () => {
|
|
236
|
+
e = !0;
|
|
237
|
+
});
|
|
238
|
+
let c = null;
|
|
239
|
+
const l = (d) => {
|
|
240
|
+
c && clearTimeout(c), c = setTimeout(async () => {
|
|
241
|
+
c = null, console.log(d), await z(a), t.ws.send({ type: "full-reload", path: "*" });
|
|
238
242
|
}, 50);
|
|
239
243
|
};
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}),
|
|
243
|
-
|
|
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}`));
|
|
244
248
|
});
|
|
245
249
|
}
|
|
246
|
-
}, o = await
|
|
247
|
-
root:
|
|
250
|
+
}, o = await q({
|
|
251
|
+
root: n,
|
|
248
252
|
plugins: [S(), r],
|
|
249
253
|
server: {
|
|
250
254
|
port: 3e3,
|
|
251
255
|
open: !0
|
|
252
256
|
}
|
|
253
257
|
});
|
|
254
|
-
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("");
|
|
255
259
|
}
|
|
256
|
-
async function K(
|
|
260
|
+
async function K(n) {
|
|
257
261
|
const a = {
|
|
258
262
|
locations: {},
|
|
259
263
|
characters: {},
|
|
@@ -275,41 +279,41 @@ async function K(t) {
|
|
|
275
279
|
{ dir: "journal", key: "journalEntries" },
|
|
276
280
|
{ dir: "interludes", key: "interludes" }
|
|
277
281
|
];
|
|
278
|
-
for (const { dir:
|
|
279
|
-
const e =
|
|
282
|
+
for (const { dir: t, key: i } of o) {
|
|
283
|
+
const e = h(n, t);
|
|
280
284
|
try {
|
|
281
285
|
const c = await p(e);
|
|
282
286
|
for (const l of c)
|
|
283
287
|
if (g(l) === ".yaml" || g(l) === ".yml") {
|
|
284
|
-
const d =
|
|
285
|
-
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);
|
|
286
290
|
}
|
|
287
291
|
} catch {
|
|
288
292
|
}
|
|
289
293
|
}
|
|
290
294
|
try {
|
|
291
|
-
const
|
|
292
|
-
for (const e of
|
|
295
|
+
const t = h(n, "locales"), i = await p(t);
|
|
296
|
+
for (const e of i)
|
|
293
297
|
if (g(e) === ".yaml" || g(e) === ".yml") {
|
|
294
|
-
const c =
|
|
295
|
-
a.locales[
|
|
298
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
299
|
+
a.locales[u] = d ?? {};
|
|
296
300
|
}
|
|
297
301
|
} catch {
|
|
298
302
|
}
|
|
299
303
|
try {
|
|
300
|
-
const
|
|
301
|
-
for (const e of
|
|
304
|
+
const t = h(n, "dialogues"), i = await p(t);
|
|
305
|
+
for (const e of i)
|
|
302
306
|
if (g(e) === ".dlg") {
|
|
303
|
-
const c =
|
|
304
|
-
a.dialogues[
|
|
307
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
308
|
+
a.dialogues[u.id] = u;
|
|
305
309
|
}
|
|
306
310
|
} catch {
|
|
307
311
|
}
|
|
308
312
|
try {
|
|
309
|
-
const
|
|
310
|
-
r =
|
|
313
|
+
const t = h(n, "game.yaml"), i = await f(t, "utf-8");
|
|
314
|
+
r = b(i);
|
|
311
315
|
} catch {
|
|
312
|
-
console.warn(
|
|
316
|
+
console.warn(s.yellow(" No game.yaml found, using defaults")), r = {
|
|
313
317
|
startLocation: "tavern",
|
|
314
318
|
startTime: { day: 1, hour: 8 },
|
|
315
319
|
startFlags: {},
|
|
@@ -319,15 +323,15 @@ async function K(t) {
|
|
|
319
323
|
}
|
|
320
324
|
return { registry: a, config: r };
|
|
321
325
|
}
|
|
322
|
-
async function z(
|
|
326
|
+
async function z(n) {
|
|
323
327
|
try {
|
|
324
|
-
const { registry: a, fileMap: r } = await X(
|
|
325
|
-
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(""));
|
|
326
330
|
} catch (a) {
|
|
327
|
-
console.error(
|
|
331
|
+
console.error(s.red(" Error running validation:"), a);
|
|
328
332
|
}
|
|
329
333
|
}
|
|
330
|
-
async function X(
|
|
334
|
+
async function X(n) {
|
|
331
335
|
const a = {
|
|
332
336
|
locations: {},
|
|
333
337
|
characters: {},
|
|
@@ -347,65 +351,65 @@ async function X(t) {
|
|
|
347
351
|
{ dir: "journal", key: "journalEntries" },
|
|
348
352
|
{ dir: "interludes", key: "interludes" }
|
|
349
353
|
];
|
|
350
|
-
for (const { dir:
|
|
351
|
-
const e =
|
|
354
|
+
for (const { dir: t, key: i } of o) {
|
|
355
|
+
const e = h(n, t);
|
|
352
356
|
try {
|
|
353
357
|
const c = await p(e);
|
|
354
358
|
for (const l of c)
|
|
355
359
|
if (g(l) === ".yaml" || g(l) === ".yml") {
|
|
356
|
-
const d =
|
|
357
|
-
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)));
|
|
358
362
|
}
|
|
359
363
|
} catch {
|
|
360
364
|
}
|
|
361
365
|
}
|
|
362
366
|
try {
|
|
363
|
-
const
|
|
364
|
-
for (const e of
|
|
367
|
+
const t = h(n, "locales"), i = await p(t);
|
|
368
|
+
for (const e of i)
|
|
365
369
|
if (g(e) === ".yaml" || g(e) === ".yml") {
|
|
366
|
-
const c =
|
|
367
|
-
a.locales[
|
|
370
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
371
|
+
a.locales[u] = d ?? {};
|
|
368
372
|
}
|
|
369
373
|
} catch {
|
|
370
374
|
}
|
|
371
375
|
try {
|
|
372
|
-
const
|
|
373
|
-
for (const e of
|
|
376
|
+
const t = h(n, "dialogues"), i = await p(t);
|
|
377
|
+
for (const e of i)
|
|
374
378
|
if (g(e) === ".dlg") {
|
|
375
|
-
const c =
|
|
376
|
-
a.dialogues[
|
|
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));
|
|
377
381
|
}
|
|
378
382
|
} catch {
|
|
379
383
|
}
|
|
380
384
|
return { registry: a, fileMap: r };
|
|
381
385
|
}
|
|
382
386
|
async function Z() {
|
|
383
|
-
const
|
|
384
|
-
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..."));
|
|
385
389
|
let r;
|
|
386
390
|
try {
|
|
387
|
-
const { registry: o, fileMap:
|
|
388
|
-
|
|
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 };
|
|
389
393
|
} catch (o) {
|
|
390
|
-
console.error(
|
|
394
|
+
console.error(s.red("Error loading content:"), o), process.exit(1);
|
|
391
395
|
}
|
|
392
396
|
console.log("");
|
|
393
397
|
try {
|
|
394
398
|
await $({
|
|
395
|
-
root:
|
|
399
|
+
root: n,
|
|
396
400
|
plugins: [S()],
|
|
397
401
|
build: {
|
|
398
402
|
outDir: "dist",
|
|
399
403
|
emptyOutDir: !0
|
|
400
404
|
}
|
|
401
405
|
});
|
|
402
|
-
const o =
|
|
403
|
-
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("");
|
|
404
408
|
} catch (o) {
|
|
405
|
-
console.error(
|
|
409
|
+
console.error(s.red("Build failed:"), o), process.exit(1);
|
|
406
410
|
}
|
|
407
411
|
}
|
|
408
|
-
async function ee(
|
|
412
|
+
async function ee(n) {
|
|
409
413
|
const a = {
|
|
410
414
|
locations: {},
|
|
411
415
|
characters: {},
|
|
@@ -425,56 +429,56 @@ async function ee(t) {
|
|
|
425
429
|
{ dir: "journal", key: "journalEntries" },
|
|
426
430
|
{ dir: "interludes", key: "interludes" }
|
|
427
431
|
];
|
|
428
|
-
for (const { dir:
|
|
429
|
-
const c =
|
|
432
|
+
for (const { dir: i, key: e } of o) {
|
|
433
|
+
const c = h(n, i);
|
|
430
434
|
try {
|
|
431
435
|
const l = await p(c);
|
|
432
436
|
for (const d of l)
|
|
433
437
|
if (g(d) === ".yaml" || g(d) === ".yml") {
|
|
434
|
-
const
|
|
435
|
-
|
|
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)));
|
|
436
440
|
}
|
|
437
441
|
} catch {
|
|
438
442
|
}
|
|
439
443
|
}
|
|
440
444
|
try {
|
|
441
|
-
const
|
|
445
|
+
const i = h(n, "locales"), e = await p(i);
|
|
442
446
|
for (const c of e)
|
|
443
447
|
if (g(c) === ".yaml" || g(c) === ".yml") {
|
|
444
|
-
const l =
|
|
445
|
-
a.locales[m] =
|
|
448
|
+
const l = h(i, c), d = await f(l, "utf-8"), u = b(d), m = c.replace(/\.(yaml|yml)$/, "");
|
|
449
|
+
a.locales[m] = u ?? {};
|
|
446
450
|
}
|
|
447
451
|
} catch {
|
|
448
452
|
}
|
|
449
453
|
try {
|
|
450
|
-
const
|
|
454
|
+
const i = h(n, "dialogues"), e = await p(i);
|
|
451
455
|
for (const c of e)
|
|
452
456
|
if (g(c) === ".dlg") {
|
|
453
|
-
const l =
|
|
454
|
-
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));
|
|
455
459
|
}
|
|
456
460
|
} catch {
|
|
457
461
|
}
|
|
458
|
-
let
|
|
462
|
+
let t = null;
|
|
459
463
|
try {
|
|
460
|
-
const
|
|
461
|
-
|
|
464
|
+
const i = h(n, "game.yaml"), e = await f(i, "utf-8");
|
|
465
|
+
t = b(e);
|
|
462
466
|
} catch {
|
|
463
|
-
|
|
467
|
+
t = { id: "game", startLocation: "", startTime: { day: 1, hour: 8 }, startFlags: {}, startVariables: {}, startInventory: [] };
|
|
464
468
|
}
|
|
465
|
-
return { registry: a, fileMap: r, config:
|
|
469
|
+
return { registry: a, fileMap: r, config: t };
|
|
466
470
|
}
|
|
467
|
-
async function
|
|
468
|
-
const
|
|
469
|
-
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("");
|
|
470
474
|
try {
|
|
471
|
-
const { registry: r, fileMap: o } = await
|
|
472
|
-
|
|
475
|
+
const { registry: r, fileMap: o } = await te(a), t = N(r, o);
|
|
476
|
+
I(t), t.length > 0 && process.exit(1);
|
|
473
477
|
} catch (r) {
|
|
474
|
-
console.error(
|
|
478
|
+
console.error(s.red("Error loading content:"), r), process.exit(1);
|
|
475
479
|
}
|
|
476
480
|
}
|
|
477
|
-
async function
|
|
481
|
+
async function te(n) {
|
|
478
482
|
const a = {
|
|
479
483
|
locations: {},
|
|
480
484
|
characters: {},
|
|
@@ -492,33 +496,33 @@ async function ne(t) {
|
|
|
492
496
|
{ dir: "quests", key: "quests" },
|
|
493
497
|
{ dir: "journal", key: "journalEntries" }
|
|
494
498
|
];
|
|
495
|
-
for (const { dir:
|
|
496
|
-
const e =
|
|
499
|
+
for (const { dir: t, key: i } of o) {
|
|
500
|
+
const e = h(n, t);
|
|
497
501
|
try {
|
|
498
502
|
const c = await p(e);
|
|
499
503
|
for (const l of c)
|
|
500
504
|
if (g(l) === ".yaml" || g(l) === ".yml") {
|
|
501
|
-
const d =
|
|
502
|
-
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)));
|
|
503
507
|
}
|
|
504
508
|
} catch {
|
|
505
509
|
}
|
|
506
510
|
}
|
|
507
511
|
try {
|
|
508
|
-
const
|
|
509
|
-
for (const e of
|
|
512
|
+
const t = h(n, "locales"), i = await p(t);
|
|
513
|
+
for (const e of i)
|
|
510
514
|
if (g(e) === ".yaml" || g(e) === ".yml") {
|
|
511
|
-
const c =
|
|
512
|
-
a.locales[
|
|
515
|
+
const c = h(t, e), l = await f(c, "utf-8"), d = b(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
516
|
+
a.locales[u] = d ?? {};
|
|
513
517
|
}
|
|
514
518
|
} catch {
|
|
515
519
|
}
|
|
516
520
|
try {
|
|
517
|
-
const
|
|
518
|
-
for (const e of
|
|
521
|
+
const t = h(n, "dialogues"), i = await p(t);
|
|
522
|
+
for (const e of i)
|
|
519
523
|
if (g(e) === ".dlg") {
|
|
520
|
-
const c =
|
|
521
|
-
a.dialogues[
|
|
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));
|
|
522
526
|
}
|
|
523
527
|
} catch {
|
|
524
528
|
}
|
|
@@ -557,14 +561,14 @@ dist
|
|
|
557
561
|
},
|
|
558
562
|
"include": ["src"]
|
|
559
563
|
}
|
|
560
|
-
`,
|
|
564
|
+
`, ie = `id: bartender
|
|
561
565
|
name: "@character.bartender.name"
|
|
562
566
|
biography: "@character.bartender.bio"
|
|
563
567
|
portrait: ""
|
|
564
568
|
location: tavern
|
|
565
569
|
dialogue: bartender_greeting
|
|
566
570
|
stats: {}
|
|
567
|
-
`,
|
|
571
|
+
`, se = `id: merchant
|
|
568
572
|
name: "@character.merchant.name"
|
|
569
573
|
biography: "@character.merchant.bio"
|
|
570
574
|
portrait: ""
|
|
@@ -648,9 +652,9 @@ NODE order_drink
|
|
|
648
652
|
GOTO after_drink
|
|
649
653
|
END
|
|
650
654
|
|
|
651
|
-
# Try to bluff a free drink â
|
|
655
|
+
# Try to bluff a free drink â rolls dice inline (demonstrates: ROLL + GOTO within one dialogue)
|
|
652
656
|
CHOICE @bartender.choice.try_bluff
|
|
653
|
-
|
|
657
|
+
GOTO bluff_attempt
|
|
654
658
|
END
|
|
655
659
|
|
|
656
660
|
# Always available as an out
|
|
@@ -658,6 +662,41 @@ NODE order_drink
|
|
|
658
662
|
GOTO start
|
|
659
663
|
END
|
|
660
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
|
+
|
|
661
700
|
NODE after_drink
|
|
662
701
|
BARTENDER: @bartender.after_drink
|
|
663
702
|
|
|
@@ -718,13 +757,17 @@ NODE work_done
|
|
|
718
757
|
NODE farewell
|
|
719
758
|
BARTENDER: @bartender.farewell
|
|
720
759
|
END dialogue
|
|
721
|
-
`, le = `#
|
|
760
|
+
`, le = `# Standalone skill-check demo â demonstrates dice syntax in isolation.
|
|
722
761
|
#
|
|
723
762
|
# ROLL <variable> <min> <max> â rolls a random integer and stores it in a variable.
|
|
724
763
|
# {varName} â in dialogue text, replaced with the variable's value.
|
|
725
764
|
# roll <min> <max> <threshold> â condition: rolls and returns true if result >= threshold.
|
|
726
765
|
#
|
|
727
|
-
#
|
|
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:
|
|
728
771
|
# START dialogue bluff_check
|
|
729
772
|
|
|
730
773
|
NODE start
|
|
@@ -753,14 +796,31 @@ NODE resolve
|
|
|
753
796
|
GOTO failure
|
|
754
797
|
|
|
755
798
|
NODE success
|
|
756
|
-
NARRATOR: @bluff.success
|
|
799
|
+
NARRATOR: @bluff.success.narration
|
|
800
|
+
BARTENDER: @bluff.success.bartender
|
|
757
801
|
ADD relationship bartender 2
|
|
758
802
|
SET flag bluffedMarcus
|
|
759
|
-
|
|
803
|
+
|
|
804
|
+
CHOICE @bluff.choice.cheers
|
|
805
|
+
END dialogue
|
|
806
|
+
END
|
|
760
807
|
|
|
761
808
|
NODE failure
|
|
762
|
-
NARRATOR: @bluff.failure
|
|
763
|
-
|
|
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
|
|
764
824
|
`, de = `# One-time narrator intro for the market. Same pattern as tavern_intro.dlg.
|
|
765
825
|
|
|
766
826
|
TRIGGER market
|
|
@@ -958,11 +1018,11 @@ stats: {}
|
|
|
958
1018
|
title: "@journal.market_square.title"
|
|
959
1019
|
text: "@journal.market_square.text"
|
|
960
1020
|
category: places
|
|
961
|
-
`,
|
|
1021
|
+
`, be = `id: odd_jobs_accepted
|
|
962
1022
|
title: "@journal.odd_jobs_accepted.title"
|
|
963
1023
|
text: "@journal.odd_jobs_accepted.text"
|
|
964
1024
|
category: quests
|
|
965
|
-
`,
|
|
1025
|
+
`, ye = `id: tavern_discovery
|
|
966
1026
|
title: "@journal.tavern_discovery.title"
|
|
967
1027
|
text: "@journal.tavern_discovery.text"
|
|
968
1028
|
category: places
|
|
@@ -1104,21 +1164,26 @@ bluff.choice.attempt: "Try to bluff him."
|
|
|
1104
1164
|
bluff.choice.back_down: "Actually, never mind."
|
|
1105
1165
|
bluff.backed_down: "Some fights aren't worth picking."
|
|
1106
1166
|
bluff.rolled: "You spin the tale with {bluffRoll} on your roll â and Marcus listens carefully."
|
|
1107
|
-
bluff.success: "The story lands perfectly. Marcus laughs and slides a free drink across the bar.
|
|
1108
|
-
bluff.
|
|
1109
|
-
|
|
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
|
|
1110
1175
|
name: "@location.market.name"
|
|
1111
1176
|
description: "@location.market.description"
|
|
1112
1177
|
banner: ""
|
|
1113
1178
|
music: ""
|
|
1114
1179
|
ambient: ""
|
|
1115
|
-
`,
|
|
1180
|
+
`, we = `id: tavern
|
|
1116
1181
|
name: "@location.tavern.name"
|
|
1117
1182
|
description: "@location.tavern.description"
|
|
1118
1183
|
banner: ""
|
|
1119
1184
|
music: ""
|
|
1120
1185
|
ambient: ""
|
|
1121
|
-
`,
|
|
1186
|
+
`, ke = `id: town
|
|
1122
1187
|
name: "@map.town.name"
|
|
1123
1188
|
image: ""
|
|
1124
1189
|
scale: 1
|
|
@@ -1268,7 +1333,7 @@ export function App() {
|
|
|
1268
1333
|
-webkit-font-smoothing: antialiased;
|
|
1269
1334
|
-moz-osx-font-smoothing: grayscale;
|
|
1270
1335
|
}
|
|
1271
|
-
`,
|
|
1336
|
+
`, Ne = `import { StrictMode } from 'react'
|
|
1272
1337
|
import { createRoot } from 'react-dom/client'
|
|
1273
1338
|
import { App } from './App'
|
|
1274
1339
|
import './index.css'
|
|
@@ -1278,12 +1343,12 @@ createRoot(document.getElementById('root')!).render(
|
|
|
1278
1343
|
<App />
|
|
1279
1344
|
</StrictMode>,
|
|
1280
1345
|
)
|
|
1281
|
-
`, O = "ðū",
|
|
1346
|
+
`, O = "ðū", Ie = "ð", j = "ðĶī", Ce = "âĻ", G = "ð", T = "â
", Re = "ð", A = /* @__PURE__ */ Object.assign({
|
|
1282
1347
|
"./templates/_root/_gitignore": oe,
|
|
1283
1348
|
"./templates/_root/index.html": ae,
|
|
1284
1349
|
"./templates/_root/tsconfig.json": re,
|
|
1285
|
-
"./templates/content/characters/bartender.yaml":
|
|
1286
|
-
"./templates/content/characters/merchant.yaml":
|
|
1350
|
+
"./templates/content/characters/bartender.yaml": ie,
|
|
1351
|
+
"./templates/content/characters/merchant.yaml": se,
|
|
1287
1352
|
"./templates/content/dialogues/bartender_greeting.dlg": ce,
|
|
1288
1353
|
"./templates/content/dialogues/bluff_check.dlg": le,
|
|
1289
1354
|
"./templates/content/dialogues/market_intro.dlg": de,
|
|
@@ -1293,20 +1358,20 @@ createRoot(document.getElementById('root')!).render(
|
|
|
1293
1358
|
"./templates/content/interludes/chapter_one.yaml": ge,
|
|
1294
1359
|
"./templates/content/items/old_coin.yaml": fe,
|
|
1295
1360
|
"./templates/content/journal/market_square.yaml": pe,
|
|
1296
|
-
"./templates/content/journal/odd_jobs_accepted.yaml":
|
|
1297
|
-
"./templates/content/journal/tavern_discovery.yaml":
|
|
1361
|
+
"./templates/content/journal/odd_jobs_accepted.yaml": be,
|
|
1362
|
+
"./templates/content/journal/tavern_discovery.yaml": ye,
|
|
1298
1363
|
"./templates/content/locales/en.yaml": _e,
|
|
1299
|
-
"./templates/content/locations/market.yaml":
|
|
1300
|
-
"./templates/content/locations/tavern.yaml":
|
|
1301
|
-
"./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,
|
|
1302
1367
|
"./templates/content/quests/odd_jobs.yaml": ve,
|
|
1303
1368
|
"./templates/src/App.custom.tsx": Oe,
|
|
1304
1369
|
"./templates/src/App.default.tsx": De,
|
|
1305
1370
|
"./templates/src/index.css": Te,
|
|
1306
|
-
"./templates/src/main.tsx":
|
|
1371
|
+
"./templates/src/main.tsx": Ne
|
|
1307
1372
|
});
|
|
1308
|
-
function Ae(
|
|
1309
|
-
const a =
|
|
1373
|
+
function Ae(n) {
|
|
1374
|
+
const a = n.replace("./templates/", "");
|
|
1310
1375
|
if (a === "src/App.default.tsx" || a === "src/App.custom.tsx") return null;
|
|
1311
1376
|
if (a.startsWith("_root/")) {
|
|
1312
1377
|
const r = a.slice(6);
|
|
@@ -1314,19 +1379,19 @@ function Ae(t) {
|
|
|
1314
1379
|
}
|
|
1315
1380
|
return a;
|
|
1316
1381
|
}
|
|
1317
|
-
async function Se(
|
|
1318
|
-
const a =
|
|
1319
|
-
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("");
|
|
1320
1385
|
const { useDefaultRenderer: r } = await F({
|
|
1321
1386
|
type: "confirm",
|
|
1322
1387
|
name: "useDefaultRenderer",
|
|
1323
1388
|
message: "Use default renderer?",
|
|
1324
1389
|
initial: !0
|
|
1325
1390
|
});
|
|
1326
|
-
r === void 0 && (console.log(
|
|
1327
|
-
${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("");
|
|
1328
1393
|
}
|
|
1329
|
-
async function je(
|
|
1394
|
+
async function je(n, a, r) {
|
|
1330
1395
|
const o = [
|
|
1331
1396
|
"content/locations",
|
|
1332
1397
|
"content/characters",
|
|
@@ -1346,11 +1411,11 @@ async function je(t, a, r) {
|
|
|
1346
1411
|
"assets/audio/voice",
|
|
1347
1412
|
"src"
|
|
1348
1413
|
];
|
|
1349
|
-
console.log(` ${
|
|
1414
|
+
console.log(` ${G} ${s.bold("Creating directories...")}`);
|
|
1350
1415
|
for (const e of o)
|
|
1351
|
-
await D(
|
|
1352
|
-
console.log(
|
|
1353
|
-
const
|
|
1416
|
+
await D(h(n, e), { recursive: !0 });
|
|
1417
|
+
console.log(s.green(` ${T} Directories created`)), console.log("");
|
|
1418
|
+
const t = {
|
|
1354
1419
|
name: a,
|
|
1355
1420
|
version: "0.1.0",
|
|
1356
1421
|
type: "module",
|
|
@@ -1374,20 +1439,20 @@ async function je(t, a, r) {
|
|
|
1374
1439
|
vite: "^6.0.0"
|
|
1375
1440
|
}
|
|
1376
1441
|
};
|
|
1377
|
-
console.log(` ${Ce} ${
|
|
1442
|
+
console.log(` ${Ce} ${s.bold("Writing project files...")}`), await w(h(n, "package.json"), JSON.stringify(t, null, 2));
|
|
1378
1443
|
for (const [e, c] of Object.entries(A)) {
|
|
1379
1444
|
const l = Ae(e);
|
|
1380
1445
|
if (l === null) continue;
|
|
1381
|
-
const d =
|
|
1382
|
-
await D(M(d), { recursive: !0 }), await
|
|
1446
|
+
const d = h(n, l);
|
|
1447
|
+
await D(M(d), { recursive: !0 }), await w(d, c);
|
|
1383
1448
|
}
|
|
1384
|
-
const
|
|
1385
|
-
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"));
|
|
1386
1451
|
}
|
|
1387
|
-
const _ = new
|
|
1388
|
-
_.name("doodle").description(
|
|
1389
|
-
_.command("create <project-name>").description("Scaffold a new Doodle Engine game project").action(async (
|
|
1390
|
-
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);
|
|
1391
1456
|
});
|
|
1392
1457
|
_.command("dev").description("Start development server with hot reload").action(async () => {
|
|
1393
1458
|
await V();
|
|
@@ -1396,6 +1461,6 @@ _.command("build").description("Build game for production").action(async () => {
|
|
|
1396
1461
|
await Z();
|
|
1397
1462
|
});
|
|
1398
1463
|
_.command("validate").description("Validate game content").action(async () => {
|
|
1399
|
-
await
|
|
1464
|
+
await ne();
|
|
1400
1465
|
});
|
|
1401
1466
|
_.parse();
|