@rpgjs/map-builder 0.0.0-stage → 5.0.0
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/LICENSE +19 -0
- package/README.md +341 -2
- package/dist/authority/authority.d.ts +118 -0
- package/dist/authority/authority.d.ts.map +1 -0
- package/dist/authority/authority.js +289 -0
- package/dist/authority/authority.js.map +1 -0
- package/dist/authority/index.d.ts +3 -0
- package/dist/authority/index.d.ts.map +1 -0
- package/dist/authority/index.js +3 -0
- package/dist/authority/store.d.ts +36 -0
- package/dist/authority/store.d.ts.map +1 -0
- package/dist/authority/store.js +35 -0
- package/dist/authority/store.js.map +1 -0
- package/dist/client/index.d.ts +72 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +200 -0
- package/dist/client/index.js.map +1 -0
- package/dist/embed/client.d.ts +85 -0
- package/dist/embed/client.d.ts.map +1 -0
- package/dist/embed/client.js +255 -0
- package/dist/embed/client.js.map +1 -0
- package/dist/embed/host.d.ts +112 -0
- package/dist/embed/host.d.ts.map +1 -0
- package/dist/embed/host.js +278 -0
- package/dist/embed/host.js.map +1 -0
- package/dist/embed/index.d.ts +4 -0
- package/dist/embed/index.d.ts.map +1 -0
- package/dist/embed/index.js +4 -0
- package/dist/embed/protocol.d.ts +137 -0
- package/dist/embed/protocol.d.ts.map +1 -0
- package/dist/embed/protocol.js +161 -0
- package/dist/embed/protocol.js.map +1 -0
- package/dist/features/index.d.ts +66 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +157 -0
- package/dist/features/index.js.map +1 -0
- package/dist/features/skin.d.ts +9 -0
- package/dist/features/skin.d.ts.map +1 -0
- package/dist/features/skin.js +43 -0
- package/dist/features/skin.js.map +1 -0
- package/dist/features/terrain.d.ts +66 -0
- package/dist/features/terrain.d.ts.map +1 -0
- package/dist/features/terrain.js +291 -0
- package/dist/features/terrain.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/interaction/builtin-intents.d.ts +9 -0
- package/dist/interaction/builtin-intents.d.ts.map +1 -0
- package/dist/interaction/builtin-intents.js +61 -0
- package/dist/interaction/builtin-intents.js.map +1 -0
- package/dist/interaction/catalog.d.ts +24 -0
- package/dist/interaction/catalog.d.ts.map +1 -0
- package/dist/interaction/catalog.js +20 -0
- package/dist/interaction/catalog.js.map +1 -0
- package/dist/interaction/controller.d.ts +137 -0
- package/dist/interaction/controller.d.ts.map +1 -0
- package/dist/interaction/controller.js +215 -0
- package/dist/interaction/controller.js.map +1 -0
- package/dist/interaction/index.d.ts +14 -0
- package/dist/interaction/index.d.ts.map +1 -0
- package/dist/interaction/index.js +13 -0
- package/dist/interaction/input.d.ts +26 -0
- package/dist/interaction/input.d.ts.map +1 -0
- package/dist/interaction/input.js +33 -0
- package/dist/interaction/input.js.map +1 -0
- package/dist/interaction/intents.d.ts +80 -0
- package/dist/interaction/intents.d.ts.map +1 -0
- package/dist/interaction/keyboard.d.ts +26 -0
- package/dist/interaction/keyboard.d.ts.map +1 -0
- package/dist/interaction/keyboard.js +32 -0
- package/dist/interaction/keyboard.js.map +1 -0
- package/dist/interaction/picking.d.ts +33 -0
- package/dist/interaction/picking.d.ts.map +1 -0
- package/dist/interaction/picking.js +35 -0
- package/dist/interaction/picking.js.map +1 -0
- package/dist/interaction/pipeline.d.ts +45 -0
- package/dist/interaction/pipeline.d.ts.map +1 -0
- package/dist/interaction/pipeline.js +26 -0
- package/dist/interaction/pipeline.js.map +1 -0
- package/dist/interaction/placement.d.ts +76 -0
- package/dist/interaction/placement.d.ts.map +1 -0
- package/dist/interaction/placement.js +238 -0
- package/dist/interaction/placement.js.map +1 -0
- package/dist/interaction/selection-tool.d.ts +29 -0
- package/dist/interaction/selection-tool.d.ts.map +1 -0
- package/dist/interaction/selection-tool.js +190 -0
- package/dist/interaction/selection-tool.js.map +1 -0
- package/dist/interaction/steps.d.ts +62 -0
- package/dist/interaction/steps.d.ts.map +1 -0
- package/dist/interaction/steps.js +126 -0
- package/dist/interaction/steps.js.map +1 -0
- package/dist/interaction/store.d.ts +10 -0
- package/dist/interaction/store.d.ts.map +1 -0
- package/dist/interaction/store.js +28 -0
- package/dist/interaction/store.js.map +1 -0
- package/dist/interaction/tools.d.ts +26 -0
- package/dist/interaction/tools.d.ts.map +1 -0
- package/dist/interaction/tools.js +41 -0
- package/dist/interaction/tools.js.map +1 -0
- package/dist/model/assets.d.ts +36 -0
- package/dist/model/assets.d.ts.map +1 -0
- package/dist/model/assets.js +18 -0
- package/dist/model/assets.js.map +1 -0
- package/dist/model/committer.d.ts +66 -0
- package/dist/model/committer.d.ts.map +1 -0
- package/dist/model/committer.js +38 -0
- package/dist/model/committer.js.map +1 -0
- package/dist/model/core-patches.d.ts +2 -0
- package/dist/model/core-patches.d.ts.map +1 -0
- package/dist/model/core-patches.js +193 -0
- package/dist/model/core-patches.js.map +1 -0
- package/dist/model/define-map.d.ts +52 -0
- package/dist/model/define-map.d.ts.map +1 -0
- package/dist/model/define-map.js +66 -0
- package/dist/model/define-map.js.map +1 -0
- package/dist/model/definition.d.ts +57 -0
- package/dist/model/definition.d.ts.map +1 -0
- package/dist/model/definition.js +6 -0
- package/dist/model/definition.js.map +1 -0
- package/dist/model/errors.d.ts +21 -0
- package/dist/model/errors.d.ts.map +1 -0
- package/dist/model/errors.js +14 -0
- package/dist/model/errors.js.map +1 -0
- package/dist/model/history.d.ts +43 -0
- package/dist/model/history.d.ts.map +1 -0
- package/dist/model/history.js +141 -0
- package/dist/model/history.js.map +1 -0
- package/dist/model/index.d.ts +14 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +14 -0
- package/dist/model/json.d.ts +15 -0
- package/dist/model/json.d.ts.map +1 -0
- package/dist/model/json.js +35 -0
- package/dist/model/json.js.map +1 -0
- package/dist/model/model.d.ts +68 -0
- package/dist/model/model.d.ts.map +1 -0
- package/dist/model/model.js +170 -0
- package/dist/model/model.js.map +1 -0
- package/dist/model/parse.d.ts +24 -0
- package/dist/model/parse.d.ts.map +1 -0
- package/dist/model/parse.js +165 -0
- package/dist/model/parse.js.map +1 -0
- package/dist/model/patches.d.ts +100 -0
- package/dist/model/patches.d.ts.map +1 -0
- package/dist/model/patches.js +21 -0
- package/dist/model/patches.js.map +1 -0
- package/dist/model/result.d.ts +22 -0
- package/dist/model/result.d.ts.map +1 -0
- package/dist/model/result.js +10 -0
- package/dist/model/result.js.map +1 -0
- package/dist/model/source.d.ts +52 -0
- package/dist/model/source.d.ts.map +1 -0
- package/dist/model/source.js +75 -0
- package/dist/model/source.js.map +1 -0
- package/dist/protocol/index.d.ts +92 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +106 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/remote/committer.d.ts +43 -0
- package/dist/remote/committer.d.ts.map +1 -0
- package/dist/remote/committer.js +206 -0
- package/dist/remote/committer.js.map +1 -0
- package/dist/remote/index.d.ts +4 -0
- package/dist/remote/index.d.ts.map +1 -0
- package/dist/remote/index.js +4 -0
- package/dist/remote/join.d.ts +30 -0
- package/dist/remote/join.d.ts.map +1 -0
- package/dist/remote/join.js +36 -0
- package/dist/remote/join.js.map +1 -0
- package/dist/remote/memory-transport.d.ts +20 -0
- package/dist/remote/memory-transport.d.ts.map +1 -0
- package/dist/remote/memory-transport.js +48 -0
- package/dist/remote/memory-transport.js.map +1 -0
- package/dist/renderer/effects.d.ts +52 -0
- package/dist/renderer/effects.d.ts.map +1 -0
- package/dist/renderer/effects.js +111 -0
- package/dist/renderer/effects.js.map +1 -0
- package/dist/renderer/feature.d.ts +79 -0
- package/dist/renderer/feature.d.ts.map +1 -0
- package/dist/renderer/feature.js +50 -0
- package/dist/renderer/feature.js.map +1 -0
- package/dist/renderer/geometry.d.ts +59 -0
- package/dist/renderer/geometry.d.ts.map +1 -0
- package/dist/renderer/geometry.js +114 -0
- package/dist/renderer/geometry.js.map +1 -0
- package/dist/renderer/index.d.ts +7 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +7 -0
- package/dist/renderer/scene.d.ts +77 -0
- package/dist/renderer/scene.d.ts.map +1 -0
- package/dist/renderer/scene.js +125 -0
- package/dist/renderer/scene.js.map +1 -0
- package/dist/renderer/textures.d.ts +20 -0
- package/dist/renderer/textures.d.ts.map +1 -0
- package/dist/renderer/textures.js +61 -0
- package/dist/renderer/textures.js.map +1 -0
- package/dist/renderer/view.d.ts +139 -0
- package/dist/renderer/view.d.ts.map +1 -0
- package/dist/renderer/view.js +349 -0
- package/dist/renderer/view.js.map +1 -0
- package/dist/rpgjs/shared.d.ts +41 -0
- package/dist/rpgjs/shared.d.ts.map +1 -0
- package/dist/rpgjs/shared.js +77 -0
- package/dist/rpgjs/shared.js.map +1 -0
- package/dist/server/index.d.ts +64 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +205 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/storage.d.ts +16 -0
- package/dist/server/storage.d.ts.map +1 -0
- package/dist/server/storage.js +52 -0
- package/dist/server/storage.js.map +1 -0
- package/dist/skins/default.d.ts +27 -0
- package/dist/skins/default.d.ts.map +1 -0
- package/dist/skins/default.js +144 -0
- package/dist/skins/default.js.map +1 -0
- package/dist/skins/frames.d.ts +19 -0
- package/dist/skins/frames.d.ts.map +1 -0
- package/dist/skins/frames.js +49 -0
- package/dist/skins/frames.js.map +1 -0
- package/dist/skins/game.d.ts +21 -0
- package/dist/skins/game.d.ts.map +1 -0
- package/dist/skins/game.js +21 -0
- package/dist/skins/game.js.map +1 -0
- package/dist/skins/index.d.ts +5 -0
- package/dist/skins/index.d.ts.map +1 -0
- package/dist/skins/index.js +5 -0
- package/dist/skins/types.d.ts +46 -0
- package/dist/skins/types.d.ts.map +1 -0
- package/dist/skins/types.js +12 -0
- package/dist/skins/types.js.map +1 -0
- package/dist/terrain/data.d.ts +10 -0
- package/dist/terrain/data.d.ts.map +1 -0
- package/dist/terrain/data.js +83 -0
- package/dist/terrain/data.js.map +1 -0
- package/dist/terrain/define.d.ts +72 -0
- package/dist/terrain/define.d.ts.map +1 -0
- package/dist/terrain/define.js +91 -0
- package/dist/terrain/define.js.map +1 -0
- package/dist/terrain/editing.d.ts +54 -0
- package/dist/terrain/editing.d.ts.map +1 -0
- package/dist/terrain/editing.js +169 -0
- package/dist/terrain/editing.js.map +1 -0
- package/dist/terrain/index.d.ts +10 -0
- package/dist/terrain/index.d.ts.map +1 -0
- package/dist/terrain/index.js +10 -0
- package/dist/terrain/kernel.d.ts +57 -0
- package/dist/terrain/kernel.d.ts.map +1 -0
- package/dist/terrain/kernel.js +198 -0
- package/dist/terrain/kernel.js.map +1 -0
- package/dist/terrain/mask.d.ts +37 -0
- package/dist/terrain/mask.d.ts.map +1 -0
- package/dist/terrain/mask.js +244 -0
- package/dist/terrain/mask.js.map +1 -0
- package/dist/terrain/patches.d.ts +32 -0
- package/dist/terrain/patches.d.ts.map +1 -0
- package/dist/terrain/patches.js +135 -0
- package/dist/terrain/patches.js.map +1 -0
- package/dist/terrain/rle.d.ts +4 -0
- package/dist/terrain/rle.d.ts.map +1 -0
- package/dist/terrain/rle.js +68 -0
- package/dist/terrain/rle.js.map +1 -0
- package/dist/terrain/scheduler.d.ts +46 -0
- package/dist/terrain/scheduler.d.ts.map +1 -0
- package/dist/terrain/scheduler.js +148 -0
- package/dist/terrain/scheduler.js.map +1 -0
- package/dist/terrain/types.d.ts +110 -0
- package/dist/terrain/types.d.ts.map +1 -0
- package/dist/terrain/types.js +11 -0
- package/dist/terrain/types.js.map +1 -0
- package/dist/terrain-render/backend.d.ts +66 -0
- package/dist/terrain-render/backend.d.ts.map +1 -0
- package/dist/terrain-render/backend.js +96 -0
- package/dist/terrain-render/backend.js.map +1 -0
- package/dist/terrain-render/host.d.ts +21 -0
- package/dist/terrain-render/host.d.ts.map +1 -0
- package/dist/terrain-render/host.js +62 -0
- package/dist/terrain-render/host.js.map +1 -0
- package/dist/terrain-render/index.d.ts +3 -0
- package/dist/terrain-render/index.d.ts.map +1 -0
- package/dist/terrain-render/index.js +3 -0
- package/dist/terrain-render/worker.d.ts +2 -0
- package/dist/terrain-render/worker.d.ts.map +1 -0
- package/dist/terrain-render/worker.js +34 -0
- package/dist/terrain-render/worker.js.map +1 -0
- package/package.json +124 -3
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { grow, intersects } from "./geometry.js";
|
|
2
|
+
import { STAGE_ORDER } from "./scene.js";
|
|
3
|
+
import { createEffectRunner } from "./effects.js";
|
|
4
|
+
import { validateFeatures } from "./feature.js";
|
|
5
|
+
import { createTextureStore, pixiTextureProvider } from "./textures.js";
|
|
6
|
+
import { bindKeyboard } from "../interaction/keyboard.js";
|
|
7
|
+
import { createInteraction } from "../interaction/controller.js";
|
|
8
|
+
import { Application, Container, Rectangle, Sprite, Texture } from "pixi.js";
|
|
9
|
+
//#region src/renderer/view.ts
|
|
10
|
+
var sameFrame = (a, b) => a === b || a !== void 0 && b !== void 0 && a.every((value, index) => value === b[index]);
|
|
11
|
+
function createMapView(options) {
|
|
12
|
+
validateFeatures(options.features);
|
|
13
|
+
const renders = options.features.flatMap((feature) => feature.render ? [feature.render] : []);
|
|
14
|
+
const root = new Container();
|
|
15
|
+
root.label = "map-view";
|
|
16
|
+
const stages = Object.fromEntries(STAGE_ORDER.map((name) => {
|
|
17
|
+
const stage = new Container();
|
|
18
|
+
stage.label = name;
|
|
19
|
+
root.addChild(stage);
|
|
20
|
+
return [name, stage];
|
|
21
|
+
}));
|
|
22
|
+
stages.objects.sortableChildren = true;
|
|
23
|
+
const store = createTextureStore(options.textures ?? pixiTextureProvider);
|
|
24
|
+
const views = /* @__PURE__ */ new Map();
|
|
25
|
+
const warned = /* @__PURE__ */ new Set();
|
|
26
|
+
const pending = /* @__PURE__ */ new Set();
|
|
27
|
+
let viewport = null;
|
|
28
|
+
const viewportListeners = /* @__PURE__ */ new Set();
|
|
29
|
+
let destroyed = false;
|
|
30
|
+
let mounted = null;
|
|
31
|
+
const reportError = (error) => {
|
|
32
|
+
if (options.onError) options.onError(error);
|
|
33
|
+
else if (!warned.has(error.src)) {
|
|
34
|
+
warned.add(error.src);
|
|
35
|
+
console.warn(`[map-builder] ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const track = (promise) => {
|
|
39
|
+
pending.add(promise);
|
|
40
|
+
promise.finally(() => pending.delete(promise));
|
|
41
|
+
};
|
|
42
|
+
const requestedSizes = /* @__PURE__ */ new Set();
|
|
43
|
+
const measure = (src) => {
|
|
44
|
+
const size = store.size(src);
|
|
45
|
+
if (!size && !requestedSizes.has(src)) {
|
|
46
|
+
requestedSizes.add(src);
|
|
47
|
+
track(store.ready(src).then(() => {
|
|
48
|
+
if (!destroyed) resyncAll(options.model.state);
|
|
49
|
+
}, (cause) => reportError({
|
|
50
|
+
code: "asset-load-failed",
|
|
51
|
+
message: `Could not load "${src}"`,
|
|
52
|
+
src,
|
|
53
|
+
cause
|
|
54
|
+
})));
|
|
55
|
+
}
|
|
56
|
+
return size;
|
|
57
|
+
};
|
|
58
|
+
const context = {
|
|
59
|
+
assets: options.assets,
|
|
60
|
+
...options.hooks ? { hooks: options.hooks } : {},
|
|
61
|
+
measure
|
|
62
|
+
};
|
|
63
|
+
const isVisible = (node) => viewport === null || intersects(node.bounds, grow(viewport, options.cullingMargin ?? 64));
|
|
64
|
+
const applyPart = (entry, part) => {
|
|
65
|
+
entry.sprite.position.set(part.x, part.y);
|
|
66
|
+
entry.sprite.width = part.width;
|
|
67
|
+
entry.sprite.height = part.height;
|
|
68
|
+
};
|
|
69
|
+
const loadPart = (nodeKey, entry, part) => {
|
|
70
|
+
const texture = store.get(part.src, part.frame);
|
|
71
|
+
if (texture) {
|
|
72
|
+
entry.sprite.texture = texture;
|
|
73
|
+
entry.sprite.visible = true;
|
|
74
|
+
applyPart(entry, part);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
entry.sprite.visible = false;
|
|
78
|
+
track(store.ready(part.src, part.frame).then((loaded) => {
|
|
79
|
+
const current = views.get(nodeKey)?.sprites.get(part.key);
|
|
80
|
+
if (!current || current !== entry || current.part.src !== part.src || !sameFrame(current.part.frame, part.frame)) return;
|
|
81
|
+
entry.sprite.texture = loaded;
|
|
82
|
+
entry.sprite.visible = true;
|
|
83
|
+
applyPart(entry, current.part);
|
|
84
|
+
}, (cause) => reportError({
|
|
85
|
+
code: "asset-load-failed",
|
|
86
|
+
message: `Could not load "${part.src}"`,
|
|
87
|
+
src: part.src,
|
|
88
|
+
cause
|
|
89
|
+
})));
|
|
90
|
+
};
|
|
91
|
+
const upsert = (owner, node) => {
|
|
92
|
+
let view = views.get(node.key);
|
|
93
|
+
if (!view) {
|
|
94
|
+
const container = new Container();
|
|
95
|
+
container.label = node.key;
|
|
96
|
+
stages[node.stage].addChild(container);
|
|
97
|
+
view = {
|
|
98
|
+
node,
|
|
99
|
+
container,
|
|
100
|
+
sprites: /* @__PURE__ */ new Map(),
|
|
101
|
+
owner
|
|
102
|
+
};
|
|
103
|
+
views.set(node.key, view);
|
|
104
|
+
} else if (view.node.stage !== node.stage) stages[node.stage].addChild(view.container);
|
|
105
|
+
view.node = node;
|
|
106
|
+
view.container.zIndex = node.sortKey;
|
|
107
|
+
view.container.visible = isVisible(node);
|
|
108
|
+
if (options.draw === false) return;
|
|
109
|
+
const keep = new Set(node.parts.map((part) => part.key));
|
|
110
|
+
for (const [key, entry] of view.sprites) {
|
|
111
|
+
if (keep.has(key)) continue;
|
|
112
|
+
entry.sprite.destroy();
|
|
113
|
+
view.sprites.delete(key);
|
|
114
|
+
}
|
|
115
|
+
for (const part of node.parts) {
|
|
116
|
+
let entry = view.sprites.get(part.key);
|
|
117
|
+
if (!entry) {
|
|
118
|
+
const sprite = new Sprite(Texture.EMPTY);
|
|
119
|
+
sprite.visible = false;
|
|
120
|
+
view.container.addChild(sprite);
|
|
121
|
+
entry = {
|
|
122
|
+
sprite,
|
|
123
|
+
part
|
|
124
|
+
};
|
|
125
|
+
view.sprites.set(part.key, entry);
|
|
126
|
+
loadPart(node.key, entry, part);
|
|
127
|
+
} else if (entry.part.src !== part.src || !sameFrame(entry.part.frame, part.frame)) {
|
|
128
|
+
entry.part = part;
|
|
129
|
+
loadPart(node.key, entry, part);
|
|
130
|
+
} else entry.part = part;
|
|
131
|
+
applyPart(entry, part);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const remove = (key) => {
|
|
135
|
+
const view = views.get(key);
|
|
136
|
+
if (!view) return;
|
|
137
|
+
runner.targetRemoved(key);
|
|
138
|
+
view.container.destroy({ children: true });
|
|
139
|
+
views.delete(key);
|
|
140
|
+
};
|
|
141
|
+
const refresh = (owner, id, state) => {
|
|
142
|
+
const node = owner.describeOne(state, id, context);
|
|
143
|
+
if (node) upsert(owner, node);
|
|
144
|
+
else for (const [key, view] of views) if (view.owner === owner && view.node.id === id) remove(key);
|
|
145
|
+
};
|
|
146
|
+
const resync = (owner, state) => {
|
|
147
|
+
const nodes = owner.describeAll(state, context);
|
|
148
|
+
const live = new Set(nodes.map((node) => node.key));
|
|
149
|
+
for (const [key, view] of views) if (view.owner === owner && !live.has(key)) remove(key);
|
|
150
|
+
for (const node of nodes) upsert(owner, node);
|
|
151
|
+
};
|
|
152
|
+
function resyncAll(state) {
|
|
153
|
+
for (const owner of renders) resync(owner, state);
|
|
154
|
+
}
|
|
155
|
+
const targetKey = (target) => typeof target === "string" ? `elements/${target}` : `${target.type}s/${target.id}`;
|
|
156
|
+
const runner = createEffectRunner({
|
|
157
|
+
exists: (key) => views.has(key),
|
|
158
|
+
apply(key, output) {
|
|
159
|
+
const view = views.get(key);
|
|
160
|
+
if (!view) return;
|
|
161
|
+
view.container.position.set(output?.offsetX ?? 0, output?.offsetY ?? 0);
|
|
162
|
+
view.container.alpha = output?.alpha ?? 1;
|
|
163
|
+
},
|
|
164
|
+
onWarning: (message) => console.warn(`[map-builder] ${message}`)
|
|
165
|
+
});
|
|
166
|
+
for (const [name, definition] of Object.entries(options.effects ?? {})) runner.register(name, definition);
|
|
167
|
+
const unsubscribe = options.model.subscribe((change) => {
|
|
168
|
+
if (destroyed) return;
|
|
169
|
+
if (change.reset) {
|
|
170
|
+
resyncAll(change.state);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const touched = /* @__PURE__ */ new Map();
|
|
174
|
+
for (const patch of change.patches) for (const owner of renders) {
|
|
175
|
+
const affected = owner.affected(patch);
|
|
176
|
+
if (affected === "none") continue;
|
|
177
|
+
const current = touched.get(owner);
|
|
178
|
+
if (affected === "all") touched.set(owner, "all");
|
|
179
|
+
else if (current !== "all") {
|
|
180
|
+
const ids = current ?? /* @__PURE__ */ new Set();
|
|
181
|
+
for (const id of affected) ids.add(id);
|
|
182
|
+
touched.set(owner, ids);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
for (const [owner, ids] of touched) if (ids === "all") resync(owner, change.state);
|
|
186
|
+
else for (const id of ids) refresh(owner, id, change.state);
|
|
187
|
+
});
|
|
188
|
+
resyncAll(options.model.state);
|
|
189
|
+
const sceneNodes = () => [...views.values()].map((view) => view.node);
|
|
190
|
+
const interaction = createInteraction({
|
|
191
|
+
model: options.model,
|
|
192
|
+
assets: options.assets,
|
|
193
|
+
describe: context,
|
|
194
|
+
nodes: sceneNodes,
|
|
195
|
+
...options.actor ? { actor: options.actor } : {},
|
|
196
|
+
...options.modes ? { modes: options.modes } : {}
|
|
197
|
+
});
|
|
198
|
+
const tickListeners = /* @__PURE__ */ new Set();
|
|
199
|
+
const advance = (deltaMs) => {
|
|
200
|
+
runner.tick(deltaMs);
|
|
201
|
+
for (const listener of [...tickListeners]) listener(deltaMs);
|
|
202
|
+
};
|
|
203
|
+
if (options.interactive ?? options.features.some((feature) => feature.setup !== void 0)) {
|
|
204
|
+
const [width, height] = options.model.state.size;
|
|
205
|
+
root.eventMode = "static";
|
|
206
|
+
root.hitArea = new Rectangle(0, 0, width, height);
|
|
207
|
+
const toInput = (event) => {
|
|
208
|
+
const point = event.getLocalPosition(root);
|
|
209
|
+
return {
|
|
210
|
+
x: point.x,
|
|
211
|
+
y: point.y,
|
|
212
|
+
button: event.button ?? 0,
|
|
213
|
+
modifiers: {
|
|
214
|
+
shift: !!event.shiftKey,
|
|
215
|
+
ctrl: !!event.ctrlKey,
|
|
216
|
+
alt: !!event.altKey,
|
|
217
|
+
meta: !!event.metaKey
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
root.on("pointerdown", (event) => interaction.feedPointer("down", toInput(event)));
|
|
222
|
+
root.on("globalpointermove", (event) => interaction.feedPointer("move", toInput(event)));
|
|
223
|
+
root.on("pointerup", (event) => interaction.feedPointer("up", toInput(event)));
|
|
224
|
+
root.on("pointerupoutside", (event) => interaction.feedPointer("up", toInput(event)));
|
|
225
|
+
}
|
|
226
|
+
const instances = [];
|
|
227
|
+
const view = {
|
|
228
|
+
root,
|
|
229
|
+
stages,
|
|
230
|
+
interaction,
|
|
231
|
+
submit: (intent, submitOptions) => interaction.submit(intent, submitOptions),
|
|
232
|
+
effects: {
|
|
233
|
+
play: (target, name, params) => runner.play(targetKey(target), name, params),
|
|
234
|
+
register: (name, definition) => runner.register(name, definition),
|
|
235
|
+
clear: (target) => runner.clear(target === void 0 ? void 0 : targetKey(target)),
|
|
236
|
+
get running() {
|
|
237
|
+
return runner.running;
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
setViewport(next) {
|
|
241
|
+
viewport = next;
|
|
242
|
+
for (const listener of [...viewportListeners]) listener(next);
|
|
243
|
+
for (const nodeView of views.values()) nodeView.container.visible = isVisible(nodeView.node);
|
|
244
|
+
},
|
|
245
|
+
tick: advance,
|
|
246
|
+
async mount(host, mountOptions = {}) {
|
|
247
|
+
if (mounted) throw new Error("The view is already mounted");
|
|
248
|
+
const app = mountOptions.createApplication?.() ?? new Application();
|
|
249
|
+
await app.init({
|
|
250
|
+
resizeTo: host,
|
|
251
|
+
backgroundAlpha: mountOptions.background === void 0 ? 0 : 1,
|
|
252
|
+
...mountOptions.background !== void 0 ? { background: mountOptions.background } : {},
|
|
253
|
+
antialias: mountOptions.antialias ?? false,
|
|
254
|
+
autoDensity: true
|
|
255
|
+
});
|
|
256
|
+
if (destroyed) {
|
|
257
|
+
app.destroy(true);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
host.appendChild(app.canvas);
|
|
261
|
+
app.stage.addChild(root);
|
|
262
|
+
const tick = (ticker) => advance(ticker.deltaMS);
|
|
263
|
+
app.ticker.add(tick);
|
|
264
|
+
const keyboardTarget = mountOptions.keyboard === true ? globalThis : mountOptions.keyboard || void 0;
|
|
265
|
+
mounted = {
|
|
266
|
+
app,
|
|
267
|
+
tick,
|
|
268
|
+
unbindKeyboard: keyboardTarget ? bindKeyboard(interaction, keyboardTarget) : void 0
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
async whenIdle() {
|
|
272
|
+
while (pending.size > 0) await Promise.allSettled([...pending]);
|
|
273
|
+
},
|
|
274
|
+
describe: sceneNodes,
|
|
275
|
+
destroy() {
|
|
276
|
+
if (destroyed) return;
|
|
277
|
+
destroyed = true;
|
|
278
|
+
unsubscribe();
|
|
279
|
+
for (const instance of instances) instance.dispose?.();
|
|
280
|
+
interaction.destroy();
|
|
281
|
+
runner.clear();
|
|
282
|
+
tickListeners.clear();
|
|
283
|
+
if (mounted) {
|
|
284
|
+
mounted.unbindKeyboard?.();
|
|
285
|
+
mounted.app.ticker.remove(mounted.tick);
|
|
286
|
+
mounted.app.destroy(true);
|
|
287
|
+
mounted = null;
|
|
288
|
+
}
|
|
289
|
+
root.destroy({ children: true });
|
|
290
|
+
views.clear();
|
|
291
|
+
store.destroy();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
const host = {
|
|
295
|
+
model: options.model,
|
|
296
|
+
assets: options.assets,
|
|
297
|
+
interaction,
|
|
298
|
+
stages,
|
|
299
|
+
textures: store,
|
|
300
|
+
describe: sceneNodes,
|
|
301
|
+
onTick(listener) {
|
|
302
|
+
tickListeners.add(listener);
|
|
303
|
+
return () => tickListeners.delete(listener);
|
|
304
|
+
},
|
|
305
|
+
onViewport(listener) {
|
|
306
|
+
viewportListeners.add(listener);
|
|
307
|
+
listener(viewport);
|
|
308
|
+
return () => viewportListeners.delete(listener);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
for (const feature of options.features) {
|
|
312
|
+
const instance = feature.setup?.(host);
|
|
313
|
+
if (!instance) continue;
|
|
314
|
+
instances.push(instance);
|
|
315
|
+
if (instance.api) Object.defineProperty(view, feature.id, {
|
|
316
|
+
value: instance.api,
|
|
317
|
+
enumerable: true
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return view;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Hosts a view in an existing PixiJS container, typically the `componentInstance`
|
|
324
|
+
* of a CanvasEngine component. Returns the function that detaches it again, which
|
|
325
|
+
* is what the `mount` hook of CanvasEngine expects to return.
|
|
326
|
+
*
|
|
327
|
+
* ```ce
|
|
328
|
+
* <Container />
|
|
329
|
+
*
|
|
330
|
+
* <script>
|
|
331
|
+
* import { mount, tick } from "canvasengine";
|
|
332
|
+
* import { attachMapView } from "@rpgjs/map-builder/renderer";
|
|
333
|
+
*
|
|
334
|
+
* mount((element) => attachMapView(element.componentInstance, view));
|
|
335
|
+
* tick(({ deltaTime }) => view.tick(deltaTime));
|
|
336
|
+
* <\/script>
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
function attachMapView(container, view) {
|
|
340
|
+
if (view.root.parent && view.root.parent !== container) view.root.parent.removeChild(view.root);
|
|
341
|
+
container.addChild(view.root);
|
|
342
|
+
return () => {
|
|
343
|
+
if (view.root.parent === container) container.removeChild(view.root);
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
export { attachMapView, createMapView };
|
|
348
|
+
|
|
349
|
+
//# sourceMappingURL=view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.js","names":[],"sources":["../../src/renderer/view.ts"],"sourcesContent":["import { Application, Container, Rectangle, Sprite, Texture } from \"pixi.js\";\nimport type { AssetResolver } from \"../model/assets\";\nimport type { MapState } from \"../model/definition\";\nimport type { ApplyResult, MaybePromise, Unsubscribe } from \"../model/result\";\nimport { bindKeyboard, type KeyTarget } from \"../interaction/keyboard\";\nimport { createInteraction, type InteractionController, type ModelApi } from \"../interaction/controller\";\nimport type { PointerInput } from \"../interaction/input\";\nimport {\n createEffectRunner,\n type EffectDefinition,\n type EffectHandle,\n type EffectOutput,\n type EffectParams,\n} from \"./effects\";\nimport { validateFeatures, type FeatureApis, type FeatureHost, type FeatureRender, type MapFeature } from \"./feature\";\nimport { grow, intersects, type Rect } from \"./geometry\";\nimport {\n STAGE_ORDER,\n type DescribeContext,\n type MapViewHooks,\n type SceneNode,\n type ScenePart,\n type StageName,\n} from \"./scene\";\nimport { createTextureStore, pixiTextureProvider, type TextureProvider } from \"./textures\";\n\nexport type MapViewErrorCode = \"asset-load-failed\";\n\nexport interface MapViewError {\n readonly code: MapViewErrorCode;\n readonly message: string;\n readonly src: string;\n readonly cause: unknown;\n}\n\n/** The part of a PixiJS `Application` the view uses to mount itself. */\nexport interface PixiApplicationLike {\n init(options: Record<string, unknown>): Promise<void>;\n readonly canvas: unknown;\n readonly stage: Container;\n readonly ticker: { add(callback: (ticker: { deltaMS: number }) => void): unknown; remove(callback: (ticker: { deltaMS: number }) => void): unknown };\n destroy(removeView?: boolean, options?: unknown): void;\n}\n\nexport interface MountOptions {\n /**\n * Listen to the keyboard (Escape, Delete…) and give it to the interaction layer.\n * `true` listens on the page, or give an event target. Off by default.\n */\n readonly keyboard?: boolean | KeyTarget;\n /** CSS-style colour of the canvas background. Transparent by default. */\n readonly background?: string | number;\n /** Anti-aliasing is off by default: it blurs pixel art. */\n readonly antialias?: boolean;\n /** Replaces `new Application()`. */\n readonly createApplication?: () => PixiApplicationLike;\n}\n\nexport interface MapViewOptions<\n F extends readonly MapFeature<string, object>[] = readonly MapFeature<string, object>[],\n R extends MaybePromise<ApplyResult> = MaybePromise<ApplyResult>,\n> {\n /** Any `MapModel` fits, whatever its committer. */\n readonly model: ModelApi<R>;\n readonly assets: AssetResolver;\n /** What the view draws and how. Nothing is drawn without features. */\n readonly features: F;\n /** Who is acting, handed to the steps of the pipelines. Whatever the game decides. */\n readonly actor?: () => unknown;\n /** Modes active at the start. Tools belong to modes (`play`, `edit`, `inspect`). Defaults to `[\"edit\"]`. */\n readonly modes?: readonly string[];\n /**\n * Turn pointer events on the map into interaction. Defaults to true as soon as a\n * feature has something to interact with, false otherwise: a view that only\n * draws never gets in the way of the clicks of a game.\n */\n readonly interactive?: boolean;\n /** How images are loaded. Defaults to PixiJS `Assets`. */\n readonly textures?: TextureProvider;\n readonly hooks?: MapViewHooks;\n /** Extra effects, by name, next to the built-in `shake` and `blink`. */\n readonly effects?: Readonly<Record<string, EffectDefinition>>;\n /**\n * Draw the sprites of the elements and events. Defaults to true. With `false` the view only keeps them as\n * what can be picked and selected (their bounds and hitboxes), so that a game that draws the map itself can\n * use the tools and the selection of the map builder without drawing everything twice. Nothing is loaded.\n */\n readonly draw?: boolean;\n /** Margin, in pixels, added around the viewport before something is hidden. Defaults to 64. */\n readonly cullingMargin?: number;\n /** Receives non-fatal problems. Defaults to `console.warn`, once per image. */\n readonly onError?: (error: MapViewError) => void;\n}\n\n/** An element or event, as an effect target. A plain string is an element id. */\nexport type EffectTarget = string | { readonly type: \"element\" | \"event\"; readonly id: string };\n\nexport interface MapViewEffects {\n /**\n * Plays a short visual effect. It does not touch the model, so it is neither\n * persisted nor undoable. It never throws: see the status of `handle.finished`.\n */\n play(target: EffectTarget, name: string, params?: EffectParams): EffectHandle;\n register(name: string, definition: EffectDefinition): void;\n clear(target?: EffectTarget): void;\n readonly running: number;\n}\n\nexport interface MapView<R extends MaybePromise<ApplyResult> = MaybePromise<ApplyResult>> {\n /** Add this to any PixiJS stage (a `Application`, a CanvasEngine scene). */\n readonly root: Container;\n /** The headless layer: modes, tools, selection, pipelines. Observable through `interaction.state`. */\n readonly interaction: InteractionController<R>;\n /** Asks for something to be done: runs the pipeline, then applies. See {@link InteractionController.submit}. */\n readonly submit: InteractionController<R>[\"submit\"];\n /** One container per stage, back to front. */\n readonly stages: Readonly<Record<StageName, Container>>;\n readonly effects: MapViewEffects;\n /**\n * Restricts drawing to a world rectangle (usually the camera view). What is\n * outside, margin included, is hidden. `null` shows everything.\n */\n setViewport(viewport: Rect | null): void;\n /** Advances time. `mount` calls it every frame; call it yourself when you host `root`. */\n tick(deltaMs: number): void;\n /** Creates a PixiJS application in `host` and draws into it. */\n mount(host: { appendChild(node: never): unknown }, options?: MountOptions): Promise<void>;\n /** Resolves once every image requested so far has loaded (or failed). */\n whenIdle(): Promise<void>;\n /** The nodes currently in the scene, as plain data. Useful for tests and tools. */\n describe(): readonly SceneNode[];\n destroy(): void;\n}\n\ninterface PartSprite {\n readonly sprite: Sprite;\n part: ScenePart;\n}\n\ninterface NodeView {\n node: SceneNode;\n readonly container: Container;\n readonly sprites: Map<string, PartSprite>;\n readonly owner: FeatureRender;\n}\n\nconst sameFrame = (a: ScenePart[\"frame\"], b: ScenePart[\"frame\"]): boolean =>\n a === b || (a !== undefined && b !== undefined && a.every((value, index) => value === b[index]));\n\nexport function createMapView<\n const F extends readonly MapFeature<string, object>[],\n R extends MaybePromise<ApplyResult> = ApplyResult,\n>(options: MapViewOptions<F, R>): MapView<R> & FeatureApis<F> {\n validateFeatures(options.features);\n const renders = options.features.flatMap((feature) => (feature.render ? [feature.render] : []));\n\n const root = new Container();\n root.label = \"map-view\";\n const stages = Object.fromEntries(\n STAGE_ORDER.map((name) => {\n const stage = new Container();\n stage.label = name;\n root.addChild(stage);\n return [name, stage];\n }),\n ) as Record<StageName, Container>;\n // Elements are drawn in depth order: the higher the sort key, the closer to the viewer.\n stages.objects.sortableChildren = true;\n\n const store = createTextureStore(options.textures ?? pixiTextureProvider);\n const views = new Map<string, NodeView>();\n const warned = new Set<string>();\n const pending = new Set<Promise<unknown>>();\n let viewport: Rect | null = null;\n const viewportListeners = new Set<(viewport: Rect | null) => void>();\n let destroyed = false;\n let mounted: { app: PixiApplicationLike; tick: (ticker: { deltaMS: number }) => void; unbindKeyboard: (() => void) | undefined } | null = null;\n\n const reportError = (error: MapViewError): void => {\n if (options.onError) options.onError(error);\n else if (!warned.has(error.src)) {\n warned.add(error.src);\n console.warn(`[map-builder] ${error.message}`);\n }\n };\n\n const track = (promise: Promise<unknown>): void => {\n pending.add(promise);\n void promise.finally(() => pending.delete(promise));\n };\n\n // Sizes of images that no asset declares are read from the loaded image, then the scene is described again.\n const requestedSizes = new Set<string>();\n const measure = (src: string): readonly [number, number] | undefined => {\n const size = store.size(src);\n if (!size && !requestedSizes.has(src)) {\n requestedSizes.add(src);\n track(\n store.ready(src).then(\n () => {\n if (!destroyed) resyncAll(options.model.state);\n },\n (cause) => reportError({ code: \"asset-load-failed\", message: `Could not load \"${src}\"`, src, cause }),\n ),\n );\n }\n return size;\n };\n\n const context: DescribeContext = {\n assets: options.assets,\n ...(options.hooks ? { hooks: options.hooks } : {}),\n measure,\n };\n\n const isVisible = (node: SceneNode): boolean =>\n viewport === null || intersects(node.bounds, grow(viewport, options.cullingMargin ?? 64));\n\n const applyPart = (entry: PartSprite, part: ScenePart): void => {\n entry.sprite.position.set(part.x, part.y);\n entry.sprite.width = part.width;\n entry.sprite.height = part.height;\n };\n\n const loadPart = (nodeKey: string, entry: PartSprite, part: ScenePart): void => {\n const texture = store.get(part.src, part.frame);\n if (texture) {\n entry.sprite.texture = texture;\n entry.sprite.visible = true;\n applyPart(entry, part);\n return;\n }\n entry.sprite.visible = false;\n track(\n store.ready(part.src, part.frame).then(\n (loaded) => {\n const view = views.get(nodeKey);\n const current = view?.sprites.get(part.key);\n if (!current || current !== entry || current.part.src !== part.src || !sameFrame(current.part.frame, part.frame)) return;\n entry.sprite.texture = loaded;\n entry.sprite.visible = true;\n applyPart(entry, current.part);\n },\n (cause) => reportError({ code: \"asset-load-failed\", message: `Could not load \"${part.src}\"`, src: part.src, cause }),\n ),\n );\n };\n\n const upsert = (owner: FeatureRender, node: SceneNode): void => {\n let view = views.get(node.key);\n if (!view) {\n const container = new Container();\n container.label = node.key;\n stages[node.stage].addChild(container);\n view = { node, container, sprites: new Map(), owner };\n views.set(node.key, view);\n } else if (view.node.stage !== node.stage) {\n stages[node.stage].addChild(view.container);\n }\n\n view.node = node;\n view.container.zIndex = node.sortKey;\n view.container.visible = isVisible(node);\n if (options.draw === false) return;\n\n const keep = new Set(node.parts.map((part) => part.key));\n for (const [key, entry] of view.sprites) {\n if (keep.has(key)) continue;\n entry.sprite.destroy();\n view.sprites.delete(key);\n }\n for (const part of node.parts) {\n let entry = view.sprites.get(part.key);\n if (!entry) {\n const sprite = new Sprite(Texture.EMPTY);\n sprite.visible = false;\n view.container.addChild(sprite);\n entry = { sprite, part };\n view.sprites.set(part.key, entry);\n loadPart(node.key, entry, part);\n } else if (entry.part.src !== part.src || !sameFrame(entry.part.frame, part.frame)) {\n entry.part = part;\n loadPart(node.key, entry, part);\n } else {\n entry.part = part;\n }\n applyPart(entry, part);\n }\n };\n\n const remove = (key: string): void => {\n const view = views.get(key);\n if (!view) return;\n runner.targetRemoved(key);\n view.container.destroy({ children: true });\n views.delete(key);\n };\n\n const refresh = (owner: FeatureRender, id: string, state: MapState): void => {\n const node = owner.describeOne(state, id, context);\n if (node) upsert(owner, node);\n else for (const [key, view] of views) if (view.owner === owner && view.node.id === id) remove(key);\n };\n\n const resync = (owner: FeatureRender, state: MapState): void => {\n const nodes = owner.describeAll(state, context);\n const live = new Set(nodes.map((node) => node.key));\n for (const [key, view] of views) if (view.owner === owner && !live.has(key)) remove(key);\n for (const node of nodes) upsert(owner, node);\n };\n\n function resyncAll(state: MapState): void {\n for (const owner of renders) resync(owner, state);\n }\n\n const targetKey = (target: EffectTarget): string =>\n typeof target === \"string\" ? `elements/${target}` : `${target.type}s/${target.id}`;\n\n const runner = createEffectRunner({\n exists: (key) => views.has(key),\n apply(key, output: EffectOutput | null) {\n const view = views.get(key);\n if (!view) return;\n view.container.position.set(output?.offsetX ?? 0, output?.offsetY ?? 0);\n view.container.alpha = output?.alpha ?? 1;\n },\n onWarning: (message) => console.warn(`[map-builder] ${message}`),\n });\n for (const [name, definition] of Object.entries(options.effects ?? {})) runner.register(name, definition);\n\n const unsubscribe = options.model.subscribe((change) => {\n if (destroyed) return;\n if (change.reset) {\n // A snapshot replaced the whole state: nothing derived from the old one can be trusted.\n resyncAll(change.state);\n return;\n }\n const touched = new Map<FeatureRender, Set<string> | \"all\">();\n for (const patch of change.patches) {\n for (const owner of renders) {\n const affected = owner.affected(patch);\n if (affected === \"none\") continue;\n const current = touched.get(owner);\n if (affected === \"all\") touched.set(owner, \"all\");\n else if (current !== \"all\") {\n const ids = current ?? new Set<string>();\n for (const id of affected) ids.add(id);\n touched.set(owner, ids);\n }\n }\n }\n for (const [owner, ids] of touched) {\n if (ids === \"all\") resync(owner, change.state);\n else for (const id of ids) refresh(owner, id, change.state);\n }\n });\n\n resyncAll(options.model.state);\n\n const sceneNodes = (): readonly SceneNode[] => [...views.values()].map((view) => view.node);\n\n const interaction = createInteraction<R>({\n model: options.model,\n assets: options.assets,\n describe: context,\n nodes: sceneNodes,\n ...(options.actor ? { actor: options.actor } : {}),\n ...(options.modes ? { modes: options.modes } : {}),\n });\n\n const tickListeners = new Set<(deltaMs: number) => void>();\n const advance = (deltaMs: number): void => {\n runner.tick(deltaMs);\n for (const listener of [...tickListeners]) listener(deltaMs);\n };\n\n // Pointer events on the map become interaction, but only when something is there to interact with:\n // a view that just draws must never get in the way of the clicks of a game.\n const interactive = options.interactive ?? options.features.some((feature) => feature.setup !== undefined);\n if (interactive) {\n const [width, height] = options.model.state.size;\n root.eventMode = \"static\";\n root.hitArea = new Rectangle(0, 0, width, height);\n const toInput = (event: PointerEventLike): PointerInput => {\n const point = event.getLocalPosition(root);\n return {\n x: point.x,\n y: point.y,\n button: event.button ?? 0,\n modifiers: { shift: !!event.shiftKey, ctrl: !!event.ctrlKey, alt: !!event.altKey, meta: !!event.metaKey },\n };\n };\n root.on(\"pointerdown\", (event: PointerEventLike) => interaction.feedPointer(\"down\", toInput(event)));\n // `globalpointermove` keeps a drag going when the pointer leaves the map.\n root.on(\"globalpointermove\", (event: PointerEventLike) => interaction.feedPointer(\"move\", toInput(event)));\n root.on(\"pointerup\", (event: PointerEventLike) => interaction.feedPointer(\"up\", toInput(event)));\n root.on(\"pointerupoutside\", (event: PointerEventLike) => interaction.feedPointer(\"up\", toInput(event)));\n }\n\n const instances: Array<{ dispose?(): void }> = [];\n\n const view: MapView<R> = {\n root,\n stages,\n interaction,\n submit: (intent, submitOptions) => interaction.submit(intent, submitOptions),\n effects: {\n play: (target, name, params) => runner.play(targetKey(target), name, params),\n register: (name, definition) => runner.register(name, definition),\n clear: (target) => runner.clear(target === undefined ? undefined : targetKey(target)),\n get running() {\n return runner.running;\n },\n },\n setViewport(next) {\n viewport = next;\n for (const listener of [...viewportListeners]) listener(next);\n for (const nodeView of views.values()) nodeView.container.visible = isVisible(nodeView.node);\n },\n tick: advance,\n async mount(host, mountOptions = {}) {\n if (mounted) throw new Error(\"The view is already mounted\");\n const app = mountOptions.createApplication?.() ?? (new Application() as unknown as PixiApplicationLike);\n await app.init({\n resizeTo: host,\n backgroundAlpha: mountOptions.background === undefined ? 0 : 1,\n ...(mountOptions.background !== undefined ? { background: mountOptions.background } : {}),\n antialias: mountOptions.antialias ?? false,\n autoDensity: true,\n });\n if (destroyed) {\n app.destroy(true);\n return;\n }\n host.appendChild(app.canvas as never);\n app.stage.addChild(root);\n const tick = (ticker: { deltaMS: number }): void => advance(ticker.deltaMS);\n app.ticker.add(tick);\n const keyboardTarget = mountOptions.keyboard === true ? (globalThis as unknown as KeyTarget) : mountOptions.keyboard || undefined;\n mounted = { app, tick, unbindKeyboard: keyboardTarget ? bindKeyboard(interaction, keyboardTarget) : undefined };\n },\n async whenIdle() {\n while (pending.size > 0) await Promise.allSettled([...pending]);\n },\n describe: sceneNodes,\n destroy() {\n if (destroyed) return;\n destroyed = true;\n unsubscribe();\n for (const instance of instances) instance.dispose?.();\n interaction.destroy();\n runner.clear();\n tickListeners.clear();\n if (mounted) {\n mounted.unbindKeyboard?.();\n mounted.app.ticker.remove(mounted.tick);\n mounted.app.destroy(true);\n mounted = null;\n }\n root.destroy({ children: true });\n views.clear();\n store.destroy();\n },\n };\n\n const host: FeatureHost<R> = {\n model: options.model,\n assets: options.assets,\n interaction,\n stages,\n textures: store,\n describe: sceneNodes,\n onTick(listener) {\n tickListeners.add(listener);\n return () => tickListeners.delete(listener);\n },\n onViewport(listener) {\n viewportListeners.add(listener);\n listener(viewport);\n return () => viewportListeners.delete(listener);\n },\n };\n // Features are set up in the order they are listed, after the model has already been drawn.\n for (const feature of options.features) {\n const instance = feature.setup?.(host as unknown as FeatureHost);\n if (!instance) continue;\n instances.push(instance);\n if (instance.api) Object.defineProperty(view, feature.id, { value: instance.api, enumerable: true });\n }\n\n return view as MapView<R> & FeatureApis<F>;\n}\n\n/** The part of a PixiJS federated pointer event that is used. */\ninterface PointerEventLike {\n readonly button?: number;\n readonly shiftKey?: boolean;\n readonly ctrlKey?: boolean;\n readonly altKey?: boolean;\n readonly metaKey?: boolean;\n getLocalPosition(target: Container): { x: number; y: number };\n}\n\n/**\n * Hosts a view in an existing PixiJS container, typically the `componentInstance`\n * of a CanvasEngine component. Returns the function that detaches it again, which\n * is what the `mount` hook of CanvasEngine expects to return.\n *\n * ```ce\n * <Container />\n *\n * <script>\n * import { mount, tick } from \"canvasengine\";\n * import { attachMapView } from \"@rpgjs/map-builder/renderer\";\n *\n * mount((element) => attachMapView(element.componentInstance, view));\n * tick(({ deltaTime }) => view.tick(deltaTime));\n * </script>\n * ```\n */\nexport function attachMapView(container: Container, view: Pick<MapView, \"root\">): () => void {\n if (view.root.parent && view.root.parent !== container) view.root.parent.removeChild(view.root);\n container.addChild(view.root);\n return () => {\n if (view.root.parent === container) container.removeChild(view.root);\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MapState } from '../model/definition';
|
|
2
|
+
import { MapPatch } from '../model/patches';
|
|
3
|
+
import { DescribeContext } from '../renderer/scene';
|
|
4
|
+
/** The one channel (a socket event) all the messages of the map builder travel on. */
|
|
5
|
+
export declare const MAP_BUILDER_CHANNEL = "map-builder";
|
|
6
|
+
/** A message with the map it is about: a player who changes map must never apply the old map's changes to the new one. */
|
|
7
|
+
export interface MapEnvelope {
|
|
8
|
+
readonly map: string;
|
|
9
|
+
readonly message: unknown;
|
|
10
|
+
}
|
|
11
|
+
export declare const wrap: (map: string, message: unknown) => MapEnvelope;
|
|
12
|
+
export declare function unwrap(raw: unknown): MapEnvelope | undefined;
|
|
13
|
+
export interface HitboxRect {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly x: number;
|
|
16
|
+
readonly y: number;
|
|
17
|
+
readonly width: number;
|
|
18
|
+
readonly height: number;
|
|
19
|
+
}
|
|
20
|
+
/** What both `RpgMap` (server) and the client scene offer for static colliders. */
|
|
21
|
+
export interface StaticHitboxHost {
|
|
22
|
+
replaceStreamedStaticHitboxes(namespace: string, hitboxes: HitboxRect[]): void;
|
|
23
|
+
clearStreamedStaticHitboxes(namespace: string): void;
|
|
24
|
+
}
|
|
25
|
+
/** One namespace per element: changing a tree only rebuilds the collider of that tree. */
|
|
26
|
+
export declare const elementNamespace: (id: string) => string;
|
|
27
|
+
/** The collider of an element: the hitbox the renderer and the pipelines already use, in world pixels. */
|
|
28
|
+
export declare function hitboxOf(state: MapState, id: string, context: DescribeContext): HitboxRect | undefined;
|
|
29
|
+
/** Ids of the elements whose collider a patch may have changed, or `"all"`. */
|
|
30
|
+
export declare function changedElementIds(patches: readonly MapPatch[]): ReadonlySet<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Keeps the static colliders of a map in step with its elements, one element at a time.
|
|
33
|
+
* `"all"` rebuilds every collider, and removes those of elements that are gone: what
|
|
34
|
+
* to do when the physics world was reset.
|
|
35
|
+
*/
|
|
36
|
+
export declare function syncElementHitboxes(host: StaticHitboxHost, state: MapState, ids: ReadonlySet<string> | "all", context: DescribeContext): void;
|
|
37
|
+
/** Forgets what was applied: the physics world was wiped, so nothing is there any more. */
|
|
38
|
+
export declare const forgetHitboxes: (host: object) => void;
|
|
39
|
+
/** Removes every collider this module applied to a host, e.g. when the player leaves the map. */
|
|
40
|
+
export declare function clearHitboxes(host: StaticHitboxHost): void;
|
|
41
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/rpgjs/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE1E,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAEjD,0HAA0H;AAC1H,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,IAAI,GAAI,KAAK,MAAM,EAAE,SAAS,OAAO,KAAG,WAAiC,CAAC;AAEvF,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CAI5D;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAC/E,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACtD;AAID,0FAA0F;AAC1F,eAAO,MAAM,gBAAgB,GAAI,IAAI,MAAM,KAAG,MAA8B,CAAC;AAE7E,0GAA0G;AAC1G,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,UAAU,GAAG,SAAS,CAItG;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAiBnF;AAID;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,KAAK,EAChC,OAAO,EAAE,eAAe,GACvB,IAAI,CAaN;AAED,2FAA2F;AAC3F,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,IAAiC,CAAC;AAEhF,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAG1D"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describeElement } from "../renderer/scene.js";
|
|
2
|
+
//#region src/rpgjs/shared.ts
|
|
3
|
+
/** The one channel (a socket event) all the messages of the map builder travel on. */
|
|
4
|
+
var MAP_BUILDER_CHANNEL = "map-builder";
|
|
5
|
+
var wrap = (map, message) => ({
|
|
6
|
+
map,
|
|
7
|
+
message
|
|
8
|
+
});
|
|
9
|
+
function unwrap(raw) {
|
|
10
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
11
|
+
const { map, message } = raw;
|
|
12
|
+
return typeof map === "string" && message !== void 0 ? {
|
|
13
|
+
map,
|
|
14
|
+
message
|
|
15
|
+
} : void 0;
|
|
16
|
+
}
|
|
17
|
+
var NAMESPACE = "map-builder";
|
|
18
|
+
/** One namespace per element: changing a tree only rebuilds the collider of that tree. */
|
|
19
|
+
var elementNamespace = (id) => `${NAMESPACE}/${id}`;
|
|
20
|
+
/** The collider of an element: the hitbox the renderer and the pipelines already use, in world pixels. */
|
|
21
|
+
function hitboxOf(state, id, context) {
|
|
22
|
+
const element = state.elements.find((entry) => entry.id === id);
|
|
23
|
+
const hitbox = element ? describeElement(element, context)?.hitbox : void 0;
|
|
24
|
+
return hitbox ? {
|
|
25
|
+
id,
|
|
26
|
+
x: hitbox.x,
|
|
27
|
+
y: hitbox.y,
|
|
28
|
+
width: hitbox.width,
|
|
29
|
+
height: hitbox.height
|
|
30
|
+
} : void 0;
|
|
31
|
+
}
|
|
32
|
+
/** Ids of the elements whose collider a patch may have changed, or `"all"`. */
|
|
33
|
+
function changedElementIds(patches) {
|
|
34
|
+
const ids = /* @__PURE__ */ new Set();
|
|
35
|
+
for (const patch of patches) switch (patch.type) {
|
|
36
|
+
case "element.add":
|
|
37
|
+
ids.add(patch.element.id);
|
|
38
|
+
break;
|
|
39
|
+
case "element.remove":
|
|
40
|
+
case "element.move":
|
|
41
|
+
case "element.update":
|
|
42
|
+
case "element.data":
|
|
43
|
+
ids.add(patch.id);
|
|
44
|
+
break;
|
|
45
|
+
default:
|
|
46
|
+
}
|
|
47
|
+
return ids;
|
|
48
|
+
}
|
|
49
|
+
var applied = /* @__PURE__ */ new WeakMap();
|
|
50
|
+
/**
|
|
51
|
+
* Keeps the static colliders of a map in step with its elements, one element at a time.
|
|
52
|
+
* `"all"` rebuilds every collider, and removes those of elements that are gone: what
|
|
53
|
+
* to do when the physics world was reset.
|
|
54
|
+
*/
|
|
55
|
+
function syncElementHitboxes(host, state, ids, context) {
|
|
56
|
+
const known = applied.get(host) ?? /* @__PURE__ */ new Set();
|
|
57
|
+
applied.set(host, known);
|
|
58
|
+
const targets = ids === "all" ? /* @__PURE__ */ new Set([...known, ...state.elements.map((element) => element.id)]) : ids;
|
|
59
|
+
for (const id of targets) {
|
|
60
|
+
const rect = hitboxOf(state, id, context);
|
|
61
|
+
if (rect) {
|
|
62
|
+
host.replaceStreamedStaticHitboxes(elementNamespace(id), [rect]);
|
|
63
|
+
known.add(id);
|
|
64
|
+
} else if (known.delete(id)) host.clearStreamedStaticHitboxes(elementNamespace(id));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Forgets what was applied: the physics world was wiped, so nothing is there any more. */
|
|
68
|
+
var forgetHitboxes = (host) => void applied.delete(host);
|
|
69
|
+
/** Removes every collider this module applied to a host, e.g. when the player leaves the map. */
|
|
70
|
+
function clearHitboxes(host) {
|
|
71
|
+
for (const id of applied.get(host) ?? []) host.clearStreamedStaticHitboxes(elementNamespace(id));
|
|
72
|
+
applied.delete(host);
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { MAP_BUILDER_CHANNEL, changedElementIds, clearHitboxes, elementNamespace, forgetHitboxes, hitboxOf, syncElementHitboxes, unwrap, wrap };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","names":[],"sources":["../../src/rpgjs/shared.ts"],"sourcesContent":["import type { MapState } from \"../model/definition\";\nimport type { MapPatch } from \"../model/patches\";\nimport { describeElement, type DescribeContext } from \"../renderer/scene\";\n\n/** The one channel (a socket event) all the messages of the map builder travel on. */\nexport const MAP_BUILDER_CHANNEL = \"map-builder\";\n\n/** A message with the map it is about: a player who changes map must never apply the old map's changes to the new one. */\nexport interface MapEnvelope {\n readonly map: string;\n readonly message: unknown;\n}\n\nexport const wrap = (map: string, message: unknown): MapEnvelope => ({ map, message });\n\nexport function unwrap(raw: unknown): MapEnvelope | undefined {\n if (typeof raw !== \"object\" || raw === null) return undefined;\n const { map, message } = raw as Partial<MapEnvelope>;\n return typeof map === \"string\" && message !== undefined ? { map, message } : undefined;\n}\n\nexport interface HitboxRect {\n readonly id: string;\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/** What both `RpgMap` (server) and the client scene offer for static colliders. */\nexport interface StaticHitboxHost {\n replaceStreamedStaticHitboxes(namespace: string, hitboxes: HitboxRect[]): void;\n clearStreamedStaticHitboxes(namespace: string): void;\n}\n\nconst NAMESPACE = \"map-builder\";\n\n/** One namespace per element: changing a tree only rebuilds the collider of that tree. */\nexport const elementNamespace = (id: string): string => `${NAMESPACE}/${id}`;\n\n/** The collider of an element: the hitbox the renderer and the pipelines already use, in world pixels. */\nexport function hitboxOf(state: MapState, id: string, context: DescribeContext): HitboxRect | undefined {\n const element = state.elements.find((entry) => entry.id === id);\n const hitbox = element ? describeElement(element, context)?.hitbox : undefined;\n return hitbox ? { id, x: hitbox.x, y: hitbox.y, width: hitbox.width, height: hitbox.height } : undefined;\n}\n\n/** Ids of the elements whose collider a patch may have changed, or `\"all\"`. */\nexport function changedElementIds(patches: readonly MapPatch[]): ReadonlySet<string> {\n const ids = new Set<string>();\n for (const patch of patches) {\n switch (patch.type) {\n case \"element.add\":\n ids.add(patch.element.id);\n break;\n case \"element.remove\":\n case \"element.move\":\n case \"element.update\":\n case \"element.data\": // hooks may derive size and hitbox from the data of an element\n ids.add(patch.id);\n break;\n default:\n }\n }\n return ids;\n}\n\nconst applied = new WeakMap<object, Set<string>>();\n\n/**\n * Keeps the static colliders of a map in step with its elements, one element at a time.\n * `\"all\"` rebuilds every collider, and removes those of elements that are gone: what\n * to do when the physics world was reset.\n */\nexport function syncElementHitboxes(\n host: StaticHitboxHost,\n state: MapState,\n ids: ReadonlySet<string> | \"all\",\n context: DescribeContext,\n): void {\n const known = applied.get(host) ?? new Set<string>();\n applied.set(host, known);\n const targets = ids === \"all\" ? new Set([...known, ...state.elements.map((element) => element.id)]) : ids;\n for (const id of targets) {\n const rect = hitboxOf(state, id, context);\n if (rect) {\n host.replaceStreamedStaticHitboxes(elementNamespace(id), [rect]);\n known.add(id);\n } else if (known.delete(id)) {\n host.clearStreamedStaticHitboxes(elementNamespace(id));\n }\n }\n}\n\n/** Forgets what was applied: the physics world was wiped, so nothing is there any more. */\nexport const forgetHitboxes = (host: object): void => void applied.delete(host);\n\n/** Removes every collider this module applied to a host, e.g. when the player leaves the map. */\nexport function clearHitboxes(host: StaticHitboxHost): void {\n for (const id of applied.get(host) ?? []) host.clearStreamedStaticHitboxes(elementNamespace(id));\n applied.delete(host);\n}\n"],"mappings":";;;AAKA,IAAa,sBAAsB;AAQnC,IAAa,QAAQ,KAAa,aAAmC;CAAE;CAAK;AAAQ;AAEpF,SAAgB,OAAO,KAAuC;CAC5D,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM,OAAO,KAAA;CACpD,MAAM,EAAE,KAAK,YAAY;CACzB,OAAO,OAAO,QAAQ,YAAY,YAAY,KAAA,IAAY;EAAE;EAAK;CAAQ,IAAI,KAAA;AAC/E;AAgBA,IAAM,YAAY;;AAGlB,IAAa,oBAAoB,OAAuB,GAAG,UAAU,GAAG;;AAGxE,SAAgB,SAAS,OAAiB,IAAY,SAAkD;CACtG,MAAM,UAAU,MAAM,SAAS,MAAM,UAAU,MAAM,OAAO,EAAE;CAC9D,MAAM,SAAS,UAAU,gBAAgB,SAAS,OAAO,CAAC,EAAE,SAAS,KAAA;CACrE,OAAO,SAAS;EAAE;EAAI,GAAG,OAAO;EAAG,GAAG,OAAO;EAAG,OAAO,OAAO;EAAO,QAAQ,OAAO;CAAO,IAAI,KAAA;AACjG;;AAGA,SAAgB,kBAAkB,SAAmD;CACnF,MAAM,sBAAM,IAAI,IAAY;CAC5B,KAAK,MAAM,SAAS,SAClB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,IAAI,IAAI,MAAM,QAAQ,EAAE;GACxB;EACF,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;GACH,IAAI,IAAI,MAAM,EAAE;GAChB;EACF;CACF;CAEF,OAAO;AACT;AAEA,IAAM,0BAAU,IAAI,QAA6B;;;;;;AAOjD,SAAgB,oBACd,MACA,OACA,KACA,SACM;CACN,MAAM,QAAQ,QAAQ,IAAI,IAAI,qBAAK,IAAI,IAAY;CACnD,QAAQ,IAAI,MAAM,KAAK;CACvB,MAAM,UAAU,QAAQ,wBAAQ,IAAI,IAAI,CAAC,GAAG,OAAO,GAAG,MAAM,SAAS,KAAK,YAAY,QAAQ,EAAE,CAAC,CAAC,IAAI;CACtG,KAAK,MAAM,MAAM,SAAS;EACxB,MAAM,OAAO,SAAS,OAAO,IAAI,OAAO;EACxC,IAAI,MAAM;GACR,KAAK,8BAA8B,iBAAiB,EAAE,GAAG,CAAC,IAAI,CAAC;GAC/D,MAAM,IAAI,EAAE;EACd,OAAO,IAAI,MAAM,OAAO,EAAE,GACxB,KAAK,4BAA4B,iBAAiB,EAAE,CAAC;CAEzD;AACF;;AAGA,IAAa,kBAAkB,SAAuB,KAAK,QAAQ,OAAO,IAAI;;AAG9E,SAAgB,cAAc,MAA8B;CAC1D,KAAK,MAAM,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,GAAG,KAAK,4BAA4B,iBAAiB,EAAE,CAAC;CAC/F,QAAQ,OAAO,IAAI;AACrB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { RpgProvider } from '@rpgjs/common';
|
|
2
|
+
import { RpgMap, RpgPlayer, RpgServer } from '@rpgjs/server';
|
|
3
|
+
import { MapAuthority, MapAuthorityOptions, PersistOptions } from '../authority/authority';
|
|
4
|
+
import { MapStore } from '../authority/store';
|
|
5
|
+
import { AssetResolver } from '../model/assets';
|
|
6
|
+
import { MapDefinition, MapEvent } from '../model/definition';
|
|
7
|
+
import { Catalog } from '../interaction/catalog';
|
|
8
|
+
import { MapSource } from '../model/source';
|
|
9
|
+
export { MAP_BUILDER_CHANNEL } from '../rpgjs/shared';
|
|
10
|
+
export { keyValueMapStore, roomStorageMapStore, type KeyValueStorage } from './storage';
|
|
11
|
+
/** Where the modifiable maps come from, by the id of the RPGJS map. A map without a definition is left alone. */
|
|
12
|
+
export type MapBuilderMaps = Readonly<Record<string, MapDefinition>> | ((mapId: string) => MapDefinition | undefined | Promise<MapDefinition | undefined>) | MapSource;
|
|
13
|
+
/** An RPGJS event made for a map event: hooks, hitbox, and what the game wants it to do. */
|
|
14
|
+
export interface MapBuilderEventDefinition {
|
|
15
|
+
readonly hooks?: Record<string, unknown>;
|
|
16
|
+
readonly hitbox?: {
|
|
17
|
+
readonly width: number;
|
|
18
|
+
readonly height: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface MapBuilderEventsOptions {
|
|
22
|
+
/**
|
|
23
|
+
* What an RPGJS event does for a map event: `onAction`, `onPlayerTouch`… By default it is
|
|
24
|
+
* only drawn, with the `graphic` of the map event as its RPGJS graphic.
|
|
25
|
+
*/
|
|
26
|
+
readonly define?: (event: MapEvent, map: RpgMap) => MapBuilderEventDefinition | undefined;
|
|
27
|
+
/** Prefix of the ids of the RPGJS events, so they never meet the game's own. Defaults to `"builder:"`. */
|
|
28
|
+
readonly idPrefix?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface MapBuilderServerOptions<C extends Catalog<string> = Catalog<string>> {
|
|
31
|
+
/** What the maps are made of. A server has no image to measure: assets must declare a `size` or a `frame`. */
|
|
32
|
+
readonly assets: AssetResolver;
|
|
33
|
+
readonly maps: MapBuilderMaps;
|
|
34
|
+
/** Rules, catalog, who may send patches, who is the author of a change… */
|
|
35
|
+
readonly authority?: Omit<MapAuthorityOptions<C>, "definition" | "revision" | "assets" | "persist" | "onChange" | "replay" | "onError">;
|
|
36
|
+
/** Keep a map between restarts: `(map) => ({ store: roomStorageMapStore(map) })`. Off by default. */
|
|
37
|
+
readonly persist?: (map: RpgMap) => Omit<PersistOptions, "id"> & {
|
|
38
|
+
readonly id?: string;
|
|
39
|
+
} | MapStore | undefined;
|
|
40
|
+
/** Who is acting, as the rules of the pipelines see them. Defaults to the player. */
|
|
41
|
+
readonly actor?: (player: RpgPlayer) => unknown;
|
|
42
|
+
/** Keep the static colliders of the physics world in step with the elements. Defaults to true. */
|
|
43
|
+
readonly physics?: boolean;
|
|
44
|
+
/** Turn the events of the map into RPGJS events. Defaults to on, drawn only; `false` leaves events alone. */
|
|
45
|
+
readonly events?: false | MapBuilderEventsOptions;
|
|
46
|
+
/** Called once the authority of a map exists: the place to hook a lever, a quest, a timer. */
|
|
47
|
+
readonly onReady?: (authority: MapAuthority, map: RpgMap) => void;
|
|
48
|
+
readonly onError?: (error: unknown) => void;
|
|
49
|
+
}
|
|
50
|
+
/** The authority of a map, once it exists. For game code: `getMapAuthority(player.getCurrentMap())?.apply(…)`. */
|
|
51
|
+
export declare const getMapAuthority: (map: object | null | undefined) => MapAuthority | undefined;
|
|
52
|
+
/** Resolves when the authority of a map exists, or to `undefined` for a map that is not modifiable. */
|
|
53
|
+
export declare const whenMapAuthority: (map: object | null | undefined) => Promise<MapAuthority | undefined>;
|
|
54
|
+
/** The module of the map builder for an RPGJS server. Install it with `provideMapBuilder`, or `provideServerModules`. */
|
|
55
|
+
export declare function createMapBuilderServer<C extends Catalog<string> = Catalog<string>>(options: MapBuilderServerOptions<C>): RpgServer;
|
|
56
|
+
/**
|
|
57
|
+
* Installs the map builder on an RPGJS server:
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* createServer({ providers: [provideMapBuilder({ assets, maps: { forest }, authority: { … } })] })
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function provideMapBuilder<C extends Catalog<string> = Catalog<string>>(options: MapBuilderServerOptions<C>): RpgProvider[];
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|