@runmu.sh/ext-scene 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +23 -3
- package/dist/index.js +176 -1
- package/package.json +4 -3
- package/src/types.ts +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.1
|
|
4
|
+
|
|
5
|
+
- After a reload or a reconnect the Scene shows the whole room again (description, who is here, exits), not only its title: when the room name arrives it reads the newest room look among the lines the client already holds.
|
|
6
|
+
|
|
7
|
+
## 1.3.0
|
|
8
|
+
|
|
9
|
+
- The room on games that send only its name. Evennia games such as Underspire send no GMCP `Room.Info` or `Room.Players` over telnet, so the Scene showed a title and nothing else. The extension now reads the room look the game prints (title, description, who is here, the exits line) and GMCP `Room.Name` and `Player.Context`, and fills the description, Present and Exits, as Underspire's own panel does. Moving fills the next room from its auto-look.
|
|
10
|
+
- It steps aside as soon as a game sends GMCP `Room.Info` or MSDP `ROOM_EXITS` / `ROOM_VNUM`: those games are unchanged.
|
|
11
|
+
- New setting **Read the room from the game text** (per world, default on).
|
|
12
|
+
- Asks for `read-output` as well as `send-commands`: it reads the game text (an observe stage: read only, live lines only).
|
|
13
|
+
- Exports `roomOf`, `exitsOf`, `peopleOf`, `roomNameOf`, `contextOf` and `TEXT_PRIORITY`.
|
|
14
|
+
|
|
3
15
|
## 1.2.0
|
|
4
16
|
|
|
5
17
|
- Alt+R (*Go to scene*) is now the extension's own command, `focus.scene`, in Settings → Keys under Scene: Focus. On μClient with extension API 1.14 it focuses the Scene panel when it is open; a rebinding you made keeps working. On an older μClient the client's own Alt+R stays in place.
|
package/README.md
CHANGED
|
@@ -16,6 +16,24 @@ The host keeps one scene per session and fills it from:
|
|
|
16
16
|
| GMCP `Char.Items.List` / `Add` / `Remove` with `location: "room"` | Present (items; `hostile: true` is marked) |
|
|
17
17
|
| MSDP `ROOM_NAME`, `ROOM_VNUM`, `ROOM_EXITS`, `AREA_NAME` (or `ROOM.*`) | Title, exits, area |
|
|
18
18
|
| Another extension: `mu.scene.set({ title, area, desc, atmosphere, pose, exits, present, items })` | Any field |
|
|
19
|
+
| This extension, from the game (1.3.0): GMCP `Room.Name` (a string or `{ name }`), `Player.Context` (`room`, `pose_line`, `presence`, without your own `character`), and the room text | Title, description, pose, Present (people), exits |
|
|
20
|
+
|
|
21
|
+
### Games that send only the room name (1.3.0)
|
|
22
|
+
Evennia games such as Underspire send no `Room.Info` or `Room.Players` over telnet: only `Room.Name`, and `Player.Context` once at login. For those the extension reads the room look the game prints:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Shard intake ← title (no closing punctuation)
|
|
26
|
+
A vast data space, humming with virtual activity. ← description
|
|
27
|
+
|
|
28
|
+
The intake terminal is installed here. ← things in the room (skipped)
|
|
29
|
+
|
|
30
|
+
^A2PGUE is standing here. You are standing here. ← Present (you are left out)
|
|
31
|
+
There are exits to the san junipero (back). ← exits, by name; ends the look
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The exits line (`There are exits to …`, `There is an exit to …`, `Exits: …`) closes a look; the lines before it are read back to the nearest title line. After a reload or reconnect (the last look is then backlog), the room name arriving makes it read the newest room look among the lines the client holds, once per session. A typed command in between starts over, so the text of a `look` at a person or an item is not taken for a room. An exit button sends the exit's name (`san junipero`), which Evennia accepts as well as its alias.
|
|
35
|
+
|
|
36
|
+
The text reader provides at priority 10, over the MSDP adapter's room name, and turns itself off for a session as soon as the game sends GMCP `Room.Info` or MSDP `ROOM_EXITS` / `ROOM_VNUM`; its fields are then released to the adapters. MSDP `ROOM_NAME` alone (Underspire mirrors `Room.Name` there) does not turn it off. Setting **Read the room from the game text** (per world, default on) turns it off.
|
|
19
37
|
|
|
20
38
|
Package names are case-insensitive. Entering a new room clears Present. The room title is also the session's location (the world switcher shows it).
|
|
21
39
|
|
|
@@ -29,10 +47,10 @@ The panel is the region `scene`. **Alt+R** (*Go to scene*, command `focus.scene`
|
|
|
29
47
|
- **Show panel: auto** (per world, on the extension's settings page: off / auto / on). On auto the Scene joins Views and adds itself the first time a session knows its room, whatever the room came from (GMCP, MSDP, another extension); once per world on this device, so a Scene you closed stays closed.
|
|
30
48
|
- A redraw in the same room (someone arrives, an item drops) keeps your scroll position; moving to another room scrolls back to the top. When the game sends no room id, every update counts as a move.
|
|
31
49
|
- Right-click (or long-press) an exit or a room item for the menus other extensions add to them. The extension registers the context kinds `scene.exit` (title *Exit*, `data: { exit }`) and `scene.item` (title *Scene item*, `data: { item: { id, name, hostile? } }`), each with a schema the host checks. On a μClient before API 1.14 it publishes the 1.12 kinds `scene-exit` and `scene-item` instead; on 1.14 an entry written for the old kinds still receives the new targets in the old shape.
|
|
32
|
-
- It sends nothing to the game except the exits you click
|
|
50
|
+
- It sends nothing to the game except the exits you click. On a game with GMCP `Room.Info` or MSDP rooms the room adapters fill the scene; otherwise it reads `Room.Name`, `Player.Context` and the room text itself (1.3.0, above).
|
|
33
51
|
|
|
34
52
|
## Contract
|
|
35
|
-
The extension
|
|
53
|
+
The extension sends no GMCP and declares no packages or payload contracts; the room adapters declare `Room 1` and feed `mu.scene` (`SceneView`). It listens to `Room.Name` and `Player.Context` (Evennia sends them unasked) and `Room.Info` / MSDP `ROOM_EXITS` / `ROOM_VNUM` only to step aside. Capabilities: `read-output` (the room text, an `observe` line stage: read only, live lines only) and `send-commands` (the exit buttons).
|
|
36
54
|
|
|
37
55
|
## Exported API (`@runmu.sh/ext-scene`, types in `src/types.ts`)
|
|
38
56
|
No `ctx.exports` API. The module exports, for tests and other renderers:
|
|
@@ -43,11 +61,13 @@ No `ctx.exports` API. The module exports, for tests and other renderers:
|
|
|
43
61
|
| `presentOf(scene)` | People first, then room items, each name once: `{ name, hostile? }[]`. |
|
|
44
62
|
| `COPY`, `SCENE_CSS` | The drawn copy and the stylesheet (every rule under `.ext-panel[data-ext="scene"] .mu-scene`). |
|
|
45
63
|
| `ITEM_KIND`, `EXIT_KIND`, `KIND_SCHEMAS` | `'scene.item'`, `'scene.exit'` and their `data` schemas. |
|
|
64
|
+
| `roomOf(lines)`, `exitsOf(line)`, `peopleOf(line)` | The room text reader (1.3.0): a `ScenePatch` (`title`, `desc`, `exits`, `present`) from lines ending in an exits line, or `null`; the exit names of an exits line; the people (not you) of a who-is-here line. |
|
|
65
|
+
| `roomNameOf(data)`, `contextOf(data)`, `TEXT_PRIORITY` | `Room.Name` → title; `Player.Context` → `{ title, pose, present }`; the provider priority (10). |
|
|
46
66
|
|
|
47
67
|
`src/types.ts` augments the SDK's `ContextKinds` with `scene.item` (`SceneItemData`) and `scene.exit` (`SceneExitData`), so an extension that imports it gets `t.data` typed in `mu.menus.context({ target: 'scene.exit', … })`.
|
|
48
68
|
|
|
49
69
|
## SDK
|
|
50
|
-
`api` `^1.12`. SDK 1.12: `mu.panels.register` with `show: 'auto'`, `mu.panels.touch`, `mu.sessions.each`, `mu.scene.watch` (and `SceneView.id`), `mu.sessions.send(dir, { sid })`, `mu.menus.target`, `h` and `mu.ui.css`, `mu.ui.style`. SDK 1.14, used when the host has them: `mu.menus.kind` (with `JsonSchema` and `ContextKinds`), `mu.panels.focus` with `mu.commands.register`. The manifest declares `focus.scene` in `contributes.commands`, so Alt+R is listed and bound before the extension activates.
|
|
70
|
+
`api` `^1.12`. SDK 1.12: `mu.panels.register` with `show: 'auto'`, `mu.panels.touch`, `mu.sessions.each`, `mu.scene.watch` (and `SceneView.id`), `mu.sessions.send(dir, { sid })`, `mu.menus.target`, `h` and `mu.ui.css`, `mu.ui.style`, `mu.scene.provide`, `mu.gmcp.on` / `state`, `mu.msdp.on` / `state`, `mu.lines.stage` (phase `observe`), `mu.settings.define`. SDK 1.14, used when the host has them: `mu.menus.kind` (with `JsonSchema` and `ContextKinds`), `mu.panels.focus` with `mu.commands.register`. The manifest declares `focus.scene` in `contributes.commands`, so Alt+R is listed and bound before the extension activates.
|
|
51
71
|
|
|
52
72
|
## Develop
|
|
53
73
|
Made with `npm create @runmu.sh/extension` ([the quickstart](https://runmu.sh/docs/extensions/quickstart)).
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,80 @@ import { defineExtension, h } from "@muclient/sdk";
|
|
|
5
5
|
var ITEM_KIND = "scene.item";
|
|
6
6
|
var EXIT_KIND = "scene.exit";
|
|
7
7
|
|
|
8
|
+
// src/room.ts
|
|
9
|
+
var EXITS_RE = /^(?:there (?:are|is) (?:an? )?(?:exits?|ways?(?: out)?) (?:to|leading to) |(?:obvious )?exits?: )(.+?)\.?$/i;
|
|
10
|
+
var HERE_RE = /^(.+?) (?:is|are) (?:[a-z]+ )?here\.?$/i;
|
|
11
|
+
var MAX_TITLE = 80;
|
|
12
|
+
function exitsOf(line) {
|
|
13
|
+
const m = EXITS_RE.exec(line.trim());
|
|
14
|
+
if (!m) return null;
|
|
15
|
+
const out = [];
|
|
16
|
+
for (let part of m[1].split(/\s*,\s*(?:and\s+)?|\s+and\s+/)) {
|
|
17
|
+
part = part.replace(/\s*\([^)]*\)\s*$/, "").replace(/^the\s+/i, "").trim();
|
|
18
|
+
if (part && !out.includes(part)) out.push(part);
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
function peopleOf(line) {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const s of line.trim().split(/(?<=\.)\s+/)) {
|
|
25
|
+
const m = HERE_RE.exec(s);
|
|
26
|
+
if (!m) continue;
|
|
27
|
+
for (const n of m[1].split(/\s*,\s*(?:and\s+)?|\s+and\s+/)) {
|
|
28
|
+
const name = n.trim();
|
|
29
|
+
if (name && !/^you$/i.test(name) && !out.includes(name)) out.push(name);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
var looksTitle = (t) => t.length > 0 && t.length <= MAX_TITLE && !/[.!?:;,"')>\]]$/.test(t);
|
|
35
|
+
function roomOf(lines, maxSections = 4) {
|
|
36
|
+
if (!lines.length) return null;
|
|
37
|
+
const exits = exitsOf(lines[lines.length - 1]);
|
|
38
|
+
if (!exits) return null;
|
|
39
|
+
const sections = [[]];
|
|
40
|
+
for (const raw of lines.slice(0, -1)) {
|
|
41
|
+
const l = raw.trim();
|
|
42
|
+
if (l) sections[sections.length - 1].push(l);
|
|
43
|
+
else if (sections[sections.length - 1].length) sections.push([]);
|
|
44
|
+
}
|
|
45
|
+
if (!sections[sections.length - 1].length) sections.pop();
|
|
46
|
+
if (!sections.length) return null;
|
|
47
|
+
const tail = sections[sections.length - 1];
|
|
48
|
+
const people = tail.some((l) => peopleOf(l).length || /^you (?:are|is) [a-z ]*here\.?$/i.test(l));
|
|
49
|
+
for (let i = sections.length - 1; i >= Math.max(0, sections.length - maxSections); i--) {
|
|
50
|
+
const sec = sections[i];
|
|
51
|
+
let t = -1;
|
|
52
|
+
for (let j = sec.length - 1; j >= 0; j--) if (looksTitle(sec[j]) && !HERE_RE.test(sec[j])) {
|
|
53
|
+
t = j;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
if (t < 0) continue;
|
|
57
|
+
if (i === sections.length - 1 && people) continue;
|
|
58
|
+
const present = people ? [...new Set(tail.flatMap(peopleOf))] : [];
|
|
59
|
+
return { title: sec[t], desc: sec.slice(t + 1).join("\n"), exits, present };
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function roomNameOf(data) {
|
|
64
|
+
if (typeof data === "string") return data.trim();
|
|
65
|
+
if (data && typeof data === "object" && typeof data.name === "string") return data.name.trim();
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
function contextOf(data) {
|
|
69
|
+
if (!data || typeof data !== "object") return null;
|
|
70
|
+
const d = data;
|
|
71
|
+
const me = typeof d.character === "string" ? d.character.trim().toLowerCase() : "";
|
|
72
|
+
const patch = {};
|
|
73
|
+
if (typeof d.room === "string" && d.room.trim()) patch.title = d.room.trim();
|
|
74
|
+
if (typeof d.pose_line === "string") patch.pose = d.pose_line.trim();
|
|
75
|
+
if (Array.isArray(d.presence)) patch.present = [...new Set(d.presence.filter((n) => typeof n === "string" && !!n.trim() && n.trim().toLowerCase() !== me).map((n) => n.trim()))];
|
|
76
|
+
return Object.keys(patch).length ? patch : null;
|
|
77
|
+
}
|
|
78
|
+
|
|
8
79
|
// src/index.ts
|
|
80
|
+
var TEXT_PRIORITY = 10;
|
|
81
|
+
var KEEP = 40;
|
|
9
82
|
var COPY = {
|
|
10
83
|
title: "Scene",
|
|
11
84
|
awaiting: "No room yet",
|
|
@@ -153,6 +226,102 @@ var index_default = defineExtension({
|
|
|
153
226
|
mu.panels.focus?.("scene");
|
|
154
227
|
} });
|
|
155
228
|
}
|
|
229
|
+
mu.settings.define({
|
|
230
|
+
title: "Scene",
|
|
231
|
+
items: [{
|
|
232
|
+
key: "fromText",
|
|
233
|
+
label: "Read the room from the game text",
|
|
234
|
+
default: true,
|
|
235
|
+
kind: "toggle",
|
|
236
|
+
scope: "both",
|
|
237
|
+
hint: "For games that send only the room name over GMCP (Evennia games such as Underspire): the description, who is here and the exits come from the room text. A game that sends GMCP Room.Info is unaffected."
|
|
238
|
+
}]
|
|
239
|
+
});
|
|
240
|
+
const fromText = (sid) => mu.settings.get("fromText", { sid }) !== false;
|
|
241
|
+
const hasRoom = (sid) => mu.gmcp.state("Room.Info", sid) !== void 0 || mu.msdp.state("ROOM_EXITS", sid) !== void 0 || mu.msdp.state("ROOM_VNUM", sid) !== void 0;
|
|
242
|
+
const given = /* @__PURE__ */ new Map();
|
|
243
|
+
const give = (sid, patch) => {
|
|
244
|
+
given.set(sid, mu.scene.provide(sid, patch, { priority: TEXT_PRIORITY }));
|
|
245
|
+
};
|
|
246
|
+
const release = (sid) => {
|
|
247
|
+
given.get(sid)?.();
|
|
248
|
+
given.delete(sid);
|
|
249
|
+
};
|
|
250
|
+
const seen = /* @__PURE__ */ new Map();
|
|
251
|
+
const titles = /* @__PURE__ */ new Map();
|
|
252
|
+
const seeded = /* @__PURE__ */ new Set();
|
|
253
|
+
const seed = (sid) => {
|
|
254
|
+
if (seeded.has(sid) || hasRoom(sid) || !fromText(sid)) return;
|
|
255
|
+
seeded.add(sid);
|
|
256
|
+
let lines = [];
|
|
257
|
+
try {
|
|
258
|
+
lines = mu.lines.recent(sid, { limit: 120 });
|
|
259
|
+
} catch {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
for (let end = lines.length - 1; end >= 0; end--) {
|
|
263
|
+
if (lines[end].kind === "echo" || !exitsOf(lines[end].text)) continue;
|
|
264
|
+
let start = end;
|
|
265
|
+
while (start > 0 && end - start < KEEP && lines[start - 1].kind !== "echo") start--;
|
|
266
|
+
const room = roomOf(lines.slice(start, end + 1).map((l) => l.text));
|
|
267
|
+
if (room) {
|
|
268
|
+
titles.set(sid, room.title ?? "");
|
|
269
|
+
give(sid, room);
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
ctx.subscriptions.push(
|
|
275
|
+
mu.gmcp.on("Room.Info", (_d, m) => release(m.sid)),
|
|
276
|
+
mu.msdp.on("ROOM_EXITS", (_v, m) => release(m.sid)),
|
|
277
|
+
mu.msdp.on("ROOM_VNUM", (_v, m) => release(m.sid)),
|
|
278
|
+
mu.gmcp.on("Room.Name", (d, m) => {
|
|
279
|
+
const title = roomNameOf(d);
|
|
280
|
+
if (!title || hasRoom(m.sid) || !fromText(m.sid)) return;
|
|
281
|
+
if (titles.get(m.sid) !== title) {
|
|
282
|
+
titles.set(m.sid, title);
|
|
283
|
+
give(m.sid, { title });
|
|
284
|
+
}
|
|
285
|
+
seed(m.sid);
|
|
286
|
+
}),
|
|
287
|
+
mu.gmcp.on("Player.Context", (d, m) => {
|
|
288
|
+
const patch = contextOf(d);
|
|
289
|
+
if (!patch || hasRoom(m.sid) || !fromText(m.sid)) return;
|
|
290
|
+
if (patch.title) titles.set(m.sid, patch.title);
|
|
291
|
+
give(m.sid, patch);
|
|
292
|
+
seed(m.sid);
|
|
293
|
+
}),
|
|
294
|
+
// Observe: read only, live lines only (not backlog). A typed command starts a new block.
|
|
295
|
+
mu.lines.stage({
|
|
296
|
+
id: "room-text",
|
|
297
|
+
phase: "observe",
|
|
298
|
+
run: (line, c) => {
|
|
299
|
+
if (line.kind === "echo") {
|
|
300
|
+
seen.delete(c.sid);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (line.kind !== "output" && line.kind !== "prompt") return;
|
|
304
|
+
const buf = seen.get(c.sid) ?? [];
|
|
305
|
+
buf.push(line.text);
|
|
306
|
+
if (buf.length > KEEP) buf.splice(0, buf.length - KEEP);
|
|
307
|
+
seen.set(c.sid, buf);
|
|
308
|
+
const room = roomOf(buf);
|
|
309
|
+
if (!room) return;
|
|
310
|
+
seen.delete(c.sid);
|
|
311
|
+
seeded.add(c.sid);
|
|
312
|
+
if (hasRoom(c.sid) || !fromText(c.sid)) return;
|
|
313
|
+
const moved = titles.get(c.sid) !== room.title;
|
|
314
|
+
titles.set(c.sid, room.title ?? "");
|
|
315
|
+
give(c.sid, moved ? { ...room, pose: "", items: [] } : room);
|
|
316
|
+
}
|
|
317
|
+
}),
|
|
318
|
+
mu.sessions.each((s) => () => {
|
|
319
|
+
seen.delete(s.id);
|
|
320
|
+
titles.delete(s.id);
|
|
321
|
+
given.delete(s.id);
|
|
322
|
+
seeded.delete(s.id);
|
|
323
|
+
})
|
|
324
|
+
);
|
|
156
325
|
ctx.subscriptions.push(mu.sessions.each((s) => {
|
|
157
326
|
let done = false;
|
|
158
327
|
return mu.scene.watch((v) => {
|
|
@@ -170,7 +339,13 @@ export {
|
|
|
170
339
|
ITEM_KIND,
|
|
171
340
|
KIND_SCHEMAS,
|
|
172
341
|
SCENE_CSS,
|
|
342
|
+
TEXT_PRIORITY,
|
|
343
|
+
contextOf,
|
|
173
344
|
index_default as default,
|
|
345
|
+
exitsOf,
|
|
346
|
+
peopleOf,
|
|
174
347
|
presentOf,
|
|
175
|
-
renderScene
|
|
348
|
+
renderScene,
|
|
349
|
+
roomNameOf,
|
|
350
|
+
roomOf
|
|
176
351
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runmu.sh/ext-scene",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "The Scene panel: the room you are in (title, area, atmosphere, description, pose), who is present and its exits as buttons, from GMCP Room.*, Char.Items.*, MSDP or
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "The Scene panel: the room you are in (title, area, atmosphere, description, pose), who is present and its exits as buttons, from GMCP Room.*, Char.Items.*, MSDP, another extension, or the room text (Evennia games such as Underspire).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"scene",
|
|
7
7
|
"room",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"api": "^1.12",
|
|
50
50
|
"source": "src/index.ts",
|
|
51
51
|
"displayName": "Scene",
|
|
52
|
-
"description": "The Scene panel: the current room, who is present and its exits as buttons, from GMCP Room.*, Char.Items.*, MSDP or
|
|
52
|
+
"description": "The Scene panel: the current room, who is present and its exits as buttons, from GMCP Room.*, Char.Items.*, MSDP, another extension, or the room text on games that send only the room name.",
|
|
53
53
|
"categories": [
|
|
54
54
|
"interface",
|
|
55
55
|
"mapping"
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
]
|
|
76
76
|
},
|
|
77
77
|
"capabilities": [
|
|
78
|
+
"read-output",
|
|
78
79
|
"send-commands"
|
|
79
80
|
],
|
|
80
81
|
"publisher": "bitmuse"
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The exported API of `@runmu.sh/ext-scene` (id `scene`). The extension
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* The exported API of `@runmu.sh/ext-scene` (id `scene`). The extension sends no GMCP: it draws the host's scene
|
|
3
|
+
* model (`mu.scene.watch`, `SceneView`), which the room adapters fill from GMCP `Room.*`, `Char.Items.*`, MSDP and
|
|
4
|
+
* other extensions (`mu.scene.set`). On games with none of those (Evennia: only `Room.Name`) it provides the room
|
|
5
|
+
* itself from `Room.Name`, `Player.Context` and the room text (1.3.0, `src/room.ts`). This file and the README
|
|
6
|
+
* change together.
|
|
5
7
|
*/
|
|
6
8
|
import type { SceneView } from '@muclient/sdk';
|
|
7
9
|
|