@songsid/agend 2.3.1 → 2.3.2-beta.10
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/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent-cli.js +21 -2
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +5 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/antigravity-usage-scan.d.ts +58 -0
- package/dist/antigravity-usage-scan.js +257 -0
- package/dist/antigravity-usage-scan.js.map +1 -0
- package/dist/antigravity-usage-worker.d.ts +1 -0
- package/dist/antigravity-usage-worker.js +10 -0
- package/dist/antigravity-usage-worker.js.map +1 -0
- package/dist/auth-api.js +4 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +4 -0
- package/dist/authority/authority-state.js +16 -10
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/session-authorizations.d.ts +7 -1
- package/dist/authority/session-authorizations.js +6 -4
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +19 -6
- package/dist/authority/web-authority.js +22 -8
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +64 -0
- package/dist/backend/kiro-identity.js +101 -0
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro-retired-model.d.ts +62 -0
- package/dist/backend/kiro-retired-model.js +160 -0
- package/dist/backend/kiro-retired-model.js.map +1 -0
- package/dist/backend/kiro.d.ts +71 -2
- package/dist/backend/kiro.js +352 -11
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +59 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-signin-guide.d.ts +1 -0
- package/dist/backend-signin-guide.js +9 -0
- package/dist/backend-signin-guide.js.map +1 -0
- package/dist/backend-signin-pending.d.ts +10 -0
- package/dist/backend-signin-pending.js +44 -0
- package/dist/backend-signin-pending.js.map +1 -0
- package/dist/browser-backend-signins.d.ts +56 -0
- package/dist/browser-backend-signins.js +189 -0
- package/dist/browser-backend-signins.js.map +1 -0
- package/dist/cache-ledger.d.ts +38 -2
- package/dist/cache-ledger.js +133 -8
- package/dist/cache-ledger.js.map +1 -1
- package/dist/cache-prices.d.ts +2 -1
- package/dist/cache-prices.js +5 -2
- package/dist/cache-prices.js.map +1 -1
- package/dist/cache-service.d.ts +68 -0
- package/dist/cache-service.js +355 -13
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +13 -0
- package/dist/channel/adapters/discord.js +103 -16
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +34 -7
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/tool-router.js +2 -1
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +11 -0
- package/dist/classic-channel-manager.d.ts +11 -0
- package/dist/classic-channel-manager.js +39 -1
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +21 -2
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.d.ts +7 -1
- package/dist/config-validator.js +92 -12
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +19 -0
- package/dist/config.js +31 -1
- package/dist/config.js.map +1 -1
- package/dist/cpu-quota.d.ts +23 -0
- package/dist/cpu-quota.js +86 -0
- package/dist/cpu-quota.js.map +1 -0
- package/dist/daemon.d.ts +57 -3
- package/dist/daemon.js +330 -50
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-permissions.d.ts +20 -6
- package/dist/dashboard-permissions.js +58 -9
- package/dist/dashboard-permissions.js.map +1 -1
- package/dist/delivery-outbox.d.ts +12 -1
- package/dist/delivery-outbox.js +67 -15
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +11 -1
- package/dist/doctor.js.map +1 -1
- package/dist/event-log.d.ts +5 -0
- package/dist/event-log.js +13 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-manager.d.ts +212 -0
- package/dist/fleet-manager.js +914 -111
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +1 -0
- package/dist/fleet-process-handlers.js +9 -1
- package/dist/fleet-process-handlers.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +9 -0
- package/dist/forged-envelope-scan.js +30 -5
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +5 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +14 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +28 -5
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +31 -8
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +138 -59
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +145 -61
- package/dist/guest-seen-people.d.ts +38 -0
- package/dist/guest-seen-people.js +78 -0
- package/dist/guest-seen-people.js.map +1 -0
- package/dist/instance-config-impact.js +14 -6
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +20 -0
- package/dist/instance-lifecycle.js +71 -13
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/kiro-db-reader.d.ts +5 -0
- package/dist/kiro-db-reader.js +18 -0
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.d.ts +2 -0
- package/dist/kiro-engine-status.js +9 -0
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-migrate.d.ts +49 -0
- package/dist/kiro-migrate.js +105 -0
- package/dist/kiro-migrate.js.map +1 -0
- package/dist/kiro-transcript-lane.d.ts +2 -0
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/kiro-transcript-worker.js +1 -0
- package/dist/kiro-transcript-worker.js.map +1 -1
- package/dist/kiro-tui-forensic.d.ts +7 -0
- package/dist/kiro-tui-forensic.js +110 -0
- package/dist/kiro-tui-forensic.js.map +1 -0
- package/dist/kiro-usage-run.d.ts +7 -0
- package/dist/kiro-usage-run.js +39 -0
- package/dist/kiro-usage-run.js.map +1 -0
- package/dist/kiro-usage-scan.d.ts +49 -0
- package/dist/kiro-usage-scan.js +223 -0
- package/dist/kiro-usage-scan.js.map +1 -0
- package/dist/kiro-usage-worker.d.ts +1 -0
- package/dist/kiro-usage-worker.js +10 -0
- package/dist/kiro-usage-worker.js.map +1 -0
- package/dist/kiro-usage.d.ts +58 -0
- package/dist/kiro-usage.js +310 -0
- package/dist/kiro-usage.js.map +1 -0
- package/dist/locale.js +120 -12
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -1
- package/dist/log-service.js.map +1 -1
- package/dist/login-url.d.ts +4 -0
- package/dist/login-url.js +22 -0
- package/dist/login-url.js.map +1 -0
- package/dist/mac-auto-restore.d.ts +117 -0
- package/dist/mac-auto-restore.js +294 -0
- package/dist/mac-auto-restore.js.map +1 -0
- package/dist/needs-you-hub.d.ts +2 -1
- package/dist/needs-you-hub.js +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +8 -2
- package/dist/needs-you.js +6 -0
- package/dist/needs-you.js.map +1 -1
- package/dist/notice-delivery.d.ts +16 -0
- package/dist/notice-delivery.js +35 -0
- package/dist/notice-delivery.js.map +1 -0
- package/dist/office-share-page.d.ts +16 -0
- package/dist/office-share-page.js +85 -0
- package/dist/office-share-page.js.map +1 -0
- package/dist/office-share-service.d.ts +91 -0
- package/dist/office-share-service.js +176 -0
- package/dist/office-share-service.js.map +1 -0
- package/dist/office-share.d.ts +258 -0
- package/dist/office-share.js +549 -0
- package/dist/office-share.js.map +1 -0
- package/dist/opencode-usage-scan.d.ts +28 -0
- package/dist/opencode-usage-scan.js +92 -0
- package/dist/opencode-usage-scan.js.map +1 -0
- package/dist/opencode-usage-worker.d.ts +1 -0
- package/dist/opencode-usage-worker.js +10 -0
- package/dist/opencode-usage-worker.js.map +1 -0
- package/dist/outbound-handlers.js +22 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +28 -2
- package/dist/outbound-schemas.js +29 -2
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +16 -2
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/quickstart-api.js +13 -2
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.js +2 -1
- package/dist/quickstart.js.map +1 -1
- package/dist/remote-card-answer.d.ts +83 -0
- package/dist/remote-card-answer.js +208 -0
- package/dist/remote-card-answer.js.map +1 -0
- package/dist/reply-triggers.d.ts +2 -0
- package/dist/reply-triggers.js +3 -0
- package/dist/reply-triggers.js.map +1 -0
- package/dist/settings-api.d.ts +28 -0
- package/dist/settings-api.js +148 -17
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +54 -17
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +12 -3
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-instance-patch.js +4 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +55 -6
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-host.d.ts +9 -0
- package/dist/setup-host.js +127 -6
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-signin-assets.d.ts +2 -0
- package/dist/setup-signin-assets.js +6 -0
- package/dist/setup-signin-assets.js.map +1 -0
- package/dist/setup-wizard.js +2 -0
- package/dist/setup-wizard.js.map +1 -1
- package/dist/start-attempt.d.ts +9 -5
- package/dist/start-attempt.js +27 -8
- package/dist/start-attempt.js.map +1 -1
- package/dist/start-failure-chat.d.ts +4 -0
- package/dist/start-failure-chat.js +23 -6
- package/dist/start-failure-chat.js.map +1 -1
- package/dist/start-failure-notice.d.ts +4 -0
- package/dist/start-failure-notice.js +8 -0
- package/dist/start-failure-notice.js.map +1 -1
- package/dist/start-failure.d.ts +25 -0
- package/dist/start-failure.js +75 -2
- package/dist/start-failure.js.map +1 -1
- package/dist/structured-signin-transport.d.ts +8 -0
- package/dist/structured-signin-transport.js +79 -0
- package/dist/structured-signin-transport.js.map +1 -0
- package/dist/structured-signin.d.ts +131 -0
- package/dist/structured-signin.js +388 -0
- package/dist/structured-signin.js.map +1 -0
- package/dist/tool-permissions.js +12 -0
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.js +6 -2
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +26 -2
- package/dist/transcript-sources.js +103 -40
- package/dist/transcript-sources.js.map +1 -1
- package/dist/turn-reply-guard.d.ts +25 -0
- package/dist/turn-reply-guard.js +111 -12
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +46 -9
- package/dist/ui/backend-signin.js +68 -12
- package/dist/ui/chat-ambient.js +1 -1
- package/dist/ui/chat-effects.js +4 -4
- package/dist/ui/chat-render.js +5 -2
- package/dist/ui/chat-strings.js +4 -4
- package/dist/ui/chat-thread.js +2 -2
- package/dist/ui/first-run.js +2 -2
- package/dist/ui/fleet-canvas-view.js +114 -0
- package/dist/ui/fleet-canvas.js +15 -77
- package/dist/ui/fleet-office-editor.js +94 -14
- package/dist/ui/fleet-office-layout.js +235 -0
- package/dist/ui/fleet-office-model.js +12 -6
- package/dist/ui/fleet-office-overview.js +167 -0
- package/dist/ui/fleet-office-pack.d.ts +44 -0
- package/dist/ui/fleet-office-pack.js +352 -37
- package/dist/ui/fleet-office-skin.js +220 -36
- package/dist/ui/fleet-office-store.js +112 -7
- package/dist/ui/fleet-office-view.js +155 -0
- package/dist/ui/fleet-office-zones.js +264 -42
- package/dist/ui/fleet-office.js +957 -160
- package/dist/ui/fleet-usage.js +26 -21
- package/dist/ui/office-share-dialog.js +145 -0
- package/dist/ui/office-share-page.js +164 -0
- package/dist/ui/panel-chat.js +9 -2
- package/dist/ui/panel-details.js +2 -2
- package/dist/ui/panel-fleet.js +11 -77
- package/dist/ui/panel-log.js +4 -4
- package/dist/ui/panel-needs.js +4 -4
- package/dist/ui/panel-settings.js +608 -167
- package/dist/ui/search-dialog.js +26 -7
- package/dist/ui/settings-appearance.js +82 -29
- package/dist/ui/settings-apply.js +42 -7
- package/dist/ui/settings-dialogs.js +76 -23
- package/dist/ui/settings-draft.js +51 -0
- package/dist/ui/settings-guests.js +255 -79
- package/dist/ui/settings-learn.js +2 -2
- package/dist/ui/settings-model.js +16 -6
- package/dist/ui/settings-registry.js +146 -0
- package/dist/ui/settings-strings.js +116 -44
- package/dist/ui/settings-web-authority.js +34 -23
- package/dist/ui/shared/app-i18n.js +18 -2
- package/dist/ui/shared/app-nav.js +10 -5
- package/dist/ui/shared/app-prefs.js +80 -2
- package/dist/ui/shared/app-route.js +39 -5
- package/dist/ui/shared/app-shell.js +73 -10
- package/dist/ui/shared/app-stream.js +3 -1
- package/dist/ui/shared/app.css +307 -17
- package/dist/ui/shared/app.js +3 -0
- package/dist/ui/shared/effect-stock-trust.js +1 -1
- package/dist/ui/shared/identity-color.js +4 -4
- package/dist/ui/shared/office/cozy.png +0 -0
- package/dist/ui/shared/side-panel.js +95 -0
- package/dist/ui/shared/signin.js +2 -2
- package/dist/ui/shared/ui-dialog.js +6 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/web-authorize.js +13 -12
- package/dist/ui/structured-signin.js +115 -0
- package/dist/usage-report.d.ts +20 -0
- package/dist/usage-report.js +54 -17
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.d.ts +9 -1
- package/dist/usage-rollup.js +20 -4
- package/dist/usage-rollup.js.map +1 -1
- package/dist/web-api.d.ts +26 -2
- package/dist/web-api.js +361 -5
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +8 -2
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +1 -7
- package/dist/web-backend-login.js +2 -24
- package/dist/web-backend-login.js.map +1 -1
- package/dist/web-experimental.d.ts +1 -1
- package/dist/web-experimental.js +1 -1
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-mod-agent.js +2 -2
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-office-acts.d.ts +173 -0
- package/dist/web-office-acts.js +285 -0
- package/dist/web-office-acts.js.map +1 -0
- package/dist/web-office-skins.d.ts +109 -0
- package/dist/web-office-skins.js +299 -0
- package/dist/web-office-skins.js.map +1 -0
- package/dist/web-office-store.d.ts +8 -2
- package/dist/web-office-store.js +77 -14
- package/dist/web-office-store.js.map +1 -1
- package/dist/web-settings-patch.d.ts +8 -1
- package/dist/web-settings-patch.js +65 -5
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -2
- package/dist/web-shell-routes.js +17 -7
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/working-recovery.d.ts +104 -0
- package/dist/working-recovery.js +298 -0
- package/dist/working-recovery.js.map +1 -0
- package/dist/working-state-journal.d.ts +5 -1
- package/dist/working-state-journal.js +31 -3
- package/dist/working-state-journal.js.map +1 -1
- package/package.json +3 -2
- package/templates/windows-shortcuts.ps1 +155 -140
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
// The Office view's skin-pack format, v1 (fleet-office-skin.js draws a checked pack;
|
|
1
|
+
// The Office view's skin-pack format, v1 (fleet-office-skin.js draws a checked pack; an admin uploads one — Fleet → Org
|
|
2
|
+
// chart → Office → Edit offices → Upload a skin pack — and the host keeps it, src/web-office-skins.ts, checking it with
|
|
3
|
+
// this same module: the page and the host agree on every rule).
|
|
2
4
|
// Its shape follows Star-Office-UI's scene (MIT; github.com/ringhyacinth/Star-Office-UI): one background on a 16:9
|
|
3
5
|
// logical canvas, named areas with ordered seat slots, furniture with a depth, and a state → area map. No code or art of
|
|
4
6
|
// it is used here — the idea of the format only.
|
|
5
7
|
//
|
|
6
8
|
// A pack is a JSON manifest plus images. Every coordinate is in logical units on a 1280×720 canvas (LOGICAL), which
|
|
7
|
-
// the page
|
|
9
|
+
// the page shows whole, then pans and zooms (fleet-office-view.js). Images are pixel art, drawn unsmoothed at `scale` logical units per image pixel.
|
|
8
10
|
//
|
|
9
11
|
// {
|
|
10
12
|
// "format": 1, required
|
|
11
13
|
// "id": "cozy", letters, digits, - and _ (≤ 40); not a built-in skin's id
|
|
12
14
|
// "scale": 2, 1–8 (default 2)
|
|
13
15
|
// "background": "office.png", drawn over the whole 1280×720 canvas (16:9)
|
|
16
|
+
// "foreground": "front.png", optional, with stateZones: drawn over the whole canvas (16:9, as
|
|
17
|
+
// the background) above the people, the furniture and the items in
|
|
18
|
+
// the air, below the name tags, bubbles and "+n" counts (and Edit
|
|
19
|
+
// layout's overlay): door frames, pillars, table fronts that hide
|
|
20
|
+
// whoever walks behind them — transparent everywhere else
|
|
21
|
+
// "matte": "#1b1d24", optional (#2540): the colour around the scene where the Office
|
|
22
|
+
// view's panel is not 16:9 (or is zoomed out), "#rrggbb"; without it
|
|
23
|
+
// the page samples the background's edge pixels
|
|
14
24
|
// "areas": { named zones: a rect [x, y, w, h] and its seats, in the order
|
|
15
25
|
// "desks": { "rect": [40, 120, 620, 440], "seats": [[120, 220], [260, 220], …] }, they fill up
|
|
16
26
|
// "lounge": { "rect": [700, 120, 540, 260], "seats": [[760, 260], …] } },
|
|
@@ -20,14 +30,43 @@
|
|
|
20
30
|
// "door": [640, 700], optional: where someone new walks in from (default: bottom middle)
|
|
21
31
|
// "waypoints": [{ "id": "hall", "x": 640, "y": 600 }, …], "edges": [["hall", "east"], …],
|
|
22
32
|
// optional: the walkable graph (else people walk straight)
|
|
33
|
+
// "straight": true, optional (#2634): every walk goes across, then up or down — each
|
|
34
|
+
// diagonal leg an L, so the walkSide / walkDown / walkUp strips
|
|
35
|
+
// always fit (graph edges laid out across and up/down put the
|
|
36
|
+
// corners on the aisles); default false
|
|
23
37
|
// "furniture": [ { "id": "rack", "sprite": STRIP, "x": 1020, "y": 140, "depth": 2,
|
|
24
|
-
// "activeWhen": "crashed", "activeSprite": STRIP
|
|
38
|
+
// "activeWhen": "crashed", "activeSprite": STRIP, "activeFor": "atlas", "loop": true, "label": "Rack",
|
|
39
|
+
// "night": { "sprite": STRIP, "x": 300, "y": 420 },
|
|
40
|
+
// "walk": { "walk": STRIP, "walkDown": STRIP, "walkUp": STRIP, "walkSide": STRIP } } ],
|
|
41
|
+
// "label" (#2574): a name tag under it, like a person's
|
|
42
|
+
// (an NPC drawn as furniture). "night" (#2568), optional: at bedtime
|
|
43
|
+
// (when the viewer has it on: 00:00–06:00 their time) drawn as this
|
|
44
|
+
// strip, playing, at (x, y) — its own place for either left out.
|
|
45
|
+
// "walk" (#2596), optional: it strolls near its (x, y) and back
|
|
46
|
+
// like an idle person, drawn walking by these strips as a
|
|
47
|
+
// character set's walking poses ("walk" required); not at bedtime
|
|
48
|
+
// with a "night".
|
|
25
49
|
// drawn by depth: below 1000 under the people, 1000 and up over
|
|
26
|
-
// them; (
|
|
50
|
+
// them; "depth": "y" (the string) sorts it among the people by base
|
|
51
|
+
// y — its anchor's y against each person's feet: someone whose feet
|
|
52
|
+
// are lower down (a greater y; a tie too) is drawn after it, in
|
|
53
|
+
// front. (x, y) is where the strip's anchor goes. `activeWhen` (a
|
|
27
54
|
// state, or a list): while anyone shown is in it, the item plays its
|
|
28
|
-
// strip (or `activeSprite`); otherwise it holds the first frame
|
|
29
|
-
//
|
|
30
|
-
//
|
|
55
|
+
// strip (or `activeSprite`); otherwise it holds the first frame —
|
|
56
|
+
// or, with "loop": true (#2570), plays its strip all along.
|
|
57
|
+
// "activeFor" (an instance's name, or a list): only their state
|
|
58
|
+
// counts, not anyone's (a room several people take turns in)
|
|
59
|
+
// "characters": { "default": { "working": STRIP, "idle": STRIP, "paused": STRIP, "needs": STRIP, "walk": STRIP,
|
|
60
|
+
// "walkDown": STRIP, "walkUp": STRIP, "walkSide": STRIP },
|
|
61
|
+
// "fox": { … } }, "default" with at least "idle" is required; a missing pose is idle.
|
|
62
|
+
// Walking (poseStrip): the current step's main axis picks the strip —
|
|
63
|
+
// |dy| > |dx|: walkDown (dy > 0, toward the viewer) or walkUp; else
|
|
64
|
+
// walkSide, drawn as it is when moving right and mirrored when moving
|
|
65
|
+
// left (so draw it facing right). A missing one is "walk", then idle
|
|
66
|
+
// ("walk" itself is never mirrored)
|
|
67
|
+
// A set may also have "prop": STRIP (#2562) — what its people
|
|
68
|
+
// send: their messages, work and reports fly as it, in place of
|
|
69
|
+
// the pack's "props" (a set without one: the pack's props)
|
|
31
70
|
// "byInstance": { "atlas": "fox" }, optional: an instance name → its character set
|
|
32
71
|
// "props": { "task": STRIP, "send": STRIP, "report": STRIP, "broadcast": STRIP }, optional traffic items
|
|
33
72
|
// "bubbles": { "en": { "working": ["On it."] }, "zh-TW": { … } } optional: short lines per state (≤ 12 a state,
|
|
@@ -44,20 +83,65 @@
|
|
|
44
83
|
// "..". The check is strict: an unknown key, a value out of range or a name that refers to nothing refuses the pack,
|
|
45
84
|
// with where it broke.
|
|
46
85
|
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
86
|
+
// Its displayed size: w·scale × h·scale logical units, unless the strip says `dw` and/or `dh` (logical units, 1–1280 and
|
|
87
|
+
// 1–720): its frame is then drawn dw × dh (still unsmoothed: nearest neighbour), and with only one of them the other keeps
|
|
88
|
+
// the frame's aspect. The anchor (ax, ay) stays in image pixels and moves with it. So art drawn at 120×120 px shows at
|
|
89
|
+
// 96×96 with "dw": 96, "dh": 96 — whatever the pack's scale. What a person hovers or clicks is the box drawn (stripBox).
|
|
90
|
+
//
|
|
91
|
+
// A name the pack declares and refers to (an area, a character set, a furniture item, an instance in byInstance, and the
|
|
92
|
+
// same in an office's overrides) is never one an object already has (Object.prototype's: "constructor", "toString",
|
|
93
|
+
// "__proto__", …): such a name is refused, and every lookup is of the pack's own names only (own()).
|
|
94
|
+
//
|
|
95
|
+
// The host keeps an uploaded pack whole and never changes it: every PNG starts with its signature, a STRIP's image is
|
|
96
|
+
// exactly frames·w × h, the background and the foreground are 16:9, and each file and the pack have a size cap (web-office-skins.ts).
|
|
97
|
+
//
|
|
98
|
+
// ── Layout overrides (the Office view's "Edit layout": a stored office's own positions on a zones pack) ──
|
|
99
|
+
//
|
|
100
|
+
// An office may keep `overrides` beside its skin — the pack stays as it was uploaded; the office moves things on it:
|
|
101
|
+
//
|
|
102
|
+
// { "pack": "cozy", the pack they were made on (on another pack they are ignored)
|
|
103
|
+
// "seats": { "desks": [[120, 220], …] }, an area's seats: as many as the pack's, each inside the area's rect
|
|
104
|
+
// "furniture": { "rack": [1000, 150] }, an item's (x, y)
|
|
105
|
+
// "furnitureNight": { "elder": [300, 420] }, an item's night place (#2665: an item with a "night" only)
|
|
106
|
+
// "door": [640, 700],
|
|
107
|
+
// "waypoints": [{ "id": "hall", "x": 640, "y": 600 }, …], "edges": [["hall", "east"], …] }
|
|
108
|
+
// the walkable graph, replaced whole (edges need waypoints)
|
|
109
|
+
//
|
|
110
|
+
// Every coordinate is on the same 1280×720 logical canvas, in the same ranges as the pack's. checkLayoutOverrides is
|
|
111
|
+
// the shape (the host checks it on every PUT /ui/offices); applyOverrides puts them on a checked pack, refusing a
|
|
112
|
+
// seat outside its area, an area or furniture the pack does not have, or a seat count that differs (the host checks
|
|
113
|
+
// that too, for a pack it keeps). A night place goes on the item's `night` (its x, y), so at bedtime (nightFurniture)
|
|
114
|
+
// it is the office's, else the pack's night x/y, else the item's (moved) day place. The host keeps `furnitureNight`
|
|
115
|
+
// beside the offices on disk, never inside them: an older host's stricter check would drop the office (#2665,
|
|
116
|
+
// web-office-store.ts).
|
|
49
117
|
|
|
50
118
|
export const LOGICAL = { w: 1280, h: 720 };
|
|
51
119
|
/** The people's depth: furniture below it is drawn under them, the rest over them. */
|
|
52
120
|
export const CHAR_DEPTH = 1000;
|
|
53
121
|
export const PACK_STATES = ["needs", "crashed", "stuck", "working", "idle", "paused", "stopped", "missing"];
|
|
54
|
-
const POSES = ["working", "idle", "paused", "needs", "walk"], PROPS = ["task", "send", "report", "broadcast"];
|
|
122
|
+
const POSES = ["working", "idle", "paused", "needs", "walk", "walkDown", "walkUp", "walkSide"], PROPS = ["task", "send", "report", "broadcast"];
|
|
123
|
+
/** A character set's strips: its poses, and what its people send (#2562). */
|
|
124
|
+
const SET_STRIPS = [...POSES, "prop"];
|
|
125
|
+
/** A furniture item's walking strips (#2596): a character set's walking poses. */
|
|
126
|
+
const WALKS = ["walk", "walkDown", "walkUp", "walkSide"];
|
|
55
127
|
const BUILT_IN = new Set(["pixel", "studio"]);
|
|
128
|
+
/**
|
|
129
|
+
* Ids built in after packs could be uploaded under them (#2631: "cozy"): a new upload is refused, but a pack the host
|
|
130
|
+
* kept under one before (`kept`) is still read back, listed and deletable, and wins the id for the offices that name it.
|
|
131
|
+
*/
|
|
132
|
+
const RESERVED = new Set(["cozy"]);
|
|
56
133
|
const ID = /^[A-Za-z0-9_-]{1,40}$/;
|
|
57
134
|
const SRC = /^[A-Za-z0-9._/-]{1,200}$/;
|
|
135
|
+
const HEX = /^#[0-9a-f]{6}$/i;
|
|
58
136
|
const LIMIT = { areas: 32, seats: 64, furniture: 128, waypoints: 512, edges: 2048, sets: 64, byInstance: 500, lines: 12, line: 40 };
|
|
59
137
|
|
|
60
138
|
class Refused extends Error { constructor(at, why) { super(`${at}: ${why}`); } }
|
|
139
|
+
/** `o[k]` when it is o's own property, else undefined: a name from a pack or a page never reaches Object.prototype. */
|
|
140
|
+
export const own = (o, k) => (o !== null && typeof o === "object" && typeof k === "string" && Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined);
|
|
141
|
+
/** Whether `k` is a name every object already has ("constructor", "toString", "__proto__", …): never a pack's name. */
|
|
142
|
+
export const inherited = (k) => typeof k === "string" && k in Object.prototype;
|
|
143
|
+
/** A name a pack declares or refers to: ID's letters, never an inherited one. */
|
|
144
|
+
const nameOk = (k) => typeof k === "string" && ID.test(k) && !inherited(k);
|
|
61
145
|
const isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
62
146
|
function keys(v, allowed, at) { for (const k of Object.keys(v)) if (!allowed.includes(k)) throw new Refused(at, `unknown key "${k}"`); }
|
|
63
147
|
function obj(v, at) { if (!isObj(v)) throw new Refused(at, "an object"); return v; }
|
|
@@ -68,41 +152,73 @@ function point(v, at) {
|
|
|
68
152
|
if (!Array.isArray(v) || v.length !== 2) throw new Refused(at, "[x, y]");
|
|
69
153
|
return { x: num(v[0], 0, LOGICAL.w, `${at}[0]`), y: num(v[1], 0, LOGICAL.h, `${at}[1]`) };
|
|
70
154
|
}
|
|
155
|
+
/** Where an item sorts: by depth, a "y" one among the people (after every number under them, before the rest). */
|
|
156
|
+
const order = (f) => (f.depth === "y" ? CHAR_DEPTH - 0.5 : f.depth);
|
|
157
|
+
/** A furniture item's numeric depth (its "y" is taken before this). */
|
|
158
|
+
function depth(v, at) { if (!Number.isInteger(v) || v < -10000 || v > 10000) throw new Refused(at, 'a whole number from -10000 to 10000, or "y"'); return v; }
|
|
71
159
|
function list(v, max, at) { if (!Array.isArray(v)) throw new Refused(at, "a list"); if (v.length > max) throw new Refused(at, `at most ${max}`); return v; }
|
|
72
160
|
|
|
73
|
-
/**
|
|
74
|
-
export
|
|
161
|
+
/** Whether `v` is a pack's `matte`: a colour as #rrggbb (six hex digits, either case). */
|
|
162
|
+
export const matteOk = (v) => typeof v === "string" && HEX.test(v);
|
|
163
|
+
|
|
164
|
+
/** A frame strip, strictly; `scale` the pack's (its displayed size, dw × dh, is resolved here). */
|
|
165
|
+
export function checkStrip(v, at = "strip", scale = 2) {
|
|
75
166
|
obj(v, at);
|
|
76
|
-
keys(v, ["src", "frames", "w", "h", "fps", "ax", "ay"], at);
|
|
167
|
+
keys(v, ["src", "frames", "w", "h", "fps", "ax", "ay", "dw", "dh"], at);
|
|
77
168
|
const w = int(v.w, 1, 512, `${at}.w`), h = int(v.h, 1, 512, `${at}.h`);
|
|
169
|
+
const dw0 = v.dw === undefined ? null : num(v.dw, 1, LOGICAL.w, `${at}.dw`), dh0 = v.dh === undefined ? null : num(v.dh, 1, LOGICAL.h, `${at}.dh`);
|
|
170
|
+
const dw = dw0 ?? (dh0 !== null ? (dh0 * w) / h : w * scale), dh = dh0 ?? (dw0 !== null ? (dw0 * h) / w : h * scale);
|
|
78
171
|
return { src: src(v.src, `${at}.src`), frames: int(v.frames, 1, 32, `${at}.frames`), w, h, fps: v.fps === undefined ? 4 : num(v.fps, 0, 30, `${at}.fps`),
|
|
79
|
-
ax: v.ax === undefined ? w / 2 : num(v.ax, 0, w, `${at}.ax`), ay: v.ay === undefined ? h : num(v.ay, 0, h, `${at}.ay`) };
|
|
172
|
+
ax: v.ax === undefined ? w / 2 : num(v.ax, 0, w, `${at}.ax`), ay: v.ay === undefined ? h : num(v.ay, 0, h, `${at}.ay`), dw, dh };
|
|
80
173
|
}
|
|
81
|
-
|
|
174
|
+
/** A furniture item's night (#2568): { sprite, x, y } — at bedtime it is drawn as `sprite` (playing) at (x, y), its own place when left out. */
|
|
175
|
+
function nightOf(v, at, scale) {
|
|
176
|
+
if (v === undefined) return null;
|
|
177
|
+
obj(v, at); keys(v, ["sprite", "x", "y"], at);
|
|
178
|
+
return { sprite: checkStrip(v.sprite, `${at}.sprite`, scale), x: v.x === undefined ? null : num(v.x, 0, LOGICAL.w, `${at}.x`), y: v.y === undefined ? null : num(v.y, 0, LOGICAL.h, `${at}.y`) };
|
|
179
|
+
}
|
|
180
|
+
/** A furniture item's walk (#2596): its walking strips, picked by heading as a character set's (poseStrip); "walk" required. */
|
|
181
|
+
function walkOf(v, at, scale) {
|
|
182
|
+
if (v === undefined) return null;
|
|
183
|
+
const w = strips(v, WALKS, at, scale);
|
|
184
|
+
if (!w.walk) throw new Refused(`${at}.walk`, "required");
|
|
185
|
+
return w;
|
|
186
|
+
}
|
|
187
|
+
/** Whose state an item's activeWhen watches (#2570): an instance's name, or a list of up to 8. */
|
|
188
|
+
function names(v, at) {
|
|
189
|
+
const one = (s, here) => { if (typeof s !== "string" || !s || s.length > 200 || inherited(s) || /[\u0000-\u001f\u007f]/.test(s)) throw new Refused(here, "an instance's name"); return s; };
|
|
190
|
+
return Array.isArray(v) ? list(v, 8, at).map((s, i) => one(s, `${at}[${i}]`)) : [one(v, at)];
|
|
191
|
+
}
|
|
192
|
+
function strips(v, names, at, scale) {
|
|
82
193
|
obj(v, at);
|
|
83
194
|
keys(v, names, at);
|
|
84
|
-
return Object.fromEntries(Object.entries(v).map(([k, s]) => [k, checkStrip(s, `${at}.${k}
|
|
195
|
+
return Object.fromEntries(Object.entries(v).map(([k, s]) => [k, checkStrip(s, `${at}.${k}`, scale)]));
|
|
85
196
|
}
|
|
86
197
|
function states(v, at) {
|
|
87
198
|
const one = (s, here) => { if (!PACK_STATES.includes(s)) throw new Refused(here, `one of ${PACK_STATES.join(", ")}`); return s; };
|
|
88
199
|
return Array.isArray(v) ? list(v, 8, at).map((s, i) => one(s, `${at}[${i}]`)) : [one(v, at)];
|
|
89
200
|
}
|
|
90
201
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
202
|
+
/**
|
|
203
|
+
* A pack's manifest, strictly: { ok: true, pack } or { ok: false, error: "<where>: <why>" }. `kept`: a pack the host
|
|
204
|
+
* keeps already (read back, not uploaded), which may hold a RESERVED id.
|
|
205
|
+
*/
|
|
206
|
+
export function checkSkinPack(x, { kept = false } = {}) {
|
|
93
207
|
try {
|
|
94
208
|
obj(x, "pack");
|
|
95
|
-
keys(x, ["format", "id", "scale", "background", "areas", "stateZones", "door", "waypoints", "edges", "furniture", "characters", "byInstance", "props", "bubbles", "room", "desk"], "pack");
|
|
209
|
+
keys(x, ["format", "id", "scale", "background", "foreground", "matte", "areas", "stateZones", "door", "waypoints", "edges", "straight", "furniture", "characters", "byInstance", "props", "bubbles", "room", "desk"], "pack");
|
|
96
210
|
if (x.format !== 1) throw new Refused("format", "1");
|
|
97
211
|
if (typeof x.id !== "string" || !ID.test(x.id)) throw new Refused("id", "1–40 letters, digits, _ or -");
|
|
98
|
-
if (BUILT_IN.has(x.id)) throw new Refused("id", "a built-in skin's id");
|
|
212
|
+
if (BUILT_IN.has(x.id) || (!kept && RESERVED.has(x.id))) throw new Refused("id", "a built-in skin's id");
|
|
99
213
|
const scale = x.scale === undefined ? 2 : int(x.scale, 1, 8, "scale");
|
|
214
|
+
if (x.matte !== undefined && !matteOk(x.matte)) throw new Refused("matte", "a colour as #rrggbb");
|
|
215
|
+
if (x.straight !== undefined && typeof x.straight !== "boolean") throw new Refused("straight", "true or false");
|
|
100
216
|
const areas = {};
|
|
101
217
|
const rawAreas = x.areas === undefined ? {} : obj(x.areas, "areas");
|
|
102
218
|
if (Object.keys(rawAreas).length > LIMIT.areas) throw new Refused("areas", `at most ${LIMIT.areas}`);
|
|
103
219
|
for (const [name, a] of Object.entries(rawAreas)) {
|
|
104
220
|
const at = `areas.${name}`;
|
|
105
|
-
if (!
|
|
221
|
+
if (!nameOk(name)) throw new Refused(at, "a name of letters, digits, _ or - (not an inherited one)");
|
|
106
222
|
obj(a, at); keys(a, ["rect", "seats"], at);
|
|
107
223
|
if (!Array.isArray(a.rect) || a.rect.length !== 4) throw new Refused(`${at}.rect`, "[x, y, w, h]");
|
|
108
224
|
const [rx, ry] = [num(a.rect[0], 0, LOGICAL.w, `${at}.rect[0]`), num(a.rect[1], 0, LOGICAL.h, `${at}.rect[1]`)];
|
|
@@ -122,11 +238,12 @@ export function checkSkinPack(x) {
|
|
|
122
238
|
stateZones = {};
|
|
123
239
|
for (const [s, area] of Object.entries(x.stateZones)) {
|
|
124
240
|
if (s !== "default" && !PACK_STATES.includes(s)) throw new Refused(`stateZones.${s}`, `a state (${PACK_STATES.join(", ")}) or "default"`);
|
|
125
|
-
if (typeof area !== "string" || !areas
|
|
241
|
+
if (typeof area !== "string" || !own(areas, area)) throw new Refused(`stateZones.${s}`, "an area the pack defines");
|
|
126
242
|
stateZones[s] = area;
|
|
127
243
|
}
|
|
128
244
|
if (x.background === undefined) throw new Refused("background", "required with stateZones");
|
|
129
245
|
}
|
|
246
|
+
if (x.foreground !== undefined && !stateZones) throw new Refused("foreground", "only with stateZones");
|
|
130
247
|
const waypoints = [], ids = new Set();
|
|
131
248
|
for (const [i, w] of list(x.waypoints ?? [], LIMIT.waypoints, "waypoints").entries()) {
|
|
132
249
|
const at = `waypoints[${i}]`;
|
|
@@ -142,27 +259,36 @@ export function checkSkinPack(x) {
|
|
|
142
259
|
const fids = new Set();
|
|
143
260
|
const furniture = list(x.furniture ?? [], LIMIT.furniture, "furniture").map((f, i) => {
|
|
144
261
|
const at = `furniture[${i}]`;
|
|
145
|
-
obj(f, at); keys(f, ["id", "sprite", "x", "y", "depth", "activeWhen", "activeSprite"], at);
|
|
146
|
-
if (
|
|
262
|
+
obj(f, at); keys(f, ["id", "sprite", "x", "y", "depth", "activeWhen", "activeSprite", "activeFor", "loop", "label", "night", "walk"], at);
|
|
263
|
+
if (!nameOk(f.id) || fids.has(f.id)) throw new Refused(`${at}.id`, "a new name of letters, digits, _ or - (not an inherited one)");
|
|
147
264
|
fids.add(f.id);
|
|
148
265
|
if (f.activeSprite !== undefined && f.activeWhen === undefined) throw new Refused(`${at}.activeSprite`, "only with activeWhen");
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
266
|
+
if (f.activeFor !== undefined && f.activeWhen === undefined) throw new Refused(`${at}.activeFor`, "only with activeWhen");
|
|
267
|
+
if (f.loop !== undefined && typeof f.loop !== "boolean") throw new Refused(`${at}.loop`, "true or false");
|
|
268
|
+
if (f.label !== undefined && (typeof f.label !== "string" || !f.label.trim() || [...f.label].length > LIMIT.line || /[\u0000-\u001f\u007f]/.test(f.label))) throw new Refused(`${at}.label`, `1–${LIMIT.line} characters`);
|
|
269
|
+
return { id: f.id, sprite: checkStrip(f.sprite, `${at}.sprite`, scale), x: num(f.x, 0, LOGICAL.w, `${at}.x`), y: num(f.y, 0, LOGICAL.h, `${at}.y`),
|
|
270
|
+
depth: f.depth === "y" ? "y" : depth(f.depth ?? 0, `${at}.depth`), activeWhen: f.activeWhen === undefined ? null : states(f.activeWhen, `${at}.activeWhen`),
|
|
271
|
+
activeSprite: f.activeSprite === undefined ? null : checkStrip(f.activeSprite, `${at}.activeSprite`, scale),
|
|
272
|
+
activeFor: f.activeFor === undefined ? null : names(f.activeFor, `${at}.activeFor`), loop: f.loop === true, label: f.label === undefined ? null : f.label.trim(), night: nightOf(f.night, `${at}.night`, scale),
|
|
273
|
+
walk: walkOf(f.walk, `${at}.walk`, scale) };
|
|
274
|
+
}).sort((a, b) => order(a) - order(b));
|
|
153
275
|
const sets = obj(x.characters, "characters");
|
|
154
276
|
if (Object.keys(sets).length > LIMIT.sets) throw new Refused("characters", `at most ${LIMIT.sets} sets`);
|
|
155
277
|
const characters = {};
|
|
156
278
|
for (const [k, v] of Object.entries(sets)) {
|
|
157
|
-
if (!
|
|
158
|
-
characters[k] = strips(v,
|
|
279
|
+
if (!nameOk(k)) throw new Refused(`characters.${k}`, "a name of letters, digits, _ or - (not an inherited one)");
|
|
280
|
+
characters[k] = strips(v, SET_STRIPS, `characters.${k}`, scale);
|
|
159
281
|
if (!characters[k].idle) throw new Refused(`characters.${k}.idle`, "required");
|
|
160
282
|
}
|
|
161
|
-
if (!characters
|
|
283
|
+
if (!own(characters, "default")) throw new Refused("characters.default", "required");
|
|
162
284
|
const byInstance = {};
|
|
163
285
|
const rawBy = x.byInstance === undefined ? {} : obj(x.byInstance, "byInstance");
|
|
164
286
|
if (Object.keys(rawBy).length > LIMIT.byInstance) throw new Refused("byInstance", `at most ${LIMIT.byInstance}`);
|
|
165
|
-
for (const [k, v] of Object.entries(rawBy)) {
|
|
287
|
+
for (const [k, v] of Object.entries(rawBy)) {
|
|
288
|
+
if (inherited(k)) throw new Refused(`byInstance.${k}`, "an instance's name (not an inherited one)");
|
|
289
|
+
if (typeof v !== "string" || !own(characters, v)) throw new Refused(`byInstance.${k}`, "a character set the pack defines");
|
|
290
|
+
byInstance[k] = v;
|
|
291
|
+
}
|
|
166
292
|
const bubbles = {};
|
|
167
293
|
for (const [lang, table] of Object.entries(x.bubbles === undefined ? {} : obj(x.bubbles, "bubbles"))) {
|
|
168
294
|
if (lang !== "en" && lang !== "zh-TW") throw new Refused(`bubbles.${lang}`, '"en" or "zh-TW"');
|
|
@@ -181,9 +307,9 @@ export function checkSkinPack(x) {
|
|
|
181
307
|
keys(room, ["floor", "wall"], "room"); keys(desk, ["back", "front", "lit"], "desk");
|
|
182
308
|
const opt = (v, at) => (v === undefined ? undefined : src(v, at));
|
|
183
309
|
return { ok: true, pack: {
|
|
184
|
-
format: 1, id: x.id, scale, background: opt(x.background, "background"), areas, stateZones,
|
|
185
|
-
door: x.door === undefined ? { x: LOGICAL.w / 2, y: LOGICAL.h - 10 } : point(x.door, "door"), waypoints, edges, furniture, characters, byInstance,
|
|
186
|
-
props: x.props === undefined ? {} : strips(x.props, PROPS, "props"), bubbles,
|
|
310
|
+
format: 1, id: x.id, scale, background: opt(x.background, "background"), foreground: opt(x.foreground, "foreground"), matte: x.matte === undefined ? undefined : x.matte.toLowerCase(), areas, stateZones,
|
|
311
|
+
door: x.door === undefined ? { x: LOGICAL.w / 2, y: LOGICAL.h - 10 } : point(x.door, "door"), waypoints, edges, straight: x.straight === true, furniture, characters, byInstance,
|
|
312
|
+
props: x.props === undefined ? {} : strips(x.props, PROPS, "props", scale), bubbles,
|
|
187
313
|
room: { floor: opt(room.floor, "room.floor"), wall: opt(room.wall, "room.wall") }, desk: { back: opt(desk.back, "desk.back"), front: opt(desk.front, "desk.front"), lit: opt(desk.lit, "desk.lit") },
|
|
188
314
|
} };
|
|
189
315
|
} catch (err) {
|
|
@@ -194,6 +320,195 @@ export function checkSkinPack(x) {
|
|
|
194
320
|
/** The checked pack, or null. */
|
|
195
321
|
export const skinPackOk = (x) => { const r = checkSkinPack(x); return r.ok ? r.pack : null; };
|
|
196
322
|
/** Every image a pack names (what a loader fetches before spriteSkin draws). */
|
|
197
|
-
export const packImages = (pack) => [...new Set([pack.background, pack.room.floor, pack.room.wall, pack.desk.back, pack.desk.front, pack.desk.lit,
|
|
198
|
-
...pack.furniture.flatMap((f) => [f.sprite.src, f.activeSprite && f.activeSprite.src]),
|
|
323
|
+
export const packImages = (pack) => [...new Set([pack.background, pack.foreground, pack.room.floor, pack.room.wall, pack.desk.back, pack.desk.front, pack.desk.lit,
|
|
324
|
+
...pack.furniture.flatMap((f) => [f.sprite.src, f.activeSprite && f.activeSprite.src, f.night && f.night.sprite.src, ...Object.values(f.walk || {}).map((st) => st.src)]),
|
|
199
325
|
...Object.values(pack.characters).flatMap((s) => Object.values(s).map((st) => st.src)), ...Object.values(pack.props).map((st) => st.src)].filter(Boolean))];
|
|
326
|
+
|
|
327
|
+
/** Every frame strip a checked pack names, with where (the host checks each image is frames·w × h). */
|
|
328
|
+
export function packStrips(pack) {
|
|
329
|
+
const out = [];
|
|
330
|
+
for (const f of pack.furniture) {
|
|
331
|
+
out.push({ at: `furniture.${f.id}.sprite`, strip: f.sprite });
|
|
332
|
+
if (f.activeSprite) out.push({ at: `furniture.${f.id}.activeSprite`, strip: f.activeSprite });
|
|
333
|
+
if (f.night) out.push({ at: `furniture.${f.id}.night.sprite`, strip: f.night.sprite });
|
|
334
|
+
for (const [k, st] of Object.entries(f.walk || {})) out.push({ at: `furniture.${f.id}.walk.${k}`, strip: st });
|
|
335
|
+
}
|
|
336
|
+
for (const [k, set] of Object.entries(pack.characters)) for (const [pose, st] of Object.entries(set)) out.push({ at: `characters.${k}.${pose}`, strip: st });
|
|
337
|
+
for (const [k, st] of Object.entries(pack.props)) out.push({ at: `props.${k}`, strip: st });
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ── Layout overrides ──
|
|
342
|
+
|
|
343
|
+
const OV_LIMIT = { areas: LIMIT.areas, seats: LIMIT.seats, furniture: LIMIT.furniture, waypoints: LIMIT.waypoints, edges: LIMIT.edges };
|
|
344
|
+
/** An office's layout overrides, strictly (their shape and ranges): { ok: true, overrides } or { ok: false, error }. */
|
|
345
|
+
export function checkLayoutOverrides(x) {
|
|
346
|
+
try {
|
|
347
|
+
obj(x, "overrides");
|
|
348
|
+
keys(x, ["pack", "seats", "furniture", "furnitureNight", "door", "waypoints", "edges"], "overrides");
|
|
349
|
+
if (typeof x.pack !== "string" || !ID.test(x.pack)) throw new Refused("overrides.pack", "1–40 letters, digits, _ or -");
|
|
350
|
+
const out = { pack: x.pack };
|
|
351
|
+
if (x.seats !== undefined) {
|
|
352
|
+
obj(x.seats, "overrides.seats");
|
|
353
|
+
if (Object.keys(x.seats).length > OV_LIMIT.areas) throw new Refused("overrides.seats", `at most ${OV_LIMIT.areas} areas`);
|
|
354
|
+
out.seats = {};
|
|
355
|
+
for (const [area, list0] of Object.entries(x.seats)) {
|
|
356
|
+
const at = `overrides.seats.${area}`;
|
|
357
|
+
if (!nameOk(area)) throw new Refused(at, "an area's name");
|
|
358
|
+
const pts = list(list0, OV_LIMIT.seats, at).map((p, i) => { const q = point(p, `${at}[${i}]`); return [q.x, q.y]; });
|
|
359
|
+
if (!pts.length) throw new Refused(at, "at least one seat");
|
|
360
|
+
out.seats[area] = pts;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (x.furniture !== undefined) {
|
|
364
|
+
obj(x.furniture, "overrides.furniture");
|
|
365
|
+
if (Object.keys(x.furniture).length > OV_LIMIT.furniture) throw new Refused("overrides.furniture", `at most ${OV_LIMIT.furniture}`);
|
|
366
|
+
out.furniture = {};
|
|
367
|
+
for (const [id, p] of Object.entries(x.furniture)) {
|
|
368
|
+
if (!nameOk(id)) throw new Refused(`overrides.furniture.${id}`, "an item's id");
|
|
369
|
+
const q = point(p, `overrides.furniture.${id}`);
|
|
370
|
+
out.furniture[id] = [q.x, q.y];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (x.furnitureNight !== undefined) {
|
|
374
|
+
obj(x.furnitureNight, "overrides.furnitureNight");
|
|
375
|
+
if (Object.keys(x.furnitureNight).length > OV_LIMIT.furniture) throw new Refused("overrides.furnitureNight", `at most ${OV_LIMIT.furniture}`);
|
|
376
|
+
out.furnitureNight = {};
|
|
377
|
+
for (const [id, p] of Object.entries(x.furnitureNight)) {
|
|
378
|
+
if (!nameOk(id)) throw new Refused(`overrides.furnitureNight.${id}`, "an item's id");
|
|
379
|
+
const q = point(p, `overrides.furnitureNight.${id}`);
|
|
380
|
+
out.furnitureNight[id] = [q.x, q.y];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (x.door !== undefined) { const q = point(x.door, "overrides.door"); out.door = [q.x, q.y]; }
|
|
384
|
+
if (x.edges !== undefined && x.waypoints === undefined) throw new Refused("overrides.edges", "only with waypoints");
|
|
385
|
+
if (x.waypoints !== undefined) {
|
|
386
|
+
const ids = new Set();
|
|
387
|
+
out.waypoints = list(x.waypoints, OV_LIMIT.waypoints, "overrides.waypoints").map((w, i) => {
|
|
388
|
+
const at = `overrides.waypoints[${i}]`;
|
|
389
|
+
obj(w, at); keys(w, ["id", "x", "y"], at);
|
|
390
|
+
if (typeof w.id !== "string" || !ID.test(w.id) || ids.has(w.id)) throw new Refused(`${at}.id`, "a new name of letters, digits, _ or -");
|
|
391
|
+
ids.add(w.id);
|
|
392
|
+
return { id: w.id, x: num(w.x, 0, LOGICAL.w, `${at}.x`), y: num(w.y, 0, LOGICAL.h, `${at}.y`) };
|
|
393
|
+
});
|
|
394
|
+
const seen = new Set();
|
|
395
|
+
out.edges = list(x.edges ?? [], OV_LIMIT.edges, "overrides.edges").map((e, i) => {
|
|
396
|
+
if (!Array.isArray(e) || e.length !== 2 || !ids.has(e[0]) || !ids.has(e[1]) || e[0] === e[1]) throw new Refused(`overrides.edges[${i}]`, "two different waypoint ids");
|
|
397
|
+
const k = e[0] < e[1] ? `${e[0]}\u0000${e[1]}` : `${e[1]}\u0000${e[0]}`;
|
|
398
|
+
if (seen.has(k)) throw new Refused(`overrides.edges[${i}]`, "joined twice");
|
|
399
|
+
seen.add(k);
|
|
400
|
+
return [e[0], e[1]];
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return { ok: true, overrides: out };
|
|
404
|
+
} catch (err) {
|
|
405
|
+
if (err instanceof Refused) return { ok: false, error: err.message };
|
|
406
|
+
throw err;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* A checked pack with an office's checked overrides on it: { ok: true, pack } (a new object; the pack is untouched) or
|
|
412
|
+
* { ok: false, error }. Overrides made on another pack change nothing (ok, the pack as it is).
|
|
413
|
+
*/
|
|
414
|
+
export function applyOverrides(pack, ov) {
|
|
415
|
+
try {
|
|
416
|
+
if (!ov || ov.pack !== pack.id) return { ok: true, pack };
|
|
417
|
+
const areas = { ...pack.areas };
|
|
418
|
+
for (const [name, pts] of Object.entries(ov.seats || {})) {
|
|
419
|
+
const a = own(pack.areas, name), at = `overrides.seats.${name}`;
|
|
420
|
+
if (!a) throw new Refused(at, "an area the pack defines");
|
|
421
|
+
if (pts.length !== a.seats.length) throw new Refused(at, `${a.seats.length} seats, as the pack has`);
|
|
422
|
+
const r = a.rect;
|
|
423
|
+
areas[name] = { ...a, seats: pts.map(([x, y], i) => {
|
|
424
|
+
if (x < r.x || x > r.x + r.w || y < r.y || y > r.y + r.h) throw new Refused(`${at}[${i}]`, "outside its area's rect");
|
|
425
|
+
return { x, y };
|
|
426
|
+
}) };
|
|
427
|
+
}
|
|
428
|
+
const fids = new Set(pack.furniture.map((f) => f.id));
|
|
429
|
+
for (const id of Object.keys(ov.furniture || {})) if (!fids.has(id)) throw new Refused(`overrides.furniture.${id}`, "an item the pack has");
|
|
430
|
+
const nids = new Set(pack.furniture.filter((f) => f.night).map((f) => f.id));
|
|
431
|
+
for (const id of Object.keys(ov.furnitureNight || {})) if (!nids.has(id)) throw new Refused(`overrides.furnitureNight.${id}`, "an item with a night the pack has");
|
|
432
|
+
const furniture = pack.furniture.map((f) => {
|
|
433
|
+
const q = own(ov.furniture, f.id), n = own(ov.furnitureNight, f.id);
|
|
434
|
+
if (!q && !n) return f;
|
|
435
|
+
return { ...f, ...(q ? { x: q[0], y: q[1] } : {}), ...(n ? { night: { ...f.night, x: n[0], y: n[1] } } : {}) };
|
|
436
|
+
});
|
|
437
|
+
const door = ov.door ? { x: ov.door[0], y: ov.door[1] } : pack.door;
|
|
438
|
+
const graph = ov.waypoints ? { waypoints: ov.waypoints.map((w) => ({ ...w })), edges: (ov.edges || []).map((e) => [e[0], e[1]]) } : { waypoints: pack.waypoints, edges: pack.edges };
|
|
439
|
+
return { ok: true, pack: { ...pack, areas, furniture, door, ...graph } };
|
|
440
|
+
} catch (err) {
|
|
441
|
+
if (err instanceof Refused) return { ok: false, error: err.message };
|
|
442
|
+
throw err;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* #2665: overrides with only the night places `pack` can take (an item it has, with a `night`): a pack changed since
|
|
448
|
+
* (an item dropped, or its night) drops that entry, never the rest of the layout. applyOverrides itself stays strict
|
|
449
|
+
* (a write naming an item the pack lacks is refused, as for `furniture`).
|
|
450
|
+
*/
|
|
451
|
+
export function fitNight(pack, ov) {
|
|
452
|
+
if (!ov || !ov.furnitureNight || ov.pack !== pack.id) return ov;
|
|
453
|
+
const ok = new Set(pack.furniture.filter((f) => f.night).map((f) => f.id));
|
|
454
|
+
const kept = Object.entries(ov.furnitureNight).filter(([id]) => ok.has(id));
|
|
455
|
+
if (kept.length === Object.keys(ov.furnitureNight).length) return ov;
|
|
456
|
+
const { furnitureNight: _gone, ...rest } = ov;
|
|
457
|
+
return kept.length ? { ...rest, furnitureNight: Object.fromEntries(kept) } : rest;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* What an edited layout keeps as overrides: only the parts that differ from the pack's own (`edited` is a pack with
|
|
462
|
+
* the same areas and furniture, positions moved); null when nothing does (the office goes back to the pack's).
|
|
463
|
+
*/
|
|
464
|
+
export function overridesBetween(pack, edited) {
|
|
465
|
+
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
466
|
+
const ov = { pack: pack.id };
|
|
467
|
+
for (const [name, a] of Object.entries(pack.areas)) {
|
|
468
|
+
const e = own(edited.areas, name);
|
|
469
|
+
if (e && !same(a.seats, e.seats)) (ov.seats ||= {})[name] = e.seats.map((s) => [s.x, s.y]);
|
|
470
|
+
}
|
|
471
|
+
const at = new Map(pack.furniture.map((f) => [f.id, f]));
|
|
472
|
+
for (const f of edited.furniture) { const o = at.get(f.id); if (o && (o.x !== f.x || o.y !== f.y)) (ov.furniture ||= {})[f.id] = [f.x, f.y]; }
|
|
473
|
+
// #2665: a night place that differs from the pack's (its own x/y, or in place: null); in place again is no entry.
|
|
474
|
+
for (const f of edited.furniture) {
|
|
475
|
+
const o = at.get(f.id);
|
|
476
|
+
if (!o || !o.night || !f.night || f.night.x === null || f.night.y === null) continue;
|
|
477
|
+
if (o.night.x !== f.night.x || o.night.y !== f.night.y) (ov.furnitureNight ||= {})[f.id] = [f.night.x, f.night.y];
|
|
478
|
+
}
|
|
479
|
+
if (pack.door.x !== edited.door.x || pack.door.y !== edited.door.y) ov.door = [edited.door.x, edited.door.y];
|
|
480
|
+
if (!same(pack.waypoints, edited.waypoints) || !same(pack.edges, edited.edges)) {
|
|
481
|
+
ov.waypoints = edited.waypoints.map((w) => ({ id: w.id, x: w.x, y: w.y }));
|
|
482
|
+
ov.edges = edited.edges.map((e) => [e[0], e[1]]);
|
|
483
|
+
}
|
|
484
|
+
return Object.keys(ov).length > 1 ? ov : null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ── Drawing a strip: which one a pose shows, and the box it covers ──
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* The strip a character set shows for `pose` (heading: the current step { dx, dy }, while walking): { strip, mirror }, or
|
|
491
|
+
* null (no set). Walking: |dy| > |dx| → walkDown (dy > 0) or walkUp, else walkSide (mirror: moving left); a missing one
|
|
492
|
+
* → "walk" (never mirrored) → idle. Any other pose: its own strip, else idle.
|
|
493
|
+
*/
|
|
494
|
+
export function poseStrip(set, pose, heading = null) {
|
|
495
|
+
if (!set || typeof set !== "object") return null;
|
|
496
|
+
if (pose === "walk" && heading) {
|
|
497
|
+
const dx = Number(heading.dx) || 0, dy = Number(heading.dy) || 0;
|
|
498
|
+
if (dx || dy) {
|
|
499
|
+
const dir = Math.abs(dy) > Math.abs(dx) ? (dy > 0 ? "walkDown" : "walkUp") : "walkSide";
|
|
500
|
+
const st = own(set, dir);
|
|
501
|
+
if (st) return { strip: st, mirror: dir === "walkSide" && dx < 0 };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const st = own(set, pose) || own(set, "idle");
|
|
505
|
+
return st ? { strip: st, mirror: false } : null;
|
|
506
|
+
}
|
|
507
|
+
/** The strip a furniture item shows while it walks (#2596: an NPC with `walk`, `walking` and `heading` set where it is
|
|
508
|
+
* drawn): its walk strip by heading, as a character's (poseStrip); null when it stands (its own sprite, as before). */
|
|
509
|
+
export const walkStrip = (f) => (f && f.walking && f.walk ? poseStrip(f.walk, "walk", f.heading) : null);
|
|
510
|
+
/** The box (logical units) a strip covers with its anchor at (x, y): its displayed size; mirrored about the anchor. */
|
|
511
|
+
export function stripBox(st, x, y, mirror = false) {
|
|
512
|
+
const kx = st.dw / st.w, ky = st.dh / st.h;
|
|
513
|
+
return { x: Math.round(x - (mirror ? st.w - st.ax : st.ax) * kx), y: Math.round(y - st.ay * ky), w: st.dw, h: st.dh };
|
|
514
|
+
}
|