@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,161 @@
|
|
|
1
|
+
import { isJsonValue, isPlainObject } from "../model/json.js";
|
|
2
|
+
//#region src/embed/protocol.ts
|
|
3
|
+
/** Version of the messages between the Studio and a game. Both ends say theirs in the handshake. */
|
|
4
|
+
var EDITOR_PROTOCOL_VERSION = 1;
|
|
5
|
+
/** Marks the window messages of the handshake, so that the page ignores everybody else's. */
|
|
6
|
+
var EDITOR_SOURCE = "rpgjs-map-builder-editor";
|
|
7
|
+
var LIMITS = {
|
|
8
|
+
/** Targets in one selection. */
|
|
9
|
+
maxTargets: 1e4,
|
|
10
|
+
maxModes: 16,
|
|
11
|
+
maxStringLength: 256
|
|
12
|
+
};
|
|
13
|
+
var isString = (value) => typeof value === "string" && value.length > 0 && value.length <= LIMITS.maxStringLength;
|
|
14
|
+
var isFinite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
15
|
+
function parseTargets(raw) {
|
|
16
|
+
if (!Array.isArray(raw) || raw.length > LIMITS.maxTargets) return void 0;
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const item of raw) {
|
|
19
|
+
if (!isPlainObject(item) || item.type !== "element" && item.type !== "event" || !isString(item.id)) return void 0;
|
|
20
|
+
out.push({
|
|
21
|
+
type: item.type,
|
|
22
|
+
id: item.id
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/** Reads a control message that came through the port. Anything with another shape is `undefined`. */
|
|
28
|
+
function parseControl(raw) {
|
|
29
|
+
if (!isPlainObject(raw)) return void 0;
|
|
30
|
+
switch (raw.type) {
|
|
31
|
+
case "modes": return Array.isArray(raw.modes) && raw.modes.length <= LIMITS.maxModes && raw.modes.every(isString) ? {
|
|
32
|
+
type: "modes",
|
|
33
|
+
modes: raw.modes
|
|
34
|
+
} : void 0;
|
|
35
|
+
case "tool":
|
|
36
|
+
if (raw.tool === "none") return {
|
|
37
|
+
type: "tool",
|
|
38
|
+
tool: "none"
|
|
39
|
+
};
|
|
40
|
+
if (raw.tool === "brush") {
|
|
41
|
+
if (!(raw.material === null || isString(raw.material)) || !isFinite(raw.radius) || raw.radius <= 0 || raw.radius > 1e4) return void 0;
|
|
42
|
+
return {
|
|
43
|
+
type: "tool",
|
|
44
|
+
tool: "brush",
|
|
45
|
+
material: raw.material,
|
|
46
|
+
radius: raw.radius
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return raw.tool === "place" && isString(raw.catalogId) ? {
|
|
50
|
+
type: "tool",
|
|
51
|
+
tool: "place",
|
|
52
|
+
catalogId: raw.catalogId
|
|
53
|
+
} : void 0;
|
|
54
|
+
case "select": {
|
|
55
|
+
const targets = parseTargets(raw.targets);
|
|
56
|
+
return targets ? {
|
|
57
|
+
type: "select",
|
|
58
|
+
targets
|
|
59
|
+
} : void 0;
|
|
60
|
+
}
|
|
61
|
+
case "pause": return typeof raw.paused === "boolean" ? {
|
|
62
|
+
type: "pause",
|
|
63
|
+
paused: raw.paused
|
|
64
|
+
} : void 0;
|
|
65
|
+
case "camera": return isFinite(raw.x) && isFinite(raw.y) ? {
|
|
66
|
+
type: "camera",
|
|
67
|
+
x: raw.x,
|
|
68
|
+
y: raw.y
|
|
69
|
+
} : void 0;
|
|
70
|
+
case "command":
|
|
71
|
+
if (!isString(raw.name) || !/^[a-zA-Z][\w.:-]*$/.test(raw.name)) return void 0;
|
|
72
|
+
if (raw.args !== void 0 && !isJsonValue(raw.args)) return void 0;
|
|
73
|
+
return {
|
|
74
|
+
type: "command",
|
|
75
|
+
name: raw.name,
|
|
76
|
+
...raw.args !== void 0 ? { args: raw.args } : {}
|
|
77
|
+
};
|
|
78
|
+
default: return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
var parseTool = (raw) => {
|
|
82
|
+
const parsed = parseControl({
|
|
83
|
+
type: "tool",
|
|
84
|
+
...isPlainObject(raw) ? raw : {}
|
|
85
|
+
});
|
|
86
|
+
return parsed?.type === "tool" ? (({ type: _type, ...tool }) => tool)(parsed) : void 0;
|
|
87
|
+
};
|
|
88
|
+
/** Reads an event from the game. */
|
|
89
|
+
function parseEvent(raw) {
|
|
90
|
+
if (!isPlainObject(raw)) return void 0;
|
|
91
|
+
switch (raw.type) {
|
|
92
|
+
case "map": return isString(raw.id) ? {
|
|
93
|
+
type: "map",
|
|
94
|
+
id: raw.id
|
|
95
|
+
} : void 0;
|
|
96
|
+
case "selection": {
|
|
97
|
+
const targets = parseTargets(raw.targets);
|
|
98
|
+
return targets ? {
|
|
99
|
+
type: "selection",
|
|
100
|
+
targets
|
|
101
|
+
} : void 0;
|
|
102
|
+
}
|
|
103
|
+
case "interaction": {
|
|
104
|
+
const tool = parseTool(raw.tool);
|
|
105
|
+
if (!tool || !Array.isArray(raw.modes) || !raw.modes.every(isString)) return void 0;
|
|
106
|
+
const rejection = isPlainObject(raw.rejection) && typeof raw.rejection.reason === "string" ? {
|
|
107
|
+
reason: raw.rejection.reason,
|
|
108
|
+
...typeof raw.rejection.message === "string" ? { message: raw.rejection.message } : {}
|
|
109
|
+
} : null;
|
|
110
|
+
return {
|
|
111
|
+
type: "interaction",
|
|
112
|
+
modes: raw.modes,
|
|
113
|
+
tool,
|
|
114
|
+
rejection
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
case "paused": return typeof raw.paused === "boolean" ? {
|
|
118
|
+
type: "paused",
|
|
119
|
+
paused: raw.paused
|
|
120
|
+
} : void 0;
|
|
121
|
+
case "pointer": return isFinite(raw.x) && isFinite(raw.y) ? {
|
|
122
|
+
type: "pointer",
|
|
123
|
+
x: raw.x,
|
|
124
|
+
y: raw.y
|
|
125
|
+
} : void 0;
|
|
126
|
+
case "state": return isString(raw.key) && raw.value !== void 0 && isJsonValue(raw.value) ? {
|
|
127
|
+
type: "state",
|
|
128
|
+
key: raw.key,
|
|
129
|
+
value: raw.value
|
|
130
|
+
} : void 0;
|
|
131
|
+
case "error": return typeof raw.message === "string" ? {
|
|
132
|
+
type: "error",
|
|
133
|
+
message: raw.message.slice(0, 1e3)
|
|
134
|
+
} : void 0;
|
|
135
|
+
default: return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function parseHandshake(raw) {
|
|
139
|
+
if (!isPlainObject(raw) || raw.source !== "rpgjs-map-builder-editor" || !isFinite(raw.version)) return void 0;
|
|
140
|
+
return raw.type === "hello" || raw.type === "ready" || raw.type === "connect" ? {
|
|
141
|
+
source: EDITOR_SOURCE,
|
|
142
|
+
type: raw.type,
|
|
143
|
+
version: raw.version
|
|
144
|
+
} : void 0;
|
|
145
|
+
}
|
|
146
|
+
function parseEnvelope(raw) {
|
|
147
|
+
if (!isPlainObject(raw) || !isJsonValue(raw)) return void 0;
|
|
148
|
+
if (raw.ch === "map") return isString(raw.map) ? {
|
|
149
|
+
ch: "map",
|
|
150
|
+
map: raw.map,
|
|
151
|
+
message: raw.message
|
|
152
|
+
} : void 0;
|
|
153
|
+
if (raw.ch === "control" || raw.ch === "event") return {
|
|
154
|
+
ch: raw.ch,
|
|
155
|
+
message: raw.message
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
export { EDITOR_PROTOCOL_VERSION, EDITOR_SOURCE, LIMITS, parseControl, parseEnvelope, parseEvent, parseHandshake, parseTargets };
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","names":[],"sources":["../../src/embed/protocol.ts"],"sourcesContent":["import { isPlainObject, isJsonValue, type JsonValue } from \"../model/json\";\nimport type { TargetRef } from \"../interaction/picking\";\n\n/** Version of the messages between the Studio and a game. Both ends say theirs in the handshake. */\nexport const EDITOR_PROTOCOL_VERSION = 1 as const;\n\n/** Marks the window messages of the handshake, so that the page ignores everybody else's. */\nexport const EDITOR_SOURCE = \"rpgjs-map-builder-editor\" as const;\n\n/** The tool that is in hand. */\nexport type EditorTool =\n | { readonly tool: \"none\" }\n | { readonly tool: \"brush\"; readonly material: string | null; readonly radius: number }\n | { readonly tool: \"place\"; readonly catalogId: string };\n\n/** What the Studio asks of the game. */\nexport type ControlMessage =\n | { readonly type: \"modes\"; readonly modes: readonly string[] }\n | ({ readonly type: \"tool\" } & EditorTool)\n | { readonly type: \"select\"; readonly targets: readonly TargetRef[] }\n | { readonly type: \"pause\"; readonly paused: boolean }\n | { readonly type: \"camera\"; readonly x: number; readonly y: number }\n /** Something only the game knows how to do: a command it registered (`copy`, `place`…). */\n | { readonly type: \"command\"; readonly name: string; readonly args?: JsonValue };\n\n/** What the game tells the Studio. */\nexport type EditorEvent =\n | { readonly type: \"map\"; readonly id: string }\n | { readonly type: \"selection\"; readonly targets: readonly TargetRef[] }\n | {\n readonly type: \"interaction\";\n readonly modes: readonly string[];\n readonly tool: EditorTool;\n /** Why the last intent was refused, until another is accepted. */\n readonly rejection: { readonly reason: string; readonly message?: string } | null;\n }\n | { readonly type: \"paused\"; readonly paused: boolean }\n | { readonly type: \"pointer\"; readonly x: number; readonly y: number }\n /** A value the game keeps and the Studio shows (`tool`, `canUndo`, `layer`…). */\n | { readonly type: \"state\"; readonly key: string; readonly value: JsonValue }\n | { readonly type: \"error\"; readonly message: string };\n\n/** Messages of the handshake, sent with `window.postMessage`. */\nexport type HandshakeMessage =\n | { readonly source: typeof EDITOR_SOURCE; readonly type: \"hello\"; readonly version: number }\n | { readonly source: typeof EDITOR_SOURCE; readonly type: \"ready\"; readonly version: number }\n | { readonly source: typeof EDITOR_SOURCE; readonly type: \"connect\"; readonly version: number };\n\nexport const LIMITS = {\n /** Targets in one selection. */\n maxTargets: 10_000,\n maxModes: 16,\n maxStringLength: 256,\n} as const;\n\nconst isString = (value: unknown): value is string => typeof value === \"string\" && value.length > 0 && value.length <= LIMITS.maxStringLength;\nconst isFinite = (value: unknown): value is number => typeof value === \"number\" && Number.isFinite(value);\n\nexport function parseTargets(raw: unknown): TargetRef[] | undefined {\n if (!Array.isArray(raw) || raw.length > LIMITS.maxTargets) return undefined;\n const out: TargetRef[] = [];\n for (const item of raw) {\n if (!isPlainObject(item) || (item.type !== \"element\" && item.type !== \"event\") || !isString(item.id)) return undefined;\n out.push({ type: item.type, id: item.id });\n }\n return out;\n}\n\n/** Reads a control message that came through the port. Anything with another shape is `undefined`. */\nexport function parseControl(raw: unknown): ControlMessage | undefined {\n if (!isPlainObject(raw)) return undefined;\n switch (raw.type) {\n case \"modes\":\n return Array.isArray(raw.modes) && raw.modes.length <= LIMITS.maxModes && raw.modes.every(isString) ? { type: \"modes\", modes: raw.modes as string[] } : undefined;\n case \"tool\":\n if (raw.tool === \"none\") return { type: \"tool\", tool: \"none\" };\n if (raw.tool === \"brush\") {\n if (!(raw.material === null || isString(raw.material)) || !isFinite(raw.radius) || raw.radius <= 0 || raw.radius > 10_000) return undefined;\n return { type: \"tool\", tool: \"brush\", material: raw.material, radius: raw.radius };\n }\n return raw.tool === \"place\" && isString(raw.catalogId) ? { type: \"tool\", tool: \"place\", catalogId: raw.catalogId } : undefined;\n case \"select\": {\n const targets = parseTargets(raw.targets);\n return targets ? { type: \"select\", targets } : undefined;\n }\n case \"pause\":\n return typeof raw.paused === \"boolean\" ? { type: \"pause\", paused: raw.paused } : undefined;\n case \"camera\":\n return isFinite(raw.x) && isFinite(raw.y) ? { type: \"camera\", x: raw.x, y: raw.y } : undefined;\n case \"command\":\n if (!isString(raw.name) || !/^[a-zA-Z][\\w.:-]*$/.test(raw.name)) return undefined;\n if (raw.args !== undefined && !isJsonValue(raw.args)) return undefined;\n return { type: \"command\", name: raw.name, ...(raw.args !== undefined ? { args: raw.args as JsonValue } : {}) };\n default:\n return undefined;\n }\n}\n\nconst parseTool = (raw: unknown): EditorTool | undefined => {\n const parsed = parseControl({ type: \"tool\", ...(isPlainObject(raw) ? raw : {}) });\n return parsed?.type === \"tool\" ? (({ type: _type, ...tool }) => tool as EditorTool)(parsed) : undefined;\n};\n\n/** Reads an event from the game. */\nexport function parseEvent(raw: unknown): EditorEvent | undefined {\n if (!isPlainObject(raw)) return undefined;\n switch (raw.type) {\n case \"map\":\n return isString(raw.id) ? { type: \"map\", id: raw.id } : undefined;\n case \"selection\": {\n const targets = parseTargets(raw.targets);\n return targets ? { type: \"selection\", targets } : undefined;\n }\n case \"interaction\": {\n const tool = parseTool(raw.tool);\n if (!tool || !Array.isArray(raw.modes) || !raw.modes.every(isString)) return undefined;\n const rejection = isPlainObject(raw.rejection) && typeof raw.rejection.reason === \"string\"\n ? { reason: raw.rejection.reason, ...(typeof raw.rejection.message === \"string\" ? { message: raw.rejection.message } : {}) }\n : null;\n return { type: \"interaction\", modes: raw.modes as string[], tool, rejection };\n }\n case \"paused\":\n return typeof raw.paused === \"boolean\" ? { type: \"paused\", paused: raw.paused } : undefined;\n case \"pointer\":\n return isFinite(raw.x) && isFinite(raw.y) ? { type: \"pointer\", x: raw.x, y: raw.y } : undefined;\n case \"state\":\n return isString(raw.key) && raw.value !== undefined && isJsonValue(raw.value) ? { type: \"state\", key: raw.key, value: raw.value as JsonValue } : undefined;\n case \"error\":\n return typeof raw.message === \"string\" ? { type: \"error\", message: raw.message.slice(0, 1000) } : undefined;\n default:\n return undefined;\n }\n}\n\nexport function parseHandshake(raw: unknown): HandshakeMessage | undefined {\n if (!isPlainObject(raw) || raw.source !== EDITOR_SOURCE || !isFinite(raw.version)) return undefined;\n return raw.type === \"hello\" || raw.type === \"ready\" || raw.type === \"connect\" ? { source: EDITOR_SOURCE, type: raw.type, version: raw.version } : undefined;\n}\n\n/** What goes through the port. The map channel carries the authority protocol, tagged with the map. */\nexport type Envelope =\n | { readonly ch: \"map\"; readonly map: string; readonly message: unknown }\n | { readonly ch: \"control\"; readonly message: unknown }\n | { readonly ch: \"event\"; readonly message: unknown };\n\nexport function parseEnvelope(raw: unknown): Envelope | undefined {\n if (!isPlainObject(raw) || !isJsonValue(raw)) return undefined;\n if (raw.ch === \"map\") return isString(raw.map) ? { ch: \"map\", map: raw.map, message: raw.message } : undefined;\n if (raw.ch === \"control\" || raw.ch === \"event\") return { ch: raw.ch, message: raw.message };\n return undefined;\n}\n\n/** What a `MessagePort` offers, so that tests and other carriers can stand in. */\nexport interface PortLike {\n postMessage(message: unknown): void;\n addEventListener(type: \"message\", listener: (event: { data: unknown }) => void): void;\n start?(): void;\n close(): void;\n}\n\n/** What a window offers to listen to messages. */\nexport interface WindowLike {\n addEventListener(type: \"message\", listener: (event: MessageEventLike) => void): void;\n removeEventListener(type: \"message\", listener: (event: MessageEventLike) => void): void;\n}\n\nexport interface MessageEventLike {\n readonly data: unknown;\n readonly origin: string;\n readonly source: unknown;\n readonly ports?: readonly PortLike[];\n}\n\n/** Something a message can be posted to: a frame's window, or `window.parent`. */\nexport interface TargetLike {\n postMessage(message: unknown, targetOrigin: string, transfer?: Transferable[]): void;\n}\n"],"mappings":";;;AAIA,IAAa,0BAA0B;;AAGvC,IAAa,gBAAgB;AAyC7B,IAAa,SAAS;;CAEpB,YAAY;CACZ,UAAU;CACV,iBAAiB;AACnB;AAEA,IAAM,YAAY,UAAoC,OAAO,UAAU,YAAY,MAAM,SAAS,KAAK,MAAM,UAAU,OAAO;AAC9H,IAAM,YAAY,UAAoC,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAExG,SAAgB,aAAa,KAAuC;CAClE,IAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,SAAS,OAAO,YAAY,OAAO,KAAA;CAClE,MAAM,MAAmB,CAAC;CAC1B,KAAK,MAAM,QAAQ,KAAK;EACtB,IAAI,CAAC,cAAc,IAAI,KAAM,KAAK,SAAS,aAAa,KAAK,SAAS,WAAY,CAAC,SAAS,KAAK,EAAE,GAAG,OAAO,KAAA;EAC7G,IAAI,KAAK;GAAE,MAAM,KAAK;GAAM,IAAI,KAAK;EAAG,CAAC;CAC3C;CACA,OAAO;AACT;;AAGA,SAAgB,aAAa,KAA0C;CACrE,IAAI,CAAC,cAAc,GAAG,GAAG,OAAO,KAAA;CAChC,QAAQ,IAAI,MAAZ;EACE,KAAK,SACH,OAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,IAAI,MAAM,UAAU,OAAO,YAAY,IAAI,MAAM,MAAM,QAAQ,IAAI;GAAE,MAAM;GAAS,OAAO,IAAI;EAAkB,IAAI,KAAA;EAC1J,KAAK;GACH,IAAI,IAAI,SAAS,QAAQ,OAAO;IAAE,MAAM;IAAQ,MAAM;GAAO;GAC7D,IAAI,IAAI,SAAS,SAAS;IACxB,IAAI,EAAE,IAAI,aAAa,QAAQ,SAAS,IAAI,QAAQ,MAAM,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI,UAAU,KAAK,IAAI,SAAS,KAAQ,OAAO,KAAA;IAClI,OAAO;KAAE,MAAM;KAAQ,MAAM;KAAS,UAAU,IAAI;KAAU,QAAQ,IAAI;IAAO;GACnF;GACA,OAAO,IAAI,SAAS,WAAW,SAAS,IAAI,SAAS,IAAI;IAAE,MAAM;IAAQ,MAAM;IAAS,WAAW,IAAI;GAAU,IAAI,KAAA;EACvH,KAAK,UAAU;GACb,MAAM,UAAU,aAAa,IAAI,OAAO;GACxC,OAAO,UAAU;IAAE,MAAM;IAAU;GAAQ,IAAI,KAAA;EACjD;EACA,KAAK,SACH,OAAO,OAAO,IAAI,WAAW,YAAY;GAAE,MAAM;GAAS,QAAQ,IAAI;EAAO,IAAI,KAAA;EACnF,KAAK,UACH,OAAO,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,IAAI;GAAE,MAAM;GAAU,GAAG,IAAI;GAAG,GAAG,IAAI;EAAE,IAAI,KAAA;EACvF,KAAK;GACH,IAAI,CAAC,SAAS,IAAI,IAAI,KAAK,CAAC,qBAAqB,KAAK,IAAI,IAAI,GAAG,OAAO,KAAA;GACxE,IAAI,IAAI,SAAS,KAAA,KAAa,CAAC,YAAY,IAAI,IAAI,GAAG,OAAO,KAAA;GAC7D,OAAO;IAAE,MAAM;IAAW,MAAM,IAAI;IAAM,GAAI,IAAI,SAAS,KAAA,IAAY,EAAE,MAAM,IAAI,KAAkB,IAAI,CAAC;GAAG;EAC/G,SACE;CACJ;AACF;AAEA,IAAM,aAAa,QAAyC;CAC1D,MAAM,SAAS,aAAa;EAAE,MAAM;EAAQ,GAAI,cAAc,GAAG,IAAI,MAAM,CAAC;CAAG,CAAC;CAChF,OAAO,QAAQ,SAAS,WAAW,EAAE,MAAM,OAAO,GAAG,WAAW,KAAA,CAAoB,MAAM,IAAI,KAAA;AAChG;;AAGA,SAAgB,WAAW,KAAuC;CAChE,IAAI,CAAC,cAAc,GAAG,GAAG,OAAO,KAAA;CAChC,QAAQ,IAAI,MAAZ;EACE,KAAK,OACH,OAAO,SAAS,IAAI,EAAE,IAAI;GAAE,MAAM;GAAO,IAAI,IAAI;EAAG,IAAI,KAAA;EAC1D,KAAK,aAAa;GAChB,MAAM,UAAU,aAAa,IAAI,OAAO;GACxC,OAAO,UAAU;IAAE,MAAM;IAAa;GAAQ,IAAI,KAAA;EACpD;EACA,KAAK,eAAe;GAClB,MAAM,OAAO,UAAU,IAAI,IAAI;GAC/B,IAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,MAAM,QAAQ,GAAG,OAAO,KAAA;GAC7E,MAAM,YAAY,cAAc,IAAI,SAAS,KAAK,OAAO,IAAI,UAAU,WAAW,WAC9E;IAAE,QAAQ,IAAI,UAAU;IAAQ,GAAI,OAAO,IAAI,UAAU,YAAY,WAAW,EAAE,SAAS,IAAI,UAAU,QAAQ,IAAI,CAAC;GAAG,IACzH;GACJ,OAAO;IAAE,MAAM;IAAe,OAAO,IAAI;IAAmB;IAAM;GAAU;EAC9E;EACA,KAAK,UACH,OAAO,OAAO,IAAI,WAAW,YAAY;GAAE,MAAM;GAAU,QAAQ,IAAI;EAAO,IAAI,KAAA;EACpF,KAAK,WACH,OAAO,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,IAAI;GAAE,MAAM;GAAW,GAAG,IAAI;GAAG,GAAG,IAAI;EAAE,IAAI,KAAA;EACxF,KAAK,SACH,OAAO,SAAS,IAAI,GAAG,KAAK,IAAI,UAAU,KAAA,KAAa,YAAY,IAAI,KAAK,IAAI;GAAE,MAAM;GAAS,KAAK,IAAI;GAAK,OAAO,IAAI;EAAmB,IAAI,KAAA;EACnJ,KAAK,SACH,OAAO,OAAO,IAAI,YAAY,WAAW;GAAE,MAAM;GAAS,SAAS,IAAI,QAAQ,MAAM,GAAG,GAAI;EAAE,IAAI,KAAA;EACpG,SACE;CACJ;AACF;AAEA,SAAgB,eAAe,KAA4C;CACzE,IAAI,CAAC,cAAc,GAAG,KAAK,IAAI,WAAA,8BAA4B,CAAC,SAAS,IAAI,OAAO,GAAG,OAAO,KAAA;CAC1F,OAAO,IAAI,SAAS,WAAW,IAAI,SAAS,WAAW,IAAI,SAAS,YAAY;EAAE,QAAQ;EAAe,MAAM,IAAI;EAAM,SAAS,IAAI;CAAQ,IAAI,KAAA;AACpJ;AAQA,SAAgB,cAAc,KAAoC;CAChE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,YAAY,GAAG,GAAG,OAAO,KAAA;CACrD,IAAI,IAAI,OAAO,OAAO,OAAO,SAAS,IAAI,GAAG,IAAI;EAAE,IAAI;EAAO,KAAK,IAAI;EAAK,SAAS,IAAI;CAAQ,IAAI,KAAA;CACrG,IAAI,IAAI,OAAO,aAAa,IAAI,OAAO,SAAS,OAAO;EAAE,IAAI,IAAI;EAAI,SAAS,IAAI;CAAQ;AAE5F"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Unsubscribe, ApplyResult, MaybePromise } from '../model/result';
|
|
2
|
+
import { Catalog, CatalogId } from '../interaction/catalog';
|
|
3
|
+
import { TargetRef } from '../interaction/picking';
|
|
4
|
+
import { PlacementApi, PlacementOptions } from '../interaction/placement';
|
|
5
|
+
import { PipelineStep } from '../interaction/pipeline';
|
|
6
|
+
import { SelectionToolOptions } from '../interaction/selection-tool';
|
|
7
|
+
import { MapFeature } from '../renderer/feature';
|
|
8
|
+
import { Skin } from '../skins/types';
|
|
9
|
+
import { MapHistory } from '../model/history';
|
|
10
|
+
export { defineMapFeature, MapFeatureError, type FeatureApis, type FeatureHost, type FeatureInstance, type FeatureRender, type MapFeature, type MapFeatureDefinition, } from '../renderer/feature';
|
|
11
|
+
export { mountSkinLayers } from './skin';
|
|
12
|
+
export declare const ELEMENTS_FEATURE_ID = "elements";
|
|
13
|
+
export declare const EVENTS_FEATURE_ID = "events";
|
|
14
|
+
export declare const SELECTION_FEATURE_ID = "selection";
|
|
15
|
+
export declare const PLACEMENT_FEATURE_ID = "placement";
|
|
16
|
+
export declare const HISTORY_FEATURE_ID = "history";
|
|
17
|
+
export interface ElementsOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Make elements editable through `view.submit`: registers the `element.move` and
|
|
20
|
+
* `element.remove` intents. Editing tools (`selection()`) do it on their own.
|
|
21
|
+
*/
|
|
22
|
+
readonly edit?: boolean;
|
|
23
|
+
/** Rules every move and every removal goes through, whoever asks: a tool, `view.submit`, a game. */
|
|
24
|
+
readonly pipeline?: {
|
|
25
|
+
readonly move?: readonly PipelineStep<"element.move">[];
|
|
26
|
+
readonly remove?: readonly PipelineStep<"element.remove">[];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Draws the elements of the map: trees, houses, anything placed. */
|
|
30
|
+
export declare function elements(options?: ElementsOptions): MapFeature<"elements">;
|
|
31
|
+
/** Draws the events that have a graphic. Events without one are invisible in the map. */
|
|
32
|
+
export declare function events(): MapFeature<"events">;
|
|
33
|
+
export interface SelectionApi {
|
|
34
|
+
/** What is selected, in the order it was selected. */
|
|
35
|
+
readonly current: readonly TargetRef[];
|
|
36
|
+
/** What is under the pointer. */
|
|
37
|
+
readonly hovered: TargetRef | null;
|
|
38
|
+
select(targets: TargetRef | readonly TargetRef[], options?: {
|
|
39
|
+
readonly additive?: boolean;
|
|
40
|
+
}): void;
|
|
41
|
+
clear(): void;
|
|
42
|
+
has(target: TargetRef): boolean;
|
|
43
|
+
/** Called when the selection or the hover changes. Plugs into Angular signals, Vue, React… */
|
|
44
|
+
subscribe(listener: (selection: readonly TargetRef[]) => void): Unsubscribe;
|
|
45
|
+
}
|
|
46
|
+
export interface SelectionFeatureOptions extends SelectionToolOptions {
|
|
47
|
+
/** Replaces parts of the default skin: `{ selection: myGlow }`. */
|
|
48
|
+
readonly skin?: Skin;
|
|
49
|
+
}
|
|
50
|
+
/** Hover, selection, rubber band, dragging and deleting. Needs the elements feature. */
|
|
51
|
+
export declare function selection(options?: SelectionFeatureOptions): MapFeature<"selection", SelectionApi>;
|
|
52
|
+
export interface PlacementFeatureOptions<C extends Catalog<string>> extends PlacementOptions<C> {
|
|
53
|
+
/** Replaces parts of the default skin: `{ ghost: myGhost }`. */
|
|
54
|
+
readonly skin?: Skin;
|
|
55
|
+
}
|
|
56
|
+
/** Placing things of a catalog, with a ghost that previews the rules. Needs the elements feature. */
|
|
57
|
+
export declare function placement<const C extends Catalog<string>>(options: PlacementFeatureOptions<C>): MapFeature<"placement", PlacementApi<CatalogId<C>>>;
|
|
58
|
+
/**
|
|
59
|
+
* Undo and redo of the model, on the view: `view.history.undo()`.
|
|
60
|
+
*
|
|
61
|
+
* The feature cannot know whether the model answers at once or later, so results are
|
|
62
|
+
* typed `MaybePromise<ApplyResult>`. With a local committer, say so: `history<ApplyResult>()`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function history<R extends MaybePromise<ApplyResult> = MaybePromise<ApplyResult>>(): MapFeature<"history", MapHistory<R>>;
|
|
65
|
+
export { terrain, canvasSurface, decodeRaster, memorySurface, TERRAIN_FEATURE_ID, type TerrainApi, type TerrainOptions, type TerrainSurface, type TerrainSurfaceFactory, } from './terrain';
|
|
66
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAmB,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACrG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGxE,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGjE,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAEzC,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAC9C,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAChD,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAChD,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,oGAAoG;IACpG,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;QACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAAC;KAC7D,CAAC;CACH;AAED,qEAAqE;AACrE,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,UAAU,CAAC,UAAU,CAAC,CAoC9E;AAED,yFAAyF;AACzF,wBAAgB,MAAM,IAAI,UAAU,CAAC,QAAQ,CAAC,CAuB7C;AAED,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACnG,KAAK,IAAI,IAAI,CAAC;IACd,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAChC,8FAA8F;IAC9F,SAAS,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,KAAK,IAAI,GAAG,WAAW,CAAC;CAC7E;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;CACtB;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAAC,WAAW,EAAE,YAAY,CAAC,CA6CtG;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;IAC7F,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;CACtB;AAED,qGAAqG;AACrG,wBAAgB,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,EACvD,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAClC,UAAU,CAAC,WAAW,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAiBrD;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,YAAY,CAAC,WAAW,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,KAAK,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAS/H;AAED,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { describeElement, describeEvent } from "../renderer/scene.js";
|
|
2
|
+
import { MapFeatureError, defineMapFeature } from "../renderer/feature.js";
|
|
3
|
+
import { sameTarget } from "../interaction/picking.js";
|
|
4
|
+
import { registerElementIntents } from "../interaction/builtin-intents.js";
|
|
5
|
+
import { createPlacement } from "../interaction/placement.js";
|
|
6
|
+
import { selectionTool } from "../interaction/selection-tool.js";
|
|
7
|
+
import { defaultSkin } from "../skins/default.js";
|
|
8
|
+
import { mergeSkin } from "../skins/types.js";
|
|
9
|
+
import { mountSkinLayers } from "./skin.js";
|
|
10
|
+
import { TERRAIN_FEATURE_ID, canvasSurface, decodeRaster, memorySurface, terrain } from "./terrain.js";
|
|
11
|
+
//#region src/features/index.ts
|
|
12
|
+
var ELEMENTS_FEATURE_ID = "elements";
|
|
13
|
+
var EVENTS_FEATURE_ID = "events";
|
|
14
|
+
var SELECTION_FEATURE_ID = "selection";
|
|
15
|
+
var PLACEMENT_FEATURE_ID = "placement";
|
|
16
|
+
var HISTORY_FEATURE_ID = "history";
|
|
17
|
+
/** Draws the elements of the map: trees, houses, anything placed. */
|
|
18
|
+
function elements(options = {}) {
|
|
19
|
+
return defineMapFeature({
|
|
20
|
+
id: ELEMENTS_FEATURE_ID,
|
|
21
|
+
render: {
|
|
22
|
+
stage: "objects",
|
|
23
|
+
describeAll: (state, context) => state.elements.flatMap((element) => describeElement(element, context) ?? []),
|
|
24
|
+
describeOne(state, id, context) {
|
|
25
|
+
const element = state.elements.find((entry) => entry.id === id);
|
|
26
|
+
return element ? describeElement(element, context) : void 0;
|
|
27
|
+
},
|
|
28
|
+
affected(patch) {
|
|
29
|
+
switch (patch.type) {
|
|
30
|
+
case "element.add": return [patch.element.id];
|
|
31
|
+
case "element.remove":
|
|
32
|
+
case "element.move":
|
|
33
|
+
case "element.update":
|
|
34
|
+
case "element.data": return [patch.id];
|
|
35
|
+
default: return "none";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
...options.edit === true || options.pipeline !== void 0 ? { setup(host) {
|
|
40
|
+
const stops = [registerElementIntents(host.interaction)];
|
|
41
|
+
if (options.pipeline?.move) stops.push(host.interaction.usePipeline("element.move", options.pipeline.move));
|
|
42
|
+
if (options.pipeline?.remove) stops.push(host.interaction.usePipeline("element.remove", options.pipeline.remove));
|
|
43
|
+
return { dispose: () => stops.forEach((stop) => stop()) };
|
|
44
|
+
} } : {}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Draws the events that have a graphic. Events without one are invisible in the map. */
|
|
48
|
+
function events() {
|
|
49
|
+
return defineMapFeature({
|
|
50
|
+
id: EVENTS_FEATURE_ID,
|
|
51
|
+
render: {
|
|
52
|
+
stage: "objects",
|
|
53
|
+
describeAll: (state, context) => state.events.flatMap((event) => describeEvent(event, context) ?? []),
|
|
54
|
+
describeOne(state, id, context) {
|
|
55
|
+
const event = state.events.find((entry) => entry.id === id);
|
|
56
|
+
return event ? describeEvent(event, context) : void 0;
|
|
57
|
+
},
|
|
58
|
+
affected(patch) {
|
|
59
|
+
switch (patch.type) {
|
|
60
|
+
case "event.add": return [patch.event.id];
|
|
61
|
+
case "event.remove":
|
|
62
|
+
case "event.move": return [patch.id];
|
|
63
|
+
default: return "none";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Hover, selection, rubber band, dragging and deleting. Needs the elements feature. */
|
|
70
|
+
function selection(options = {}) {
|
|
71
|
+
const { skin, ...toolOptions } = options;
|
|
72
|
+
return defineMapFeature({
|
|
73
|
+
id: SELECTION_FEATURE_ID,
|
|
74
|
+
requires: [ELEMENTS_FEATURE_ID],
|
|
75
|
+
setup(host) {
|
|
76
|
+
const controller = host.interaction;
|
|
77
|
+
const stops = [
|
|
78
|
+
registerElementIntents(controller),
|
|
79
|
+
controller.addTool(selectionTool(toolOptions)),
|
|
80
|
+
mountSkinLayers(host, (() => {
|
|
81
|
+
const look = mergeSkin(defaultSkin(), skin);
|
|
82
|
+
return [
|
|
83
|
+
look.selection,
|
|
84
|
+
look.hover,
|
|
85
|
+
look.drag,
|
|
86
|
+
look.marquee
|
|
87
|
+
];
|
|
88
|
+
})())
|
|
89
|
+
];
|
|
90
|
+
return {
|
|
91
|
+
api: {
|
|
92
|
+
get current() {
|
|
93
|
+
return controller.state.get().selection;
|
|
94
|
+
},
|
|
95
|
+
get hovered() {
|
|
96
|
+
return controller.state.get().hovered;
|
|
97
|
+
},
|
|
98
|
+
select(targets, selectOptions) {
|
|
99
|
+
const list = Array.isArray(targets) ? targets : [targets];
|
|
100
|
+
const next = [...selectOptions?.additive ? controller.state.get().selection : []];
|
|
101
|
+
for (const target of list) if (!next.some((other) => sameTarget(other, target))) next.push(target);
|
|
102
|
+
controller.state.update({ selection: next });
|
|
103
|
+
},
|
|
104
|
+
clear: () => controller.state.update({ selection: [] }),
|
|
105
|
+
has: (target) => controller.state.get().selection.some((other) => sameTarget(other, target)),
|
|
106
|
+
subscribe(listener) {
|
|
107
|
+
let last = controller.state.get();
|
|
108
|
+
return controller.state.subscribe((state) => {
|
|
109
|
+
if (state.selection !== last.selection || state.hovered !== last.hovered) listener(state.selection);
|
|
110
|
+
last = state;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
dispose: () => stops.forEach((stop) => stop())
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Placing things of a catalog, with a ghost that previews the rules. Needs the elements feature. */
|
|
120
|
+
function placement(options) {
|
|
121
|
+
const { skin, ...placementOptions } = options;
|
|
122
|
+
return defineMapFeature({
|
|
123
|
+
id: PLACEMENT_FEATURE_ID,
|
|
124
|
+
requires: [ELEMENTS_FEATURE_ID],
|
|
125
|
+
setup(host) {
|
|
126
|
+
const placed = createPlacement(host.interaction, placementOptions);
|
|
127
|
+
const unmount = mountSkinLayers(host, [mergeSkin(defaultSkin(), skin).ghost]);
|
|
128
|
+
return {
|
|
129
|
+
api: placed.api,
|
|
130
|
+
dispose() {
|
|
131
|
+
unmount();
|
|
132
|
+
placed.dispose();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Undo and redo of the model, on the view: `view.history.undo()`.
|
|
140
|
+
*
|
|
141
|
+
* The feature cannot know whether the model answers at once or later, so results are
|
|
142
|
+
* typed `MaybePromise<ApplyResult>`. With a local committer, say so: `history<ApplyResult>()`.
|
|
143
|
+
*/
|
|
144
|
+
function history() {
|
|
145
|
+
return defineMapFeature({
|
|
146
|
+
id: HISTORY_FEATURE_ID,
|
|
147
|
+
setup(host) {
|
|
148
|
+
const model = host.model;
|
|
149
|
+
if (!model.history) throw new Error("The history feature needs a model that has a history");
|
|
150
|
+
return { api: model.history };
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
export { ELEMENTS_FEATURE_ID, EVENTS_FEATURE_ID, HISTORY_FEATURE_ID, MapFeatureError, PLACEMENT_FEATURE_ID, SELECTION_FEATURE_ID, TERRAIN_FEATURE_ID, canvasSurface, decodeRaster, defineMapFeature, elements, events, history, memorySurface, mountSkinLayers, placement, selection, terrain };
|
|
156
|
+
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/features/index.ts"],"sourcesContent":["import type { Unsubscribe } from \"../model/result\";\nimport { registerElementIntents } from \"../interaction/builtin-intents\";\nimport type { Catalog, CatalogId } from \"../interaction/catalog\";\nimport type { InteractionController } from \"../interaction/controller\";\nimport type { TargetRef } from \"../interaction/picking\";\nimport { sameTarget } from \"../interaction/picking\";\nimport { createPlacement, type PlacementApi, type PlacementOptions } from \"../interaction/placement\";\nimport type { PipelineStep } from \"../interaction/pipeline\";\nimport { selectionTool, type SelectionToolOptions } from \"../interaction/selection-tool\";\nimport { defineMapFeature, type MapFeature } from \"../renderer/feature\";\nimport { describeElement, describeEvent } from \"../renderer/scene\";\nimport { defaultSkin } from \"../skins/default\";\nimport { mergeSkin, type Skin } from \"../skins/types\";\nimport type { MapHistory } from \"../model/history\";\nimport type { ApplyResult, MaybePromise } from \"../model/result\";\nimport { mountSkinLayers } from \"./skin\";\n\nexport {\n defineMapFeature,\n MapFeatureError,\n type FeatureApis,\n type FeatureHost,\n type FeatureInstance,\n type FeatureRender,\n type MapFeature,\n type MapFeatureDefinition,\n} from \"../renderer/feature\";\nexport { mountSkinLayers } from \"./skin\";\n\nexport const ELEMENTS_FEATURE_ID = \"elements\";\nexport const EVENTS_FEATURE_ID = \"events\";\nexport const SELECTION_FEATURE_ID = \"selection\";\nexport const PLACEMENT_FEATURE_ID = \"placement\";\nexport const HISTORY_FEATURE_ID = \"history\";\n\nexport interface ElementsOptions {\n /**\n * Make elements editable through `view.submit`: registers the `element.move` and\n * `element.remove` intents. Editing tools (`selection()`) do it on their own.\n */\n readonly edit?: boolean;\n /** Rules every move and every removal goes through, whoever asks: a tool, `view.submit`, a game. */\n readonly pipeline?: {\n readonly move?: readonly PipelineStep<\"element.move\">[];\n readonly remove?: readonly PipelineStep<\"element.remove\">[];\n };\n}\n\n/** Draws the elements of the map: trees, houses, anything placed. */\nexport function elements(options: ElementsOptions = {}): MapFeature<\"elements\"> {\n const editable = options.edit === true || options.pipeline !== undefined;\n return defineMapFeature({\n id: ELEMENTS_FEATURE_ID,\n render: {\n stage: \"objects\",\n describeAll: (state, context) => state.elements.flatMap((element) => describeElement(element, context) ?? []),\n describeOne(state, id, context) {\n const element = state.elements.find((entry) => entry.id === id);\n return element ? describeElement(element, context) : undefined;\n },\n affected(patch) {\n switch (patch.type) {\n case \"element.add\":\n return [patch.element.id];\n case \"element.remove\":\n case \"element.move\":\n case \"element.update\":\n case \"element.data\":\n return [patch.id];\n default:\n return \"none\";\n }\n },\n },\n ...(editable\n ? {\n setup(host) {\n const stops: Unsubscribe[] = [registerElementIntents(host.interaction as unknown as InteractionController)];\n if (options.pipeline?.move) stops.push(host.interaction.usePipeline(\"element.move\", options.pipeline.move));\n if (options.pipeline?.remove) stops.push(host.interaction.usePipeline(\"element.remove\", options.pipeline.remove));\n return { dispose: () => stops.forEach((stop) => stop()) };\n },\n }\n : {}),\n });\n}\n\n/** Draws the events that have a graphic. Events without one are invisible in the map. */\nexport function events(): MapFeature<\"events\"> {\n return defineMapFeature({\n id: EVENTS_FEATURE_ID,\n render: {\n stage: \"objects\",\n describeAll: (state, context) => state.events.flatMap((event) => describeEvent(event, context) ?? []),\n describeOne(state, id, context) {\n const event = state.events.find((entry) => entry.id === id);\n return event ? describeEvent(event, context) : undefined;\n },\n affected(patch) {\n switch (patch.type) {\n case \"event.add\":\n return [patch.event.id];\n case \"event.remove\":\n case \"event.move\":\n return [patch.id];\n default:\n return \"none\";\n }\n },\n },\n });\n}\n\nexport interface SelectionApi {\n /** What is selected, in the order it was selected. */\n readonly current: readonly TargetRef[];\n /** What is under the pointer. */\n readonly hovered: TargetRef | null;\n select(targets: TargetRef | readonly TargetRef[], options?: { readonly additive?: boolean }): void;\n clear(): void;\n has(target: TargetRef): boolean;\n /** Called when the selection or the hover changes. Plugs into Angular signals, Vue, React… */\n subscribe(listener: (selection: readonly TargetRef[]) => void): Unsubscribe;\n}\n\nexport interface SelectionFeatureOptions extends SelectionToolOptions {\n /** Replaces parts of the default skin: `{ selection: myGlow }`. */\n readonly skin?: Skin;\n}\n\n/** Hover, selection, rubber band, dragging and deleting. Needs the elements feature. */\nexport function selection(options: SelectionFeatureOptions = {}): MapFeature<\"selection\", SelectionApi> {\n const { skin, ...toolOptions } = options;\n return defineMapFeature({\n id: SELECTION_FEATURE_ID,\n requires: [ELEMENTS_FEATURE_ID],\n setup(host) {\n const controller = host.interaction;\n const stops: Unsubscribe[] = [\n registerElementIntents(controller as unknown as InteractionController),\n controller.addTool(selectionTool(toolOptions)),\n mountSkinLayers(\n host,\n (() => {\n const look = mergeSkin(defaultSkin(), skin);\n return [look.selection, look.hover, look.drag, look.marquee];\n })(),\n ),\n ];\n const api: SelectionApi = {\n get current() {\n return controller.state.get().selection;\n },\n get hovered() {\n return controller.state.get().hovered;\n },\n select(targets, selectOptions) {\n const list = Array.isArray(targets) ? (targets as readonly TargetRef[]) : [targets as TargetRef];\n const base = selectOptions?.additive ? controller.state.get().selection : [];\n const next = [...base];\n for (const target of list) if (!next.some((other) => sameTarget(other, target))) next.push(target);\n controller.state.update({ selection: next });\n },\n clear: () => controller.state.update({ selection: [] }),\n has: (target) => controller.state.get().selection.some((other) => sameTarget(other, target)),\n subscribe(listener) {\n let last = controller.state.get();\n return controller.state.subscribe((state) => {\n if (state.selection !== last.selection || state.hovered !== last.hovered) listener(state.selection);\n last = state;\n });\n },\n };\n return { api, dispose: () => stops.forEach((stop) => stop()) };\n },\n });\n}\n\nexport interface PlacementFeatureOptions<C extends Catalog<string>> extends PlacementOptions<C> {\n /** Replaces parts of the default skin: `{ ghost: myGhost }`. */\n readonly skin?: Skin;\n}\n\n/** Placing things of a catalog, with a ghost that previews the rules. Needs the elements feature. */\nexport function placement<const C extends Catalog<string>>(\n options: PlacementFeatureOptions<C>,\n): MapFeature<\"placement\", PlacementApi<CatalogId<C>>> {\n const { skin, ...placementOptions } = options;\n return defineMapFeature({\n id: PLACEMENT_FEATURE_ID,\n requires: [ELEMENTS_FEATURE_ID],\n setup(host) {\n const placed = createPlacement(host.interaction as never, placementOptions);\n const unmount = mountSkinLayers(host, [mergeSkin(defaultSkin(), skin).ghost]);\n return {\n api: placed.api,\n dispose() {\n unmount();\n placed.dispose();\n },\n };\n },\n });\n}\n\n/**\n * Undo and redo of the model, on the view: `view.history.undo()`.\n *\n * The feature cannot know whether the model answers at once or later, so results are\n * typed `MaybePromise<ApplyResult>`. With a local committer, say so: `history<ApplyResult>()`.\n */\nexport function history<R extends MaybePromise<ApplyResult> = MaybePromise<ApplyResult>>(): MapFeature<\"history\", MapHistory<R>> {\n return defineMapFeature({\n id: HISTORY_FEATURE_ID,\n setup(host) {\n const model = host.model;\n if (!model.history) throw new Error(\"The history feature needs a model that has a history\");\n return { api: model.history as unknown as MapHistory<R> };\n },\n });\n}\n\nexport {\n terrain,\n canvasSurface,\n decodeRaster,\n memorySurface,\n TERRAIN_FEATURE_ID,\n type TerrainApi,\n type TerrainOptions,\n type TerrainSurface,\n type TerrainSurfaceFactory,\n} from \"./terrain\";\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FeatureHost } from '../renderer/feature';
|
|
2
|
+
import { SkinLayerFactory } from '../skins/types';
|
|
3
|
+
/**
|
|
4
|
+
* Puts skin layers in the overlay stage and keeps them up to date: they redraw when
|
|
5
|
+
* the interaction state changes, when the map changes, and animate on each tick.
|
|
6
|
+
* Returns the function that removes them.
|
|
7
|
+
*/
|
|
8
|
+
export declare function mountSkinLayers(host: FeatureHost, factories: ReadonlyArray<SkinLayerFactory | undefined>): () => void;
|
|
9
|
+
//# sourceMappingURL=skin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skin.d.ts","sourceRoot":"","sources":["../../src/features/skin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,gBAAgB,GAAG,SAAS,CAAC,GAAG,MAAM,IAAI,CA6BrH"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Container } from "pixi.js";
|
|
2
|
+
//#region src/features/skin.ts
|
|
3
|
+
/**
|
|
4
|
+
* Puts skin layers in the overlay stage and keeps them up to date: they redraw when
|
|
5
|
+
* the interaction state changes, when the map changes, and animate on each tick.
|
|
6
|
+
* Returns the function that removes them.
|
|
7
|
+
*/
|
|
8
|
+
function mountSkinLayers(host, factories) {
|
|
9
|
+
const containers = [];
|
|
10
|
+
const layers = [];
|
|
11
|
+
const refresh = () => {
|
|
12
|
+
const snapshot = host.interaction.state.get();
|
|
13
|
+
for (const layer of layers) layer.update(snapshot);
|
|
14
|
+
};
|
|
15
|
+
for (const factory of factories) {
|
|
16
|
+
if (!factory) continue;
|
|
17
|
+
const container = new Container();
|
|
18
|
+
host.stages.overlay.addChild(container);
|
|
19
|
+
containers.push(container);
|
|
20
|
+
layers.push(factory({
|
|
21
|
+
layer: container,
|
|
22
|
+
textures: host.textures,
|
|
23
|
+
assets: host.assets,
|
|
24
|
+
nodes: host.describe,
|
|
25
|
+
requestUpdate: refresh
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
const stops = [
|
|
29
|
+
host.interaction.state.subscribe(refresh),
|
|
30
|
+
host.model.subscribe(refresh),
|
|
31
|
+
host.onTick((deltaMs) => layers.forEach((layer) => layer.tick?.(deltaMs)))
|
|
32
|
+
];
|
|
33
|
+
refresh();
|
|
34
|
+
return () => {
|
|
35
|
+
stops.forEach((stop) => stop());
|
|
36
|
+
layers.forEach((layer) => layer.destroy());
|
|
37
|
+
containers.forEach((container) => container.destroy({ children: true }));
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { mountSkinLayers };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=skin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skin.js","names":[],"sources":["../../src/features/skin.ts"],"sourcesContent":["import { Container } from \"pixi.js\";\nimport type { FeatureHost } from \"../renderer/feature\";\nimport type { SkinLayer, SkinLayerFactory } from \"../skins/types\";\n\n/**\n * Puts skin layers in the overlay stage and keeps them up to date: they redraw when\n * the interaction state changes, when the map changes, and animate on each tick.\n * Returns the function that removes them.\n */\nexport function mountSkinLayers(host: FeatureHost, factories: ReadonlyArray<SkinLayerFactory | undefined>): () => void {\n const containers: Container[] = [];\n const layers: SkinLayer[] = [];\n const refresh = (): void => {\n const snapshot = host.interaction.state.get();\n for (const layer of layers) layer.update(snapshot);\n };\n\n for (const factory of factories) {\n if (!factory) continue;\n const container = new Container();\n host.stages.overlay.addChild(container);\n containers.push(container);\n layers.push(factory({ layer: container, textures: host.textures, assets: host.assets, nodes: host.describe, requestUpdate: refresh }));\n }\n\n const stops = [\n host.interaction.state.subscribe(refresh),\n // The view subscribed to the model before any feature, so the scene is already up to date here.\n host.model.subscribe(refresh),\n host.onTick((deltaMs) => layers.forEach((layer) => layer.tick?.(deltaMs))),\n ];\n refresh();\n\n return () => {\n stops.forEach((stop) => stop());\n layers.forEach((layer) => layer.destroy());\n containers.forEach((container) => container.destroy({ children: true }));\n };\n}\n"],"mappings":";;;;;;;AASA,SAAgB,gBAAgB,MAAmB,WAAoE;CACrH,MAAM,aAA0B,CAAC;CACjC,MAAM,SAAsB,CAAC;CAC7B,MAAM,gBAAsB;EAC1B,MAAM,WAAW,KAAK,YAAY,MAAM,IAAI;EAC5C,KAAK,MAAM,SAAS,QAAQ,MAAM,OAAO,QAAQ;CACnD;CAEA,KAAK,MAAM,WAAW,WAAW;EAC/B,IAAI,CAAC,SAAS;EACd,MAAM,YAAY,IAAI,UAAU;EAChC,KAAK,OAAO,QAAQ,SAAS,SAAS;EACtC,WAAW,KAAK,SAAS;EACzB,OAAO,KAAK,QAAQ;GAAE,OAAO;GAAW,UAAU,KAAK;GAAU,QAAQ,KAAK;GAAQ,OAAO,KAAK;GAAU,eAAe;EAAQ,CAAC,CAAC;CACvI;CAEA,MAAM,QAAQ;EACZ,KAAK,YAAY,MAAM,UAAU,OAAO;EAExC,KAAK,MAAM,UAAU,OAAO;EAC5B,KAAK,QAAQ,YAAY,OAAO,SAAS,UAAU,MAAM,OAAO,OAAO,CAAC,CAAC;CAC3E;CACA,QAAQ;CAER,aAAa;EACX,MAAM,SAAS,SAAS,KAAK,CAAC;EAC9B,OAAO,SAAS,UAAU,MAAM,QAAQ,CAAC;EACzC,WAAW,SAAS,cAAc,UAAU,QAAQ,EAAE,UAAU,KAAK,CAAC,CAAC;CACzE;AACF"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Texture } from 'pixi.js';
|
|
2
|
+
import { RasterImage } from '@rpgjs/render-map2d';
|
|
3
|
+
import { PipelineStep } from '../interaction/pipeline';
|
|
4
|
+
import { MapFeature } from '../renderer/feature';
|
|
5
|
+
import { TerrainBackend } from '../terrain-render/backend';
|
|
6
|
+
import { readTerrain } from '../terrain/data';
|
|
7
|
+
import { BrushApi } from '../terrain/editing';
|
|
8
|
+
export declare const TERRAIN_FEATURE_ID = "terrain";
|
|
9
|
+
/** One chunk of the terrain as pixels: a texture, and a way to write into it. */
|
|
10
|
+
export interface TerrainSurface {
|
|
11
|
+
readonly texture: Texture;
|
|
12
|
+
/** Writes RGBA pixels at `(x, y)` inside the surface, then makes the change visible. */
|
|
13
|
+
put(x: number, y: number, width: number, height: number, pixels: Uint8ClampedArray): void;
|
|
14
|
+
destroy(): void;
|
|
15
|
+
}
|
|
16
|
+
export type TerrainSurfaceFactory = (width: number, height: number) => TerrainSurface;
|
|
17
|
+
export interface TerrainOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Where the kernel runs. Defaults to the calling thread, which is fine for small maps and tests.
|
|
20
|
+
* For live painting use `workerTerrainBackend` from `@rpgjs/map-builder/terrain-render`.
|
|
21
|
+
*/
|
|
22
|
+
readonly backend?: TerrainBackend | (() => TerrainBackend | Promise<TerrainBackend>);
|
|
23
|
+
/** Decodes an image into pixels. Defaults to the browser (`createImageBitmap`). */
|
|
24
|
+
readonly loadImage?: (src: string) => Promise<RasterImage>;
|
|
25
|
+
/** Side of the squares the terrain is rendered in. Defaults to 512. */
|
|
26
|
+
readonly chunk?: number;
|
|
27
|
+
/** How chunk textures are made. Defaults to canvases; replaceable for tests. */
|
|
28
|
+
readonly surface?: TerrainSurfaceFactory;
|
|
29
|
+
/** Receives non-fatal problems (an image that did not load, a render that failed). Defaults to `console.warn`. */
|
|
30
|
+
readonly onError?: (error: unknown) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Make the terrain editable: registers the `terrain.paint` intent and the brush (`view.terrain.brush`).
|
|
33
|
+
* Passing `pipeline` also turns it on.
|
|
34
|
+
*/
|
|
35
|
+
readonly edit?: boolean;
|
|
36
|
+
/** Rules every painting goes through, whoever asks: the brush, `view.submit`, a game. */
|
|
37
|
+
readonly pipeline?: readonly PipelineStep<"terrain.paint">[];
|
|
38
|
+
/** Modes in which the brush is active. Defaults to `["edit"]`. */
|
|
39
|
+
readonly brushModes?: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
export interface TerrainApi {
|
|
42
|
+
/** Resolves when everything that was asked to be (re)rendered has been. */
|
|
43
|
+
whenIdle(): Promise<void>;
|
|
44
|
+
/** Chunks waiting or being rendered. */
|
|
45
|
+
readonly pending: number;
|
|
46
|
+
/** The backend in use, once there is one. */
|
|
47
|
+
readonly backend: TerrainBackend | undefined;
|
|
48
|
+
/** The brush, when the terrain is editable (`terrain({ edit: true })`). */
|
|
49
|
+
readonly brush: BrushApi | undefined;
|
|
50
|
+
}
|
|
51
|
+
/** Pixels of an image, decoded in the browser. */
|
|
52
|
+
export declare function decodeRaster(src: string): Promise<RasterImage>;
|
|
53
|
+
/** Surfaces backed by canvases (nearest filtering, 1:1: the pixels are what the kernel produced). */
|
|
54
|
+
export declare const canvasSurface: TerrainSurfaceFactory;
|
|
55
|
+
/** Surfaces that only keep their pixels in memory: for tests and tools that read them back. */
|
|
56
|
+
export declare function memorySurface(): TerrainSurfaceFactory & {
|
|
57
|
+
pixelsOf(texture: Texture): Uint8ClampedArray | undefined;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Draws the painted terrain of the map (`extensions.terrain`): the control mask, the materials and
|
|
61
|
+
* their render modes, rendered by the kernel of `@rpgjs/render-map2d` into chunk textures.
|
|
62
|
+
* Painting changes only the chunks it touches. What is on screen renders first.
|
|
63
|
+
*/
|
|
64
|
+
export declare function terrain(options?: TerrainOptions): MapFeature<"terrain", TerrainApi>;
|
|
65
|
+
export { readTerrain };
|
|
66
|
+
//# sourceMappingURL=terrain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terrain.d.ts","sourceRoot":"","sources":["../../src/features/terrain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,OAAO,EAAiB,MAAM,SAAS,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAkB,MAAM,iBAAiB,CAAC;AAI9D,OAAO,EAAwC,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGzF,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,wFAAwF;IACxF,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1F,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,cAAc,CAAC;AAEtF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IACrF,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3D,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IACzC,kHAAkH;IAClH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;IAC7D,kEAAkE;IAClE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAC;IAC7C,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC;CACtC;AAED,kDAAkD;AAClD,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAUpE;AAED,qGAAqG;AACrG,eAAO,MAAM,aAAa,EAAE,qBAe3B,CAAC;AAEF,+FAA+F;AAC/F,wBAAgB,aAAa,IAAI,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,CAAA;CAAE,CAerH;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,UAAU,CAAC,SAAS,EAAE,UAAU,CAAC,CAyNvF;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|