@doodle-engine/cli 0.0.19 â 0.0.21
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 +325 -265
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/create.d.ts.map +1 -1
- package/dist/manifest.d.ts +3 -3
- package/dist/manifest.d.ts.map +1 -1
- package/dist/service-worker.d.ts +1 -1
- package/dist/service-worker.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/validate.d.ts +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -9,73 +9,73 @@ import { join as h, extname as f, relative as E, dirname as U } from "path";
|
|
|
9
9
|
import { parse as y } from "yaml";
|
|
10
10
|
import { extractAssetPaths as W, getAssetType as R, parseDialogue as k } from "@doodle-engine/core";
|
|
11
11
|
import J from "prompts";
|
|
12
|
-
function C(
|
|
12
|
+
function C(n, o) {
|
|
13
13
|
const s = [];
|
|
14
|
-
for (const r of Object.values(
|
|
15
|
-
const
|
|
16
|
-
s.push(...Q(r,
|
|
14
|
+
for (const r of Object.values(n.dialogues)) {
|
|
15
|
+
const t = o.get(r.id) || `dialogue:${r.id}`;
|
|
16
|
+
s.push(...Q(r, t));
|
|
17
17
|
}
|
|
18
|
-
for (const r of Object.values(
|
|
19
|
-
if (r.dialogue && !
|
|
20
|
-
const
|
|
18
|
+
for (const r of Object.values(n.characters))
|
|
19
|
+
if (r.dialogue && !n.dialogues[r.dialogue]) {
|
|
20
|
+
const t = o.get(r.id) || `character:${r.id}`;
|
|
21
21
|
s.push({
|
|
22
|
-
file:
|
|
22
|
+
file: t,
|
|
23
23
|
message: `Character "${r.id}" references non-existent dialogue "${r.dialogue}"`,
|
|
24
24
|
suggestion: `Create dialogue "${r.dialogue}" or fix the reference`
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
return s.push(...K(
|
|
27
|
+
return s.push(...K(n, o)), s;
|
|
28
28
|
}
|
|
29
|
-
function Q(
|
|
29
|
+
function Q(n, o) {
|
|
30
30
|
const s = [], r = /* @__PURE__ */ new Set();
|
|
31
|
-
for (const
|
|
32
|
-
r.has(
|
|
31
|
+
for (const t of n.nodes)
|
|
32
|
+
r.has(t.id) && s.push({
|
|
33
33
|
file: o,
|
|
34
|
-
message: `Duplicate node ID "${
|
|
34
|
+
message: `Duplicate node ID "${t.id}"`,
|
|
35
35
|
suggestion: "Node IDs must be unique within a dialogue"
|
|
36
|
-
}), r.add(
|
|
37
|
-
r.has(
|
|
36
|
+
}), r.add(t.id);
|
|
37
|
+
r.has(n.startNode) || s.push({
|
|
38
38
|
file: o,
|
|
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
|
-
s.push(...Y(
|
|
42
|
+
for (const t of n.nodes)
|
|
43
|
+
s.push(...Y(t, r, o));
|
|
44
44
|
return s;
|
|
45
45
|
}
|
|
46
|
-
function Y(
|
|
46
|
+
function Y(n, o, s) {
|
|
47
47
|
const r = [];
|
|
48
|
-
if (
|
|
48
|
+
if (n.next && !o.has(n.next) && r.push({
|
|
49
49
|
file: s,
|
|
50
|
-
message: `Node "${
|
|
51
|
-
suggestion: `Add NODE ${
|
|
52
|
-
}),
|
|
53
|
-
for (const
|
|
54
|
-
o.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
|
+
o.has(t.next) || r.push({
|
|
55
55
|
file: s,
|
|
56
|
-
message: `Node "${
|
|
57
|
-
suggestion: `Add NODE ${
|
|
58
|
-
}), r.push(...O(
|
|
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
|
+
}), r.push(...O(t.condition, n.id, s));
|
|
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 && !o.has(t.next) && r.push({
|
|
63
63
|
file: s,
|
|
64
|
-
message: `Node "${
|
|
65
|
-
suggestion: `Add NODE ${
|
|
66
|
-
}),
|
|
67
|
-
for (const e of
|
|
68
|
-
r.push(...O(e,
|
|
69
|
-
if (
|
|
70
|
-
for (const e of
|
|
71
|
-
r.push(...A(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
|
+
r.push(...O(e, n.id, s));
|
|
69
|
+
if (t.effects)
|
|
70
|
+
for (const e of t.effects)
|
|
71
|
+
r.push(...A(e, n.id, s));
|
|
72
72
|
}
|
|
73
|
-
if (
|
|
74
|
-
for (const
|
|
75
|
-
r.push(...O(
|
|
76
|
-
if (
|
|
77
|
-
for (const
|
|
78
|
-
r.push(...A(
|
|
73
|
+
if (n.conditions)
|
|
74
|
+
for (const t of n.conditions)
|
|
75
|
+
r.push(...O(t, n.id, s));
|
|
76
|
+
if (n.effects)
|
|
77
|
+
for (const t of n.effects)
|
|
78
|
+
r.push(...A(t, n.id, s));
|
|
79
79
|
return r;
|
|
80
80
|
}
|
|
81
81
|
const z = {
|
|
@@ -123,51 +123,51 @@ const z = {
|
|
|
123
123
|
showInterlude: ["interludeId"],
|
|
124
124
|
roll: ["variable", "min", "max"]
|
|
125
125
|
};
|
|
126
|
-
function O(
|
|
126
|
+
function O(n, o, s) {
|
|
127
127
|
const r = [];
|
|
128
|
-
if (!
|
|
128
|
+
if (!n.type)
|
|
129
129
|
return r.push({
|
|
130
130
|
file: s,
|
|
131
131
|
message: `Node "${o}" has condition with missing type`
|
|
132
132
|
}), r;
|
|
133
|
-
if (
|
|
134
|
-
return
|
|
133
|
+
if (n.type === "timeIs")
|
|
134
|
+
return n.hour === void 0 && n.day === void 0 && r.push({
|
|
135
135
|
file: s,
|
|
136
136
|
message: `Node "${o}" condition "timeIs" must have at least one of "hour" or "day" argument`
|
|
137
137
|
}), r;
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
138
|
+
const t = z[n.type];
|
|
139
|
+
if (!t)
|
|
140
140
|
return r;
|
|
141
|
-
for (const a of
|
|
142
|
-
(
|
|
141
|
+
for (const a of t)
|
|
142
|
+
(n[a] === void 0 || n[a] === null || n[a] === "") && r.push({
|
|
143
143
|
file: s,
|
|
144
|
-
message: `Node "${o}" condition "${
|
|
144
|
+
message: `Node "${o}" condition "${n.type}" missing required "${a}" argument`
|
|
145
145
|
});
|
|
146
146
|
return r;
|
|
147
147
|
}
|
|
148
|
-
function A(
|
|
148
|
+
function A(n, o, s) {
|
|
149
149
|
const r = [];
|
|
150
|
-
if (!
|
|
150
|
+
if (!n.type)
|
|
151
151
|
return r.push({
|
|
152
152
|
file: s,
|
|
153
153
|
message: `Node "${o}" has effect with missing type`
|
|
154
154
|
}), r;
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
155
|
+
const t = V[n.type];
|
|
156
|
+
if (!t)
|
|
157
157
|
return r;
|
|
158
|
-
for (const a of
|
|
159
|
-
(
|
|
158
|
+
for (const a of t)
|
|
159
|
+
(n[a] === void 0 || n[a] === null || n[a] === "") && r.push({
|
|
160
160
|
file: s,
|
|
161
|
-
message: `Node "${o}" effect "${
|
|
161
|
+
message: `Node "${o}" effect "${n.type}" missing required "${a}" argument`
|
|
162
162
|
});
|
|
163
163
|
return r;
|
|
164
164
|
}
|
|
165
|
-
function K(
|
|
165
|
+
function K(n, o) {
|
|
166
166
|
const s = [], r = /* @__PURE__ */ new Set();
|
|
167
|
-
for (const e of Object.values(
|
|
167
|
+
for (const e of Object.values(n.locales))
|
|
168
168
|
for (const i of Object.keys(e))
|
|
169
169
|
r.add(i);
|
|
170
|
-
const
|
|
170
|
+
const t = (e) => e.startsWith("@"), a = (e, i, l) => {
|
|
171
171
|
const d = e.slice(1);
|
|
172
172
|
if (!r.has(d)) {
|
|
173
173
|
const u = o.get(i) || `${l}:${i}`;
|
|
@@ -178,44 +178,54 @@ function K(t, o) {
|
|
|
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) && a(e.name, e.id, "location"), t(e.description) && a(e.description, e.id, "location");
|
|
183
|
+
for (const e of Object.values(n.characters))
|
|
184
|
+
t(e.name) && a(e.name, e.id, "character"), t(e.biography) && a(e.biography, e.id, "character");
|
|
185
|
+
for (const e of Object.values(n.items))
|
|
186
|
+
t(e.name) && a(e.name, e.id, "item"), t(e.description) && a(e.description, e.id, "item");
|
|
187
|
+
for (const e of Object.values(n.quests)) {
|
|
188
|
+
t(e.name) && a(e.name, e.id, "quest"), t(e.description) && a(e.description, e.id, "quest");
|
|
189
189
|
for (const i of e.stages)
|
|
190
|
-
|
|
190
|
+
t(i.description) && a(i.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) && a(e.title, e.id, "journal"), t(e.text) && a(e.text, e.id, "journal");
|
|
194
|
+
for (const e of Object.values(n.dialogues))
|
|
195
195
|
for (const i of e.nodes) {
|
|
196
|
-
|
|
196
|
+
t(i.text) && a(i.text, e.id, "dialogue");
|
|
197
197
|
for (const l of i.choices)
|
|
198
|
-
|
|
198
|
+
t(l.text) && a(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) && a(e.text, e.id, "interlude");
|
|
202
202
|
return s;
|
|
203
203
|
}
|
|
204
|
-
function I(
|
|
205
|
-
if (
|
|
204
|
+
function I(n) {
|
|
205
|
+
if (n.length === 0) {
|
|
206
206
|
console.log(c.green("â No validation errors"));
|
|
207
207
|
return;
|
|
208
208
|
}
|
|
209
|
-
console.log(
|
|
210
|
-
|
|
211
|
-
`
|
|
212
|
-
|
|
209
|
+
console.log(
|
|
210
|
+
c.red(
|
|
211
|
+
`
|
|
212
|
+
â Found ${n.length} validation error${n.length === 1 ? "" : "s"}:
|
|
213
|
+
`
|
|
214
|
+
)
|
|
215
|
+
);
|
|
216
|
+
for (const o of n)
|
|
213
217
|
console.log(c.bold(o.file) + (o.line ? `:${o.line}` : "")), console.log(" " + c.red(o.message)), o.suggestion && console.log(" " + c.dim(o.suggestion)), console.log();
|
|
214
218
|
}
|
|
215
|
-
async function q(
|
|
216
|
-
const { shell: a, game: e } = W(
|
|
219
|
+
async function q(n, o, s, r, t = Date.now().toString()) {
|
|
220
|
+
const { shell: a, game: e } = W(
|
|
221
|
+
s,
|
|
222
|
+
r
|
|
223
|
+
);
|
|
217
224
|
async function i(g) {
|
|
218
|
-
const _ = h(
|
|
225
|
+
const _ = h(
|
|
226
|
+
o,
|
|
227
|
+
g.startsWith("/") ? g.slice(1) : g
|
|
228
|
+
);
|
|
219
229
|
try {
|
|
220
230
|
return (await B(_)).size;
|
|
221
231
|
} catch {
|
|
@@ -223,22 +233,26 @@ async function q(t, o, s, r, n = Date.now().toString()) {
|
|
|
223
233
|
}
|
|
224
234
|
}
|
|
225
235
|
const l = await Promise.all(
|
|
226
|
-
a.map(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
236
|
+
a.map(
|
|
237
|
+
async (g) => ({
|
|
238
|
+
path: g,
|
|
239
|
+
type: R(g),
|
|
240
|
+
size: await i(g),
|
|
241
|
+
tier: 1
|
|
242
|
+
})
|
|
243
|
+
)
|
|
232
244
|
), d = await Promise.all(
|
|
233
|
-
e.map(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
245
|
+
e.map(
|
|
246
|
+
async (g) => ({
|
|
247
|
+
path: g,
|
|
248
|
+
type: R(g),
|
|
249
|
+
size: await i(g),
|
|
250
|
+
tier: 2
|
|
251
|
+
})
|
|
252
|
+
)
|
|
239
253
|
), u = l.reduce((g, _) => g + (_.size ?? 0), 0), m = d.reduce((g, _) => g + (_.size ?? 0), 0);
|
|
240
254
|
return {
|
|
241
|
-
version:
|
|
255
|
+
version: t,
|
|
242
256
|
shell: l,
|
|
243
257
|
game: d,
|
|
244
258
|
shellSize: u,
|
|
@@ -246,24 +260,24 @@ async function q(t, o, s, r, n = Date.now().toString()) {
|
|
|
246
260
|
};
|
|
247
261
|
}
|
|
248
262
|
const S = "ðū", X = "âĻ", Z = "âïļ", ee = "â";
|
|
249
|
-
async function
|
|
250
|
-
const
|
|
263
|
+
async function ne() {
|
|
264
|
+
const n = process.cwd(), o = h(n, "content");
|
|
251
265
|
console.log(""), console.log(c.bold.magenta(` ${S} Doodle Engine Dev Server ${S}`)), console.log("");
|
|
252
266
|
const s = {
|
|
253
267
|
name: "doodle-content-loader",
|
|
254
|
-
configureServer(
|
|
255
|
-
|
|
268
|
+
configureServer(t) {
|
|
269
|
+
t.middlewares.use("/api/content", async (d, u) => {
|
|
256
270
|
try {
|
|
257
271
|
const m = await j(o);
|
|
258
272
|
u.setHeader("Content-Type", "application/json"), u.end(JSON.stringify(m));
|
|
259
273
|
} catch (m) {
|
|
260
274
|
console.error(c.red(" Error loading content:"), m), u.statusCode = 500, u.end(JSON.stringify({ error: "Failed to load content" }));
|
|
261
275
|
}
|
|
262
|
-
}),
|
|
276
|
+
}), t.middlewares.use("/api/manifest", async (d, u) => {
|
|
263
277
|
try {
|
|
264
278
|
const { registry: m, config: g } = await j(o), _ = await q(
|
|
265
|
-
h(
|
|
266
|
-
|
|
279
|
+
h(n, "assets"),
|
|
280
|
+
n,
|
|
267
281
|
m,
|
|
268
282
|
g,
|
|
269
283
|
"dev"
|
|
@@ -284,7 +298,7 @@ async function te() {
|
|
|
284
298
|
let i = null;
|
|
285
299
|
const l = (d) => {
|
|
286
300
|
i && clearTimeout(i), i = setTimeout(async () => {
|
|
287
|
-
i = null, console.log(d), await
|
|
301
|
+
i = null, console.log(d), await te(o), t.ws.send({ type: "full-reload", path: "*" });
|
|
288
302
|
}, 50);
|
|
289
303
|
};
|
|
290
304
|
a.on("change", (d) => {
|
|
@@ -294,7 +308,7 @@ async function te() {
|
|
|
294
308
|
});
|
|
295
309
|
}
|
|
296
310
|
}, r = await F({
|
|
297
|
-
root:
|
|
311
|
+
root: n,
|
|
298
312
|
plugins: [G(), s],
|
|
299
313
|
server: {
|
|
300
314
|
port: 3e3,
|
|
@@ -303,7 +317,7 @@ async function te() {
|
|
|
303
317
|
});
|
|
304
318
|
await r.listen(), r.printUrls(), console.log(""), console.log(c.dim(` ${X} Watching content files for changes...`)), console.log("");
|
|
305
319
|
}
|
|
306
|
-
async function j(
|
|
320
|
+
async function j(n) {
|
|
307
321
|
const o = {
|
|
308
322
|
locations: {},
|
|
309
323
|
characters: {},
|
|
@@ -325,8 +339,8 @@ async function j(t) {
|
|
|
325
339
|
{ dir: "journal", key: "journalEntries" },
|
|
326
340
|
{ dir: "interludes", key: "interludes" }
|
|
327
341
|
];
|
|
328
|
-
for (const { dir:
|
|
329
|
-
const e = h(
|
|
342
|
+
for (const { dir: t, key: a } of r) {
|
|
343
|
+
const e = h(n, t);
|
|
330
344
|
try {
|
|
331
345
|
const i = await b(e);
|
|
332
346
|
for (const l of i)
|
|
@@ -338,25 +352,25 @@ async function j(t) {
|
|
|
338
352
|
}
|
|
339
353
|
}
|
|
340
354
|
try {
|
|
341
|
-
const
|
|
355
|
+
const t = h(n, "locales"), a = await b(t);
|
|
342
356
|
for (const e of a)
|
|
343
357
|
if (f(e) === ".yaml" || f(e) === ".yml") {
|
|
344
|
-
const i = h(
|
|
358
|
+
const i = h(t, e), l = await p(i, "utf-8"), d = y(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
345
359
|
o.locales[u] = d ?? {};
|
|
346
360
|
}
|
|
347
361
|
} catch {
|
|
348
362
|
}
|
|
349
363
|
try {
|
|
350
|
-
const
|
|
364
|
+
const t = h(n, "dialogues"), a = await b(t);
|
|
351
365
|
for (const e of a)
|
|
352
366
|
if (f(e) === ".dlg") {
|
|
353
|
-
const i = h(
|
|
367
|
+
const i = h(t, e), l = await p(i, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
354
368
|
o.dialogues[u.id] = u;
|
|
355
369
|
}
|
|
356
370
|
} catch {
|
|
357
371
|
}
|
|
358
372
|
try {
|
|
359
|
-
const
|
|
373
|
+
const t = h(n, "game.yaml"), a = await p(t, "utf-8");
|
|
360
374
|
s = y(a);
|
|
361
375
|
} catch {
|
|
362
376
|
console.warn(c.yellow(" No game.yaml found, using defaults")), s = {
|
|
@@ -369,15 +383,15 @@ async function j(t) {
|
|
|
369
383
|
}
|
|
370
384
|
return { registry: o, config: s };
|
|
371
385
|
}
|
|
372
|
-
async function
|
|
386
|
+
async function te(n) {
|
|
373
387
|
try {
|
|
374
|
-
const { registry: o, fileMap: s } = await oe(
|
|
388
|
+
const { registry: o, fileMap: s } = await oe(n), r = C(o, s);
|
|
375
389
|
r.length > 0 && (console.log(""), I(r), console.log(""));
|
|
376
390
|
} catch (o) {
|
|
377
391
|
console.error(c.red(" Error running validation:"), o);
|
|
378
392
|
}
|
|
379
393
|
}
|
|
380
|
-
async function oe(
|
|
394
|
+
async function oe(n) {
|
|
381
395
|
const o = {
|
|
382
396
|
locations: {},
|
|
383
397
|
characters: {},
|
|
@@ -397,8 +411,8 @@ async function oe(t) {
|
|
|
397
411
|
{ dir: "journal", key: "journalEntries" },
|
|
398
412
|
{ dir: "interludes", key: "interludes" }
|
|
399
413
|
];
|
|
400
|
-
for (const { dir:
|
|
401
|
-
const e = h(
|
|
414
|
+
for (const { dir: t, key: a } of r) {
|
|
415
|
+
const e = h(n, t);
|
|
402
416
|
try {
|
|
403
417
|
const i = await b(e);
|
|
404
418
|
for (const l of i)
|
|
@@ -410,34 +424,34 @@ async function oe(t) {
|
|
|
410
424
|
}
|
|
411
425
|
}
|
|
412
426
|
try {
|
|
413
|
-
const
|
|
427
|
+
const t = h(n, "locales"), a = await b(t);
|
|
414
428
|
for (const e of a)
|
|
415
429
|
if (f(e) === ".yaml" || f(e) === ".yml") {
|
|
416
|
-
const i = h(
|
|
430
|
+
const i = h(t, e), l = await p(i, "utf-8"), d = y(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
417
431
|
o.locales[u] = d ?? {};
|
|
418
432
|
}
|
|
419
433
|
} catch {
|
|
420
434
|
}
|
|
421
435
|
try {
|
|
422
|
-
const
|
|
436
|
+
const t = h(n, "dialogues"), a = await b(t);
|
|
423
437
|
for (const e of a)
|
|
424
438
|
if (f(e) === ".dlg") {
|
|
425
|
-
const i = h(
|
|
439
|
+
const i = h(t, e), l = await p(i, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
426
440
|
o.dialogues[u.id] = u, s.set(u.id, E(process.cwd(), i));
|
|
427
441
|
}
|
|
428
442
|
} catch {
|
|
429
443
|
}
|
|
430
444
|
return { registry: o, fileMap: s };
|
|
431
445
|
}
|
|
432
|
-
function ae(
|
|
433
|
-
const o = `doodle-engine-assets-${
|
|
434
|
-
...
|
|
435
|
-
...
|
|
446
|
+
function ae(n) {
|
|
447
|
+
const o = `doodle-engine-assets-${n.version}`, s = [
|
|
448
|
+
...n.shell.map((t) => t.path),
|
|
449
|
+
...n.game.map((t) => t.path)
|
|
436
450
|
], r = JSON.stringify(s, null, 2);
|
|
437
451
|
return `/**
|
|
438
452
|
* Doodle Engine Service Worker
|
|
439
453
|
* Generated at build time â do not edit manually.
|
|
440
|
-
* Cache version: ${
|
|
454
|
+
* Cache version: ${n.version}
|
|
441
455
|
*/
|
|
442
456
|
|
|
443
457
|
const CACHE_NAME = ${JSON.stringify(o)};
|
|
@@ -505,44 +519,47 @@ self.addEventListener('fetch', (event) => {
|
|
|
505
519
|
`;
|
|
506
520
|
}
|
|
507
521
|
async function re() {
|
|
508
|
-
const
|
|
522
|
+
const n = process.cwd(), o = h(n, "content");
|
|
509
523
|
console.log(""), console.log(c.bold.magenta("ð Building Doodle Engine game...")), console.log(""), console.log(c.dim("Validating content..."));
|
|
510
|
-
let s, r,
|
|
524
|
+
let s, r, t;
|
|
511
525
|
try {
|
|
512
526
|
const a = await se(o);
|
|
513
|
-
r = a.registry,
|
|
527
|
+
r = a.registry, t = a.config;
|
|
514
528
|
const { fileMap: e } = a, i = C(r, e);
|
|
515
|
-
I(i), i.length > 0 && (console.log(c.red("Build failed due to validation errors.")), console.log(""), process.exit(1)), s = { registry: r, config:
|
|
529
|
+
I(i), i.length > 0 && (console.log(c.red("Build failed due to validation errors.")), console.log(""), process.exit(1)), s = { registry: r, config: t };
|
|
516
530
|
} catch (a) {
|
|
517
531
|
console.error(c.red("Error loading content:"), a), process.exit(1);
|
|
518
532
|
}
|
|
519
533
|
console.log("");
|
|
520
534
|
try {
|
|
521
535
|
await P({
|
|
522
|
-
root:
|
|
536
|
+
root: n,
|
|
523
537
|
plugins: [G()],
|
|
524
538
|
build: {
|
|
525
539
|
outDir: "dist",
|
|
526
540
|
emptyOutDir: !0
|
|
527
541
|
}
|
|
528
542
|
});
|
|
529
|
-
const a = h(
|
|
543
|
+
const a = h(n, "dist"), e = n;
|
|
530
544
|
console.log(c.dim("Generating asset manifest..."));
|
|
531
545
|
const i = await q(
|
|
532
|
-
h(
|
|
546
|
+
h(n, "assets"),
|
|
533
547
|
e,
|
|
534
548
|
r,
|
|
535
|
-
|
|
549
|
+
t,
|
|
536
550
|
Date.now().toString()
|
|
537
551
|
), l = h(a, "api");
|
|
538
|
-
await T(l, { recursive: !0 }), await w(h(l, "content"), JSON.stringify(s)), await w(h(l, "manifest"), JSON.stringify(i)), await w(
|
|
552
|
+
await T(l, { recursive: !0 }), await w(h(l, "content"), JSON.stringify(s)), await w(h(l, "manifest"), JSON.stringify(i)), await w(
|
|
553
|
+
h(a, "asset-manifest.json"),
|
|
554
|
+
JSON.stringify(i, null, 2)
|
|
555
|
+
), console.log(c.dim("Generating service worker..."));
|
|
539
556
|
const d = ae(i);
|
|
540
557
|
await w(h(a, "sw.js"), d), console.log(""), console.log(c.green("â
Build complete! Output in dist/")), console.log(""), console.log("To preview the build:"), console.log(c.dim(" yarn preview")), console.log("");
|
|
541
558
|
} catch (a) {
|
|
542
559
|
console.error(c.red("Build failed:"), a), process.exit(1);
|
|
543
560
|
}
|
|
544
561
|
}
|
|
545
|
-
async function se(
|
|
562
|
+
async function se(n) {
|
|
546
563
|
const o = {
|
|
547
564
|
locations: {},
|
|
548
565
|
characters: {},
|
|
@@ -563,7 +580,7 @@ async function se(t) {
|
|
|
563
580
|
{ dir: "interludes", key: "interludes" }
|
|
564
581
|
];
|
|
565
582
|
for (const { dir: a, key: e } of r) {
|
|
566
|
-
const i = h(
|
|
583
|
+
const i = h(n, a);
|
|
567
584
|
try {
|
|
568
585
|
const l = await b(i);
|
|
569
586
|
for (const d of l)
|
|
@@ -575,7 +592,7 @@ async function se(t) {
|
|
|
575
592
|
}
|
|
576
593
|
}
|
|
577
594
|
try {
|
|
578
|
-
const a = h(
|
|
595
|
+
const a = h(n, "locales"), e = await b(a);
|
|
579
596
|
for (const i of e)
|
|
580
597
|
if (f(i) === ".yaml" || f(i) === ".yml") {
|
|
581
598
|
const l = h(a, i), d = await p(l, "utf-8"), u = y(d), m = i.replace(/\.(yaml|yml)$/, "");
|
|
@@ -584,7 +601,7 @@ async function se(t) {
|
|
|
584
601
|
} catch {
|
|
585
602
|
}
|
|
586
603
|
try {
|
|
587
|
-
const a = h(
|
|
604
|
+
const a = h(n, "dialogues"), e = await b(a);
|
|
588
605
|
for (const i of e)
|
|
589
606
|
if (f(i) === ".dlg") {
|
|
590
607
|
const l = h(a, i), d = await p(l, "utf-8"), u = i.replace(".dlg", ""), m = k(d, u);
|
|
@@ -592,26 +609,33 @@ async function se(t) {
|
|
|
592
609
|
}
|
|
593
610
|
} catch {
|
|
594
611
|
}
|
|
595
|
-
let
|
|
612
|
+
let t = null;
|
|
596
613
|
try {
|
|
597
|
-
const a = h(
|
|
598
|
-
|
|
614
|
+
const a = h(n, "game.yaml"), e = await p(a, "utf-8");
|
|
615
|
+
t = y(e);
|
|
599
616
|
} catch {
|
|
600
|
-
|
|
617
|
+
t = {
|
|
618
|
+
id: "game",
|
|
619
|
+
startLocation: "",
|
|
620
|
+
startTime: { day: 1, hour: 8 },
|
|
621
|
+
startFlags: {},
|
|
622
|
+
startVariables: {},
|
|
623
|
+
startInventory: []
|
|
624
|
+
};
|
|
601
625
|
}
|
|
602
|
-
return { registry: o, fileMap: s, config:
|
|
626
|
+
return { registry: o, fileMap: s, config: t };
|
|
603
627
|
}
|
|
604
628
|
async function ie() {
|
|
605
|
-
const
|
|
629
|
+
const n = process.cwd(), o = h(n, "content");
|
|
606
630
|
console.log(""), console.log(c.bold.magenta("ðū Validating Doodle Engine content...")), console.log("");
|
|
607
631
|
try {
|
|
608
|
-
const { registry: s, fileMap: r } = await ce(o),
|
|
609
|
-
I(
|
|
632
|
+
const { registry: s, fileMap: r } = await ce(o), t = C(s, r);
|
|
633
|
+
I(t), t.length > 0 && process.exit(1);
|
|
610
634
|
} catch (s) {
|
|
611
635
|
console.error(c.red("Error loading content:"), s), process.exit(1);
|
|
612
636
|
}
|
|
613
637
|
}
|
|
614
|
-
async function ce(
|
|
638
|
+
async function ce(n) {
|
|
615
639
|
const o = {
|
|
616
640
|
locations: {},
|
|
617
641
|
characters: {},
|
|
@@ -629,8 +653,8 @@ async function ce(t) {
|
|
|
629
653
|
{ dir: "quests", key: "quests" },
|
|
630
654
|
{ dir: "journal", key: "journalEntries" }
|
|
631
655
|
];
|
|
632
|
-
for (const { dir:
|
|
633
|
-
const e = h(
|
|
656
|
+
for (const { dir: t, key: a } of r) {
|
|
657
|
+
const e = h(n, t);
|
|
634
658
|
try {
|
|
635
659
|
const i = await b(e);
|
|
636
660
|
for (const l of i)
|
|
@@ -642,19 +666,19 @@ async function ce(t) {
|
|
|
642
666
|
}
|
|
643
667
|
}
|
|
644
668
|
try {
|
|
645
|
-
const
|
|
669
|
+
const t = h(n, "locales"), a = await b(t);
|
|
646
670
|
for (const e of a)
|
|
647
671
|
if (f(e) === ".yaml" || f(e) === ".yml") {
|
|
648
|
-
const i = h(
|
|
672
|
+
const i = h(t, e), l = await p(i, "utf-8"), d = y(l), u = e.replace(/\.(yaml|yml)$/, "");
|
|
649
673
|
o.locales[u] = d ?? {};
|
|
650
674
|
}
|
|
651
675
|
} catch {
|
|
652
676
|
}
|
|
653
677
|
try {
|
|
654
|
-
const
|
|
678
|
+
const t = h(n, "dialogues"), a = await b(t);
|
|
655
679
|
for (const e of a)
|
|
656
680
|
if (f(e) === ".dlg") {
|
|
657
|
-
const i = h(
|
|
681
|
+
const i = h(t, e), l = await p(i, "utf-8"), d = e.replace(".dlg", ""), u = k(l, d);
|
|
658
682
|
o.dialogues[u.id] = u, s.set(u.id, E(process.cwd(), i));
|
|
659
683
|
}
|
|
660
684
|
} catch {
|
|
@@ -1364,52 +1388,82 @@ stages:
|
|
|
1364
1388
|
description: "@quest.odd_jobs.stage.talked_to_merchant"
|
|
1365
1389
|
- id: complete
|
|
1366
1390
|
description: "@quest.odd_jobs.stage.complete"
|
|
1367
|
-
`, Re = `import { useEffect, useState } from
|
|
1368
|
-
import { Engine } from
|
|
1369
|
-
import type { GameState, Snapshot } from
|
|
1370
|
-
import { GameProvider, useGame } from
|
|
1391
|
+
`, Re = `import { useEffect, useState } from "react";
|
|
1392
|
+
import { Engine } from "@doodle-engine/core";
|
|
1393
|
+
import type { GameState, Snapshot } from "@doodle-engine/core";
|
|
1394
|
+
import { GameProvider, useGame } from "@doodle-engine/react";
|
|
1371
1395
|
|
|
1372
1396
|
export function App() {
|
|
1373
|
-
const [game, setGame] = useState<{
|
|
1397
|
+
const [game, setGame] = useState<{
|
|
1398
|
+
engine: Engine;
|
|
1399
|
+
snapshot: Snapshot;
|
|
1400
|
+
} | null>(null);
|
|
1374
1401
|
|
|
1375
1402
|
useEffect(() => {
|
|
1376
|
-
fetch(
|
|
1377
|
-
.then(res => res.json())
|
|
1378
|
-
.then(data => {
|
|
1379
|
-
const engine = new Engine(data.registry, createEmptyState())
|
|
1380
|
-
const snapshot = engine.newGame(data.config)
|
|
1381
|
-
setGame({ engine, snapshot })
|
|
1382
|
-
})
|
|
1383
|
-
}, [])
|
|
1403
|
+
fetch("/api/content")
|
|
1404
|
+
.then((res) => res.json())
|
|
1405
|
+
.then((data) => {
|
|
1406
|
+
const engine = new Engine(data.registry, createEmptyState());
|
|
1407
|
+
const snapshot = engine.newGame(data.config);
|
|
1408
|
+
setGame({ engine, snapshot });
|
|
1409
|
+
});
|
|
1410
|
+
}, []);
|
|
1384
1411
|
|
|
1385
1412
|
if (!game) {
|
|
1386
|
-
return
|
|
1413
|
+
return (
|
|
1414
|
+
<div className="app-bootstrap">
|
|
1415
|
+
<div className="spinner" />
|
|
1416
|
+
</div>
|
|
1417
|
+
);
|
|
1387
1418
|
}
|
|
1388
1419
|
|
|
1389
1420
|
return (
|
|
1390
|
-
<GameProvider
|
|
1421
|
+
<GameProvider
|
|
1422
|
+
engine={game.engine}
|
|
1423
|
+
initialSnapshot={game.snapshot}
|
|
1424
|
+
devTools={import.meta.env.DEV}
|
|
1425
|
+
>
|
|
1391
1426
|
<GameUI />
|
|
1392
1427
|
</GameProvider>
|
|
1393
|
-
)
|
|
1428
|
+
);
|
|
1394
1429
|
}
|
|
1395
1430
|
|
|
1396
1431
|
function GameUI() {
|
|
1397
|
-
const { snapshot, actions } = useGame()
|
|
1432
|
+
const { snapshot, actions } = useGame();
|
|
1398
1433
|
|
|
1399
1434
|
return (
|
|
1400
|
-
<div
|
|
1435
|
+
<div
|
|
1436
|
+
style={{
|
|
1437
|
+
padding: "2rem",
|
|
1438
|
+
fontFamily: "sans-serif",
|
|
1439
|
+
maxWidth: "800px",
|
|
1440
|
+
margin: "0 auto",
|
|
1441
|
+
}}
|
|
1442
|
+
>
|
|
1401
1443
|
<h1>{snapshot.location.name}</h1>
|
|
1402
1444
|
<p>{snapshot.location.description}</p>
|
|
1403
1445
|
|
|
1404
1446
|
{snapshot.dialogue && (
|
|
1405
|
-
<div
|
|
1447
|
+
<div
|
|
1448
|
+
style={{
|
|
1449
|
+
background: "#f0f0f0",
|
|
1450
|
+
padding: "1rem",
|
|
1451
|
+
borderRadius: "8px",
|
|
1452
|
+
margin: "1rem 0",
|
|
1453
|
+
}}
|
|
1454
|
+
>
|
|
1406
1455
|
<strong>{snapshot.dialogue.speakerName}:</strong>
|
|
1407
1456
|
<p>{snapshot.dialogue.text}</p>
|
|
1408
|
-
{snapshot.choices.map(choice => (
|
|
1457
|
+
{snapshot.choices.map((choice) => (
|
|
1409
1458
|
<button
|
|
1410
1459
|
key={choice.id}
|
|
1411
1460
|
onClick={() => actions.selectChoice(choice.id)}
|
|
1412
|
-
style={{
|
|
1461
|
+
style={{
|
|
1462
|
+
display: "block",
|
|
1463
|
+
margin: "0.5rem 0",
|
|
1464
|
+
padding: "0.5rem 1rem",
|
|
1465
|
+
cursor: "pointer",
|
|
1466
|
+
}}
|
|
1413
1467
|
>
|
|
1414
1468
|
{choice.text}
|
|
1415
1469
|
</button>
|
|
@@ -1420,11 +1474,16 @@ function GameUI() {
|
|
|
1420
1474
|
{!snapshot.dialogue && snapshot.charactersHere.length > 0 && (
|
|
1421
1475
|
<div>
|
|
1422
1476
|
<h2>Characters here</h2>
|
|
1423
|
-
{snapshot.charactersHere.map(char => (
|
|
1477
|
+
{snapshot.charactersHere.map((char) => (
|
|
1424
1478
|
<button
|
|
1425
1479
|
key={char.id}
|
|
1426
1480
|
onClick={() => actions.talkTo(char.id)}
|
|
1427
|
-
style={{
|
|
1481
|
+
style={{
|
|
1482
|
+
display: "block",
|
|
1483
|
+
margin: "0.5rem 0",
|
|
1484
|
+
padding: "0.5rem 1rem",
|
|
1485
|
+
cursor: "pointer",
|
|
1486
|
+
}}
|
|
1428
1487
|
>
|
|
1429
1488
|
Talk to {char.name}
|
|
1430
1489
|
</button>
|
|
@@ -1432,12 +1491,12 @@ function GameUI() {
|
|
|
1432
1491
|
</div>
|
|
1433
1492
|
)}
|
|
1434
1493
|
</div>
|
|
1435
|
-
)
|
|
1494
|
+
);
|
|
1436
1495
|
}
|
|
1437
1496
|
|
|
1438
1497
|
function createEmptyState(): GameState {
|
|
1439
1498
|
return {
|
|
1440
|
-
currentLocation:
|
|
1499
|
+
currentLocation: "",
|
|
1441
1500
|
currentTime: { day: 1, hour: 0 },
|
|
1442
1501
|
flags: {},
|
|
1443
1502
|
variables: {},
|
|
@@ -1453,29 +1512,36 @@ function createEmptyState(): GameState {
|
|
|
1453
1512
|
pendingSounds: [],
|
|
1454
1513
|
pendingVideo: null,
|
|
1455
1514
|
pendingInterlude: null,
|
|
1456
|
-
currentLocale:
|
|
1457
|
-
}
|
|
1515
|
+
currentLocale: "en",
|
|
1516
|
+
};
|
|
1458
1517
|
}
|
|
1459
|
-
`, Ae = `import { useEffect, useState } from
|
|
1460
|
-
import type {
|
|
1461
|
-
|
|
1518
|
+
`, Ae = `import { useEffect, useState } from "react";
|
|
1519
|
+
import type {
|
|
1520
|
+
ContentRegistry,
|
|
1521
|
+
GameConfig,
|
|
1522
|
+
AssetManifest,
|
|
1523
|
+
} from "@doodle-engine/core";
|
|
1524
|
+
import { GameShell } from "@doodle-engine/react";
|
|
1462
1525
|
|
|
1463
1526
|
export function App() {
|
|
1464
1527
|
const [content, setContent] = useState<{
|
|
1465
|
-
registry: ContentRegistry
|
|
1466
|
-
config: GameConfig
|
|
1467
|
-
} | null>(null)
|
|
1468
|
-
const [manifest, setManifest] = useState<AssetManifest | null>(null)
|
|
1528
|
+
registry: ContentRegistry;
|
|
1529
|
+
config: GameConfig;
|
|
1530
|
+
} | null>(null);
|
|
1531
|
+
const [manifest, setManifest] = useState<AssetManifest | null>(null);
|
|
1469
1532
|
|
|
1470
1533
|
useEffect(() => {
|
|
1471
1534
|
Promise.all([
|
|
1472
|
-
fetch(
|
|
1473
|
-
fetch(
|
|
1535
|
+
fetch("/api/content").then((res) => res.json()),
|
|
1536
|
+
fetch("/api/manifest").then((res) => res.json()),
|
|
1474
1537
|
]).then(([contentData, manifestData]) => {
|
|
1475
|
-
setContent({
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1538
|
+
setContent({
|
|
1539
|
+
registry: contentData.registry,
|
|
1540
|
+
config: contentData.config,
|
|
1541
|
+
});
|
|
1542
|
+
setManifest(manifestData);
|
|
1543
|
+
});
|
|
1544
|
+
}, []);
|
|
1479
1545
|
|
|
1480
1546
|
// Minimal bootstrap state while fetching manifest/content
|
|
1481
1547
|
if (!content || !manifest) {
|
|
@@ -1483,7 +1549,7 @@ export function App() {
|
|
|
1483
1549
|
<div className="app-bootstrap">
|
|
1484
1550
|
<div className="spinner" />
|
|
1485
1551
|
</div>
|
|
1486
|
-
)
|
|
1552
|
+
);
|
|
1487
1553
|
}
|
|
1488
1554
|
|
|
1489
1555
|
return (
|
|
@@ -1493,73 +1559,49 @@ export function App() {
|
|
|
1493
1559
|
manifest={manifest}
|
|
1494
1560
|
title="My Doodle Game"
|
|
1495
1561
|
subtitle="A text-based adventure"
|
|
1496
|
-
availableLocales={[{ code:
|
|
1562
|
+
availableLocales={[{ code: "en", label: "English" }]}
|
|
1497
1563
|
devTools={import.meta.env.DEV}
|
|
1498
1564
|
/>
|
|
1499
|
-
)
|
|
1565
|
+
);
|
|
1500
1566
|
}
|
|
1501
1567
|
`, Se = `body {
|
|
1502
1568
|
margin: 0;
|
|
1503
|
-
font-family:
|
|
1504
|
-
|
|
1505
|
-
sans-serif;
|
|
1569
|
+
font-family:
|
|
1570
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
|
|
1571
|
+
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
|
1506
1572
|
-webkit-font-smoothing: antialiased;
|
|
1507
1573
|
-moz-osx-font-smoothing: grayscale;
|
|
1508
1574
|
}
|
|
1509
1575
|
|
|
1510
|
-
/* ââ
|
|
1511
|
-
/* Override these to
|
|
1576
|
+
/* ââ Theme overrides ââââââââââââââââââââââââââââââââââââââââââââââââ */
|
|
1577
|
+
/* Override these variables to customize the shell screens. */
|
|
1578
|
+
/* Full variable list: node_modules/@doodle-engine/react/dist/shell.css */
|
|
1512
1579
|
|
|
1580
|
+
/*
|
|
1513
1581
|
:root {
|
|
1514
1582
|
--doodle-bg-primary: #0f0f23;
|
|
1515
1583
|
--doodle-bg-secondary: #1a1a2e;
|
|
1516
|
-
--doodle-text-primary: #ffffff;
|
|
1517
|
-
--doodle-text-secondary: #a0a0b0;
|
|
1518
1584
|
--doodle-accent: #6366f1;
|
|
1519
|
-
--doodle-accent-hover: #818cf8;
|
|
1520
|
-
--doodle-font-family: system-ui, -apple-system, sans-serif;
|
|
1521
1585
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
.
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
display: flex;
|
|
1529
|
-
align-items: center;
|
|
1530
|
-
justify-content: center;
|
|
1531
|
-
background: var(--doodle-bg-primary);
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
.app-bootstrap .spinner {
|
|
1535
|
-
width: 40px;
|
|
1536
|
-
height: 40px;
|
|
1537
|
-
border: 3px solid rgba(255, 255, 255, 0.1);
|
|
1538
|
-
border-top-color: var(--doodle-accent);
|
|
1539
|
-
border-radius: 50%;
|
|
1540
|
-
animation: spin 0.8s linear infinite;
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
@keyframes spin {
|
|
1544
|
-
to { transform: rotate(360deg); }
|
|
1545
|
-
}
|
|
1546
|
-
`, je = `import { StrictMode } from 'react'
|
|
1547
|
-
import { createRoot } from 'react-dom/client'
|
|
1548
|
-
import { App } from './App'
|
|
1549
|
-
import './index.css'
|
|
1586
|
+
*/
|
|
1587
|
+
`, je = `import { StrictMode } from "react";
|
|
1588
|
+
import { createRoot } from "react-dom/client";
|
|
1589
|
+
import "@doodle-engine/react/style.css";
|
|
1590
|
+
import { App } from "./App";
|
|
1591
|
+
import "./index.css";
|
|
1550
1592
|
|
|
1551
1593
|
// Register service worker in production for offline asset caching
|
|
1552
|
-
if (
|
|
1553
|
-
navigator.serviceWorker.register(
|
|
1594
|
+
if ("serviceWorker" in navigator && import.meta.env.PROD) {
|
|
1595
|
+
navigator.serviceWorker.register("/sw.js").catch(() => {
|
|
1554
1596
|
// SW registration failure is non-fatal
|
|
1555
|
-
})
|
|
1597
|
+
});
|
|
1556
1598
|
}
|
|
1557
1599
|
|
|
1558
|
-
createRoot(document.getElementById(
|
|
1600
|
+
createRoot(document.getElementById("root")!).render(
|
|
1559
1601
|
<StrictMode>
|
|
1560
1602
|
<App />
|
|
1561
1603
|
</StrictMode>,
|
|
1562
|
-
)
|
|
1604
|
+
);
|
|
1563
1605
|
`, D = "ðū", xe = "ð", H = "ðĶī", Ge = "âĻ", $ = "ð", N = "â
", qe = "ð", x = /* @__PURE__ */ Object.assign({
|
|
1564
1606
|
"./templates/_root/_gitignore": le,
|
|
1565
1607
|
"./templates/_root/index.html": de,
|
|
@@ -1587,28 +1629,35 @@ createRoot(document.getElementById('root')!).render(
|
|
|
1587
1629
|
"./templates/src/index.css": Se,
|
|
1588
1630
|
"./templates/src/main.tsx": je
|
|
1589
1631
|
});
|
|
1590
|
-
function He(
|
|
1591
|
-
const o =
|
|
1592
|
-
if (o === "src/App.default.tsx" || o === "src/App.custom.tsx")
|
|
1632
|
+
function He(n) {
|
|
1633
|
+
const o = n.replace("./templates/", "");
|
|
1634
|
+
if (o === "src/App.default.tsx" || o === "src/App.custom.tsx")
|
|
1635
|
+
return null;
|
|
1593
1636
|
if (o.startsWith("_root/")) {
|
|
1594
1637
|
const s = o.slice(6);
|
|
1595
1638
|
return s.startsWith("_") ? "." + s.slice(1) : s;
|
|
1596
1639
|
}
|
|
1597
1640
|
return o;
|
|
1598
1641
|
}
|
|
1599
|
-
async function $e(
|
|
1600
|
-
const o = h(process.cwd(),
|
|
1601
|
-
console.log(""), console.log(c.bold.magenta(` ${D} Doodle Engine ${D}`)), console.log(c.dim(" Text-based RPG and Adventure Game Scaffolder")), console.log(""), console.log(` ${xe} Creating new game: ${c.bold.cyan(
|
|
1642
|
+
async function $e(n) {
|
|
1643
|
+
const o = h(process.cwd(), n);
|
|
1644
|
+
console.log(""), console.log(c.bold.magenta(` ${D} Doodle Engine ${D}`)), console.log(c.dim(" Text-based RPG and Adventure Game Scaffolder")), console.log(""), console.log(` ${xe} Creating new game: ${c.bold.cyan(n)}`), console.log("");
|
|
1602
1645
|
const { useDefaultRenderer: s } = await J({
|
|
1603
1646
|
type: "confirm",
|
|
1604
1647
|
name: "useDefaultRenderer",
|
|
1605
1648
|
message: "Use default renderer?",
|
|
1606
1649
|
initial: !0
|
|
1607
1650
|
});
|
|
1608
|
-
s === void 0 && (console.log(
|
|
1609
|
-
|
|
1651
|
+
s === void 0 && (console.log(
|
|
1652
|
+
c.yellow(`
|
|
1653
|
+
${H} No worries, maybe next time! Woof!`)
|
|
1654
|
+
), process.exit(0)), console.log(""), await Me(o, n, s), console.log(""), console.log(c.bold.green(` ${N} Project created successfully!`)), console.log(""), console.log(c.dim(` ${$} ${o}`)), console.log(""), console.log(c.bold(" Next steps:")), console.log(c.cyan(` cd ${n}`)), console.log(
|
|
1655
|
+
c.cyan(" npm install ") + c.dim("# or: yarn install / pnpm install")
|
|
1656
|
+
), console.log(
|
|
1657
|
+
c.cyan(" npm run dev ") + c.dim("# or: yarn dev / pnpm dev")
|
|
1658
|
+
), console.log(""), console.log(c.dim(` ${qe} Happy game making! ${D}`)), console.log("");
|
|
1610
1659
|
}
|
|
1611
|
-
async function Me(
|
|
1660
|
+
async function Me(n, o, s) {
|
|
1612
1661
|
const r = [
|
|
1613
1662
|
"content/locations",
|
|
1614
1663
|
"content/characters",
|
|
@@ -1630,9 +1679,9 @@ async function Me(t, o, s) {
|
|
|
1630
1679
|
];
|
|
1631
1680
|
console.log(` ${$} ${c.bold("Creating directories...")}`);
|
|
1632
1681
|
for (const e of r)
|
|
1633
|
-
await T(h(
|
|
1682
|
+
await T(h(n, e), { recursive: !0 });
|
|
1634
1683
|
console.log(c.green(` ${N} Directories created`)), console.log("");
|
|
1635
|
-
const
|
|
1684
|
+
const t = {
|
|
1636
1685
|
name: o,
|
|
1637
1686
|
version: "0.1.0",
|
|
1638
1687
|
type: "module",
|
|
@@ -1656,23 +1705,34 @@ async function Me(t, o, s) {
|
|
|
1656
1705
|
vite: "^6.0.0"
|
|
1657
1706
|
}
|
|
1658
1707
|
};
|
|
1659
|
-
console.log(` ${Ge} ${c.bold("Writing project files...")}`), await w(
|
|
1708
|
+
console.log(` ${Ge} ${c.bold("Writing project files...")}`), await w(
|
|
1709
|
+
h(n, "package.json"),
|
|
1710
|
+
JSON.stringify(t, null, 2)
|
|
1711
|
+
);
|
|
1660
1712
|
for (const [e, i] of Object.entries(x)) {
|
|
1661
1713
|
const l = He(e);
|
|
1662
1714
|
if (l === null) continue;
|
|
1663
|
-
const d = h(
|
|
1715
|
+
const d = h(n, l);
|
|
1664
1716
|
await T(U(d), { recursive: !0 }), await w(d, i);
|
|
1665
1717
|
}
|
|
1666
1718
|
const a = s ? "./templates/src/App.default.tsx" : "./templates/src/App.custom.tsx";
|
|
1667
|
-
await w(h(
|
|
1719
|
+
await w(h(n, "src/App.tsx"), x[a]), console.log(c.green(` ${N} Source files created`)), console.log(""), console.log(` ${H} ${c.bold("Starter content written")}`), console.log(""), console.log(c.dim(" Content includes:")), console.log(c.dim(" 2 locations (tavern, market)")), console.log(c.dim(" 2 characters (bartender, merchant)")), console.log(c.dim(" 1 item (old coin)")), console.log(c.dim(" 1 map (town with 2 locations)")), console.log(c.dim(" 1 quest (odd jobs, 3 stages)")), console.log(c.dim(" 3 journal entries")), console.log(
|
|
1720
|
+
c.dim(" 1 interlude (chapter one, auto-triggers at tavern)")
|
|
1721
|
+
), console.log(
|
|
1722
|
+
c.dim(
|
|
1723
|
+
" 5 dialogues (2 narrator intros, 2 NPC conversations, 1 skill check)"
|
|
1724
|
+
)
|
|
1725
|
+
), console.log(c.dim(" English locale with all strings"));
|
|
1668
1726
|
}
|
|
1669
1727
|
const v = new M();
|
|
1670
|
-
v.name("doodle").description(
|
|
1671
|
-
|
|
1672
|
-
|
|
1728
|
+
v.name("doodle").description(
|
|
1729
|
+
c.magenta("ðū Doodle Engine") + c.dim(" â Narrative RPG development tools")
|
|
1730
|
+
).version("0.0.1");
|
|
1731
|
+
v.command("create <project-name>").description("Scaffold a new Doodle Engine game project").action(async (n) => {
|
|
1732
|
+
await $e(n);
|
|
1673
1733
|
});
|
|
1674
1734
|
v.command("dev").description("Start development server with hot reload").action(async () => {
|
|
1675
|
-
await
|
|
1735
|
+
await ne();
|
|
1676
1736
|
});
|
|
1677
1737
|
v.command("build").description("Build game for production").action(async () => {
|
|
1678
1738
|
await re();
|