@agentdeck/bridge 0.2.1 → 0.2.3
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/assets/fonts/IBMPlexSans-Bold.ttf +0 -0
- package/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Bold.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/assets/fonts/LICENSES.md +22 -0
- package/dist/adapters/claude-code.d.ts +6 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +8 -0
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/openclaw.d.ts +24 -0
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +291 -35
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/adapters/opencode-adapter.d.ts +59 -0
- package/dist/adapters/opencode-adapter.d.ts.map +1 -1
- package/dist/adapters/opencode-adapter.js +297 -35
- package/dist/adapters/opencode-adapter.js.map +1 -1
- package/dist/adb-reverse.d.ts +1 -1
- package/dist/adb-reverse.js +1 -1
- package/dist/antigravity-local.d.ts +1 -0
- package/dist/antigravity-local.d.ts.map +1 -1
- package/dist/antigravity-local.js +33 -0
- package/dist/antigravity-local.js.map +1 -1
- package/dist/apme/adapters/claude-hook.d.ts.map +1 -1
- package/dist/apme/adapters/claude-hook.js +5 -0
- package/dist/apme/adapters/claude-hook.js.map +1 -1
- package/dist/apme/adapters/codex-hook.d.ts +17 -0
- package/dist/apme/adapters/codex-hook.d.ts.map +1 -0
- package/dist/apme/adapters/codex-hook.js +95 -0
- package/dist/apme/adapters/codex-hook.js.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts +86 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.js +496 -0
- package/dist/apme/adapters/codex-turn-manager.js.map +1 -0
- package/dist/apme/adapters/openclaw-hook.d.ts +99 -0
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -0
- package/dist/apme/adapters/openclaw-hook.js +234 -0
- package/dist/apme/adapters/openclaw-hook.js.map +1 -0
- package/dist/apme/adapters/opencode-hook.d.ts +56 -0
- package/dist/apme/adapters/opencode-hook.d.ts.map +1 -0
- package/dist/apme/adapters/opencode-hook.js +182 -0
- package/dist/apme/adapters/opencode-hook.js.map +1 -0
- package/dist/apme/adapters/timeline.d.ts.map +1 -1
- package/dist/apme/adapters/timeline.js +6 -1
- package/dist/apme/adapters/timeline.js.map +1 -1
- package/dist/apme/antigravity-transcript.d.ts +18 -0
- package/dist/apme/antigravity-transcript.d.ts.map +1 -0
- package/dist/apme/antigravity-transcript.js +94 -0
- package/dist/apme/antigravity-transcript.js.map +1 -0
- package/dist/apme/classify-turn.d.ts +13 -0
- package/dist/apme/classify-turn.d.ts.map +1 -0
- package/dist/apme/classify-turn.js +49 -0
- package/dist/apme/classify-turn.js.map +1 -0
- package/dist/apme/claude-transcript-reader.d.ts +10 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
- package/dist/apme/claude-transcript-reader.js +35 -0
- package/dist/apme/claude-transcript-reader.js.map +1 -1
- package/dist/apme/collector.d.ts +129 -6
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +470 -23
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +87 -11
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +43 -6
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/index.d.ts +26 -5
- package/dist/apme/index.d.ts.map +1 -1
- package/dist/apme/index.js +135 -9
- package/dist/apme/index.js.map +1 -1
- package/dist/apme/pareto.d.ts +39 -0
- package/dist/apme/pareto.d.ts.map +1 -0
- package/dist/apme/pareto.js +59 -0
- package/dist/apme/pareto.js.map +1 -0
- package/dist/apme/recommend.d.ts +3 -0
- package/dist/apme/recommend.d.ts.map +1 -1
- package/dist/apme/recommend.js +43 -1
- package/dist/apme/recommend.js.map +1 -1
- package/dist/apme/runner.d.ts +92 -4
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +586 -70
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/sample-to-timeline.d.ts +26 -0
- package/dist/apme/sample-to-timeline.d.ts.map +1 -0
- package/dist/apme/sample-to-timeline.js +95 -0
- package/dist/apme/sample-to-timeline.js.map +1 -0
- package/dist/apme/scorers/index.d.ts +47 -0
- package/dist/apme/scorers/index.d.ts.map +1 -0
- package/dist/apme/scorers/index.js +96 -0
- package/dist/apme/scorers/index.js.map +1 -0
- package/dist/apme/settings.d.ts +14 -10
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +34 -8
- package/dist/apme/settings.js.map +1 -1
- package/dist/apme/store.d.ts +33 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +334 -25
- package/dist/apme/store.js.map +1 -1
- package/dist/awaiting-overlay.d.ts +99 -0
- package/dist/awaiting-overlay.d.ts.map +1 -0
- package/dist/awaiting-overlay.js +148 -0
- package/dist/awaiting-overlay.js.map +1 -0
- package/dist/ble-sync-spawn.d.ts +63 -0
- package/dist/ble-sync-spawn.d.ts.map +1 -0
- package/dist/ble-sync-spawn.js +196 -0
- package/dist/ble-sync-spawn.js.map +1 -0
- package/dist/bridge-core.d.ts +24 -1
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +157 -24
- package/dist/bridge-core.js.map +1 -1
- package/dist/broadcast.d.ts +3 -0
- package/dist/broadcast.d.ts.map +1 -0
- package/dist/broadcast.js +122 -0
- package/dist/broadcast.js.map +1 -0
- package/dist/check-deps.d.ts.map +1 -1
- package/dist/check-deps.js +25 -9
- package/dist/check-deps.js.map +1 -1
- package/dist/cli.d.ts +15 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1025 -94
- package/dist/cli.js.map +1 -1
- package/dist/codex-auth.d.ts +21 -0
- package/dist/codex-auth.d.ts.map +1 -1
- package/dist/codex-auth.js +52 -1
- package/dist/codex-auth.js.map +1 -1
- package/dist/codex-output-parser.d.ts +8 -0
- package/dist/codex-output-parser.d.ts.map +1 -1
- package/dist/codex-output-parser.js +110 -25
- package/dist/codex-output-parser.js.map +1 -1
- package/dist/codex-rate-limits.d.ts +16 -0
- package/dist/codex-rate-limits.d.ts.map +1 -0
- package/dist/codex-rate-limits.js +197 -0
- package/dist/codex-rate-limits.js.map +1 -0
- package/dist/codex-rollout-response.d.ts +31 -0
- package/dist/codex-rollout-response.d.ts.map +1 -0
- package/dist/codex-rollout-response.js +135 -0
- package/dist/codex-rollout-response.js.map +1 -0
- package/dist/daemon-server.d.ts +85 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +1627 -159
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon.js +15 -6
- package/dist/daemon.js.map +1 -1
- package/dist/display-dim.d.ts +9 -0
- package/dist/display-dim.d.ts.map +1 -0
- package/dist/display-dim.js +53 -0
- package/dist/display-dim.js.map +1 -0
- package/dist/display-monitor.d.ts +15 -2
- package/dist/display-monitor.d.ts.map +1 -1
- package/dist/display-monitor.js +104 -26
- package/dist/display-monitor.js.map +1 -1
- package/dist/esp32-serial.d.ts +160 -1
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +950 -153
- package/dist/esp32-serial.js.map +1 -1
- package/dist/fallback-task-timeline.d.ts +42 -0
- package/dist/fallback-task-timeline.d.ts.map +1 -0
- package/dist/fallback-task-timeline.js +112 -0
- package/dist/fallback-task-timeline.js.map +1 -0
- package/dist/foundation-models-helper.d.ts +11 -0
- package/dist/foundation-models-helper.d.ts.map +1 -0
- package/dist/foundation-models-helper.js +241 -0
- package/dist/foundation-models-helper.js.map +1 -0
- package/dist/hook-migration.js +1 -1
- package/dist/hook-migration.js.map +1 -1
- package/dist/hook-server.d.ts +2 -3
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +22 -10
- package/dist/hook-server.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +33 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +164 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts +18 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.js +85 -0
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts +20 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.js +57 -0
- package/dist/idotmatrix/idotmatrix-settings.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +215 -179
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +15 -16
- package/dist/log-stream.d.ts.map +1 -1
- package/dist/log-stream.js +18 -96
- package/dist/log-stream.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -3
- package/dist/logger.js.map +1 -1
- package/dist/mdns.d.ts +17 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +52 -18
- package/dist/mdns.js.map +1 -1
- package/dist/modules/adb-module.d.ts +1 -1
- package/dist/modules/adb-module.d.ts.map +1 -1
- package/dist/modules/adb-module.js +4 -3
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/modules/broadcast-module.d.ts +17 -0
- package/dist/modules/broadcast-module.d.ts.map +1 -0
- package/dist/modules/broadcast-module.js +26 -0
- package/dist/modules/broadcast-module.js.map +1 -0
- package/dist/modules/idotmatrix-module.d.ts +9 -0
- package/dist/modules/idotmatrix-module.d.ts.map +1 -0
- package/dist/modules/idotmatrix-module.js +46 -0
- package/dist/modules/idotmatrix-module.js.map +1 -0
- package/dist/modules/index.d.ts +3 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +9 -3
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/pixoo-module.d.ts.map +1 -1
- package/dist/modules/pixoo-module.js +14 -4
- package/dist/modules/pixoo-module.js.map +1 -1
- package/dist/modules/serial-module.d.ts +4 -0
- package/dist/modules/serial-module.d.ts.map +1 -1
- package/dist/modules/serial-module.js +14 -12
- package/dist/modules/serial-module.js.map +1 -1
- package/dist/modules/timebox-module.d.ts +9 -0
- package/dist/modules/timebox-module.d.ts.map +1 -0
- package/dist/modules/timebox-module.js +46 -0
- package/dist/modules/timebox-module.js.map +1 -0
- package/dist/modules/types.d.ts +4 -2
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/openclaw-session.d.ts +18 -0
- package/dist/openclaw-session.d.ts.map +1 -0
- package/dist/openclaw-session.js +34 -0
- package/dist/openclaw-session.js.map +1 -0
- package/dist/output-parser.d.ts +30 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +125 -9
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +26 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +258 -49
- package/dist/passive-observer.js.map +1 -1
- package/dist/permission-resolver.d.ts +47 -0
- package/dist/permission-resolver.d.ts.map +1 -0
- package/dist/permission-resolver.js +103 -0
- package/dist/permission-resolver.js.map +1 -0
- package/dist/pixoo/micro-glyphs.d.ts +49 -0
- package/dist/pixoo/micro-glyphs.d.ts.map +1 -0
- package/dist/pixoo/micro-glyphs.js +276 -0
- package/dist/pixoo/micro-glyphs.js.map +1 -0
- package/dist/pixoo/pixoo-bridge.d.ts +2 -2
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
- package/dist/pixoo/pixoo-bridge.js +93 -25
- package/dist/pixoo/pixoo-bridge.js.map +1 -1
- package/dist/pixoo/pixoo-camera.d.ts +19 -3
- package/dist/pixoo/pixoo-camera.d.ts.map +1 -1
- package/dist/pixoo/pixoo-camera.js +62 -27
- package/dist/pixoo/pixoo-camera.js.map +1 -1
- package/dist/pixoo/pixoo-client.d.ts +13 -3
- package/dist/pixoo/pixoo-client.d.ts.map +1 -1
- package/dist/pixoo/pixoo-client.js +51 -22
- package/dist/pixoo/pixoo-client.js.map +1 -1
- package/dist/pixoo/pixoo-discover.d.ts +37 -0
- package/dist/pixoo/pixoo-discover.d.ts.map +1 -0
- package/dist/pixoo/pixoo-discover.js +105 -0
- package/dist/pixoo/pixoo-discover.js.map +1 -0
- package/dist/pixoo/pixoo-renderer.d.ts +8 -3
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +189 -60
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pixoo/pixoo-settings.d.ts +6 -0
- package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
- package/dist/pixoo/pixoo-settings.js +9 -0
- package/dist/pixoo/pixoo-settings.js.map +1 -1
- package/dist/pixoo/pixoo-sprites.d.ts +60 -11
- package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
- package/dist/pixoo/pixoo-sprites.js +512 -209
- package/dist/pixoo/pixoo-sprites.js.map +1 -1
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +8 -2
- package/dist/pty-manager.js.map +1 -1
- package/dist/session-activity.d.ts +14 -0
- package/dist/session-activity.d.ts.map +1 -0
- package/dist/session-activity.js +160 -0
- package/dist/session-activity.js.map +1 -0
- package/dist/session-aggregator.d.ts +17 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +48 -9
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-registry.d.ts +71 -3
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +137 -8
- package/dist/session-registry.js.map +1 -1
- package/dist/session-timeline-relay.d.ts.map +1 -1
- package/dist/session-timeline-relay.js +13 -4
- package/dist/session-timeline-relay.js.map +1 -1
- package/dist/session-transcript-timeline.d.ts +49 -0
- package/dist/session-transcript-timeline.d.ts.map +1 -0
- package/dist/session-transcript-timeline.js +276 -0
- package/dist/session-transcript-timeline.js.map +1 -0
- package/dist/state-machine.d.ts +6 -1
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +86 -28
- package/dist/state-machine.js.map +1 -1
- package/dist/timebox/timebox-daemon-sync.d.ts +19 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -0
- package/dist/timebox/timebox-daemon-sync.js +135 -0
- package/dist/timebox/timebox-daemon-sync.js.map +1 -0
- package/dist/timebox/timebox-discover.d.ts +19 -0
- package/dist/timebox/timebox-discover.d.ts.map +1 -0
- package/dist/timebox/timebox-discover.js +87 -0
- package/dist/timebox/timebox-discover.js.map +1 -0
- package/dist/timebox/timebox-settings.d.ts +32 -0
- package/dist/timebox/timebox-settings.d.ts.map +1 -0
- package/dist/timebox/timebox-settings.js +80 -0
- package/dist/timebox/timebox-settings.js.map +1 -0
- package/dist/timeline-store.d.ts +63 -3
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +292 -14
- package/dist/timeline-store.js.map +1 -1
- package/dist/timeline-summarizer.d.ts.map +1 -1
- package/dist/timeline-summarizer.js +29 -5
- package/dist/timeline-summarizer.js.map +1 -1
- package/dist/tui/ansi.d.ts +0 -4
- package/dist/tui/ansi.d.ts.map +1 -1
- package/dist/tui/ansi.js +14 -7
- package/dist/tui/ansi.js.map +1 -1
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.d.ts.map +1 -1
- package/dist/tui/dashboard.js +24 -7
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/tui/renderer.d.ts +36 -0
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +303 -119
- package/dist/tui/renderer.js.map +1 -1
- package/dist/tui/terrarium.d.ts.map +1 -1
- package/dist/tui/terrarium.js +37 -49
- package/dist/tui/terrarium.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +7 -1
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +10 -3
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +74 -12
- package/dist/usage-event.js.map +1 -1
- package/dist/utility-proxy.d.ts.map +1 -1
- package/dist/utility-proxy.js +8 -0
- package/dist/utility-proxy.js.map +1 -1
- package/dist/utils/project-name.d.ts +3 -0
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +44 -2
- package/dist/utils/project-name.js.map +1 -1
- package/dist/windows-service.d.ts +26 -0
- package/dist/windows-service.d.ts.map +1 -0
- package/dist/windows-service.js +145 -0
- package/dist/windows-service.js.map +1 -0
- package/dist/ws-server.d.ts +22 -1
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +89 -4
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +112 -0
- package/package.json +9 -3
- package/scripts/build-fm-helper.mjs +26 -0
- package/LICENSE +0 -21
- package/dist/apme/tuner.d.ts +0 -64
- package/dist/apme/tuner.d.ts.map +0 -1
- package/dist/apme/tuner.js +0 -297
- package/dist/apme/tuner.js.map +0 -1
- package/dist/d200h/hid-protocol.d.ts +0 -62
- package/dist/d200h/hid-protocol.d.ts.map +0 -1
- package/dist/d200h/hid-protocol.js +0 -124
- package/dist/d200h/hid-protocol.js.map +0 -1
- package/dist/d200h/image-renderer.d.ts +0 -36
- package/dist/d200h/image-renderer.d.ts.map +0 -1
- package/dist/d200h/image-renderer.js +0 -487
- package/dist/d200h/image-renderer.js.map +0 -1
- package/dist/modules/d200h-module.d.ts +0 -43
- package/dist/modules/d200h-module.d.ts.map +0 -1
- package/dist/modules/d200h-module.js +0 -360
- package/dist/modules/d200h-module.js.map +0 -1
- package/dist/modules/d200h-renderer.d.ts +0 -7
- package/dist/modules/d200h-renderer.d.ts.map +0 -1
- package/dist/modules/d200h-renderer.js +0 -321
- package/dist/modules/d200h-renderer.js.map +0 -1
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* zoom 2.0 → octopus ~14px wide, crayfish ~24px wide
|
|
24
24
|
*/
|
|
25
25
|
import { worldToScreen, isVisible } from './pixoo-camera.js';
|
|
26
|
+
import { State, STATE_COLORS } from '@agentdeck/shared';
|
|
26
27
|
// ===== Cell Types (shared with ESP32/Android) =====
|
|
27
28
|
const EMPTY = 0;
|
|
28
29
|
const BODY = 1;
|
|
@@ -32,132 +33,324 @@ const RIGHT_ARM = 4;
|
|
|
32
33
|
const LEFT_LEG = 5;
|
|
33
34
|
const RIGHT_LEG = 6;
|
|
34
35
|
const ANTENNA = 7;
|
|
35
|
-
// ===== Octopus
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
43
|
-
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
|
|
44
|
-
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
|
|
45
|
-
[0, 1, 1, 1, 2, 1, 1, 1,
|
|
46
|
-
[0, 1, 1, 1, 2, 1, 1, 1,
|
|
47
|
-
[
|
|
48
|
-
[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
49
|
-
[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
50
|
-
[
|
|
51
|
-
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
52
|
-
[0, 0,
|
|
53
|
-
[0, 0,
|
|
54
|
-
[0, 0,
|
|
36
|
+
// ===== Octopus HD 24×24 — Claude Code mascot aligned with SVG viewBox =====
|
|
37
|
+
export const OCTOPUS_GRID_HD = [
|
|
38
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
39
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
40
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
41
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
42
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
43
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
44
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
45
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
46
|
+
[0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0],
|
|
47
|
+
[0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0],
|
|
48
|
+
[0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0],
|
|
49
|
+
[3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4],
|
|
50
|
+
[3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4],
|
|
51
|
+
[3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4],
|
|
52
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
53
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
54
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
55
|
+
[0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0],
|
|
56
|
+
[0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0],
|
|
57
|
+
[0, 0, 0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 6, 6, 0, 0, 6, 6, 0, 0, 0, 0],
|
|
58
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
59
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
60
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
61
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
55
62
|
];
|
|
56
|
-
const
|
|
57
|
-
const
|
|
63
|
+
const OCTO_HD_COLS = 24;
|
|
64
|
+
const OCTO_HD_ROWS = 24;
|
|
65
|
+
// ===== Octopus MD 14×13 — Claude Code boxy mascot =====
|
|
66
|
+
export const OCTOPUS_GRID_MD = [
|
|
67
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // Flat top (12)
|
|
68
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // (14)
|
|
69
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // (14)
|
|
70
|
+
[1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1], // Wide eye holes (2px)
|
|
71
|
+
[1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1], // Eye holes bottom
|
|
72
|
+
[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Arms + body (14)
|
|
73
|
+
[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Arms + body (14)
|
|
74
|
+
[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Arms + body (14)
|
|
75
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // Slightly tapered waist (12)
|
|
76
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], // (12)
|
|
77
|
+
[0, 5, 5, 0, 5, 5, 0, 0, 6, 6, 0, 6, 6, 0], // Thick 2px legs for distortion-free downscaling
|
|
78
|
+
[0, 5, 5, 0, 5, 5, 0, 0, 6, 6, 0, 6, 6, 0],
|
|
79
|
+
[0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0]
|
|
80
|
+
];
|
|
81
|
+
const OCTO_GRID_MD_COLS = 14;
|
|
82
|
+
const OCTO_GRID_MD_ROWS = 13;
|
|
83
|
+
export const OCTOPUS_GRID = OCTOPUS_GRID_HD; // alias for compatibility
|
|
84
|
+
const OCTO_COLS = OCTO_HD_COLS;
|
|
85
|
+
const OCTO_ROWS = OCTO_HD_ROWS;
|
|
58
86
|
/** World width of octopus in normalized coords. */
|
|
59
87
|
export const OCTO_WORLD_W = 7 / 64;
|
|
60
88
|
// ===== Octopus LOD 7×7 — chunky grid for zoom < 1.3 =====
|
|
61
|
-
const OCTOPUS_LOD = [
|
|
62
|
-
[0, 1, 1, 1, 1, 1, 0],
|
|
63
|
-
[1, 1, 1, 1, 1, 1, 1],
|
|
64
|
-
[1,
|
|
65
|
-
[
|
|
66
|
-
[0, 1, 1, 1, 1, 1, 0],
|
|
67
|
-
[0,
|
|
68
|
-
[0, 0,
|
|
89
|
+
export const OCTOPUS_LOD = [
|
|
90
|
+
[0, 1, 1, 1, 1, 1, 0],
|
|
91
|
+
[1, 1, 1, 1, 1, 1, 1],
|
|
92
|
+
[1, 2, 2, 1, 2, 2, 1], // Bold eye holes
|
|
93
|
+
[3, 1, 1, 1, 1, 1, 4], // Arms + body
|
|
94
|
+
[0, 1, 1, 1, 1, 1, 0],
|
|
95
|
+
[0, 5, 5, 1, 6, 6, 0], // Thick limbs
|
|
96
|
+
[0, 5, 0, 0, 0, 6, 0],
|
|
69
97
|
];
|
|
70
98
|
const OCTO_LOD_COLS = 7;
|
|
71
99
|
const OCTO_LOD_ROWS = 7;
|
|
72
|
-
// ===== Crayfish
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
[0, 0, 7, 0, 0, 0, 0, 0, 0,
|
|
80
|
-
[0, 0, 0,
|
|
81
|
-
[0, 0, 0, 1, 1, 1, 1, 1, 1,
|
|
82
|
-
[
|
|
83
|
-
[0,
|
|
84
|
-
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
|
|
85
|
-
[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], //
|
|
86
|
-
[0,
|
|
100
|
+
// ===== Crayfish HD 24×24 — OpenClaw mascot, round full body matching design/brand/openclaw.svg =====
|
|
101
|
+
// Round body, antennae curving to the top corners, two separated side-claw blobs (3/4), two leg
|
|
102
|
+
// stubs (5/6). Eyes are NOT in the grid — drawn as a teal+black overlay at eyeRow/eyeCols below.
|
|
103
|
+
export const CRAYFISH_GRID_HD = [
|
|
104
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
105
|
+
[0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0],
|
|
106
|
+
[0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0],
|
|
107
|
+
[0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0],
|
|
108
|
+
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
109
|
+
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
110
|
+
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
111
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
112
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
113
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], // eyes overlaid at cols 8,14
|
|
114
|
+
[0, 3, 3, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 4, 0],
|
|
115
|
+
[3, 3, 3, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 4, 4, 4],
|
|
116
|
+
[3, 3, 3, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 4, 4, 4],
|
|
117
|
+
[0, 3, 3, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 4, 0],
|
|
118
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
119
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
120
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
121
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
122
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
123
|
+
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
124
|
+
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
125
|
+
[0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 1, 1, 1, 1, 6, 6, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
126
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 1, 1, 1, 1, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
127
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
128
|
+
];
|
|
129
|
+
const CF_HD_COLS = 24;
|
|
130
|
+
const CF_HD_ROWS = 24;
|
|
131
|
+
// ===== Crayfish MD 12×8 — OpenClaw mascot, round body (eyes overlaid at row 3, cols 4/7) =====
|
|
132
|
+
export const CRAYFISH_GRID_MD = [
|
|
133
|
+
[0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0], // Antennae tips at the corners
|
|
134
|
+
[0, 0, 7, 0, 0, 1, 1, 0, 0, 7, 0, 0], // Antennae + head crown
|
|
135
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], // Rounded head dome
|
|
136
|
+
[0, 3, 3, 1, 1, 1, 1, 1, 1, 4, 4, 0], // Claws + body (eyes overlay row 3)
|
|
137
|
+
[3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4], // Wide claws + body
|
|
138
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // Thorax
|
|
139
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], // Lower body
|
|
140
|
+
[0, 0, 0, 5, 5, 0, 0, 6, 6, 0, 0, 0] // Walking legs
|
|
87
141
|
];
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
142
|
+
const CF_GRID_MD_COLS = 12;
|
|
143
|
+
const CF_GRID_MD_ROWS = 8;
|
|
144
|
+
export const CRAYFISH_GRID = CRAYFISH_GRID_HD; // alias for compatibility
|
|
145
|
+
const CF_COLS = CF_HD_COLS;
|
|
146
|
+
const CF_ROWS = CF_HD_ROWS;
|
|
147
|
+
/** World width of crayfish in normalized coords. */
|
|
91
148
|
export const CF_WORLD_W = 12 / 64;
|
|
92
|
-
// ===== Crayfish LOD 8×6 — compact
|
|
93
|
-
const CRAYFISH_LOD = [
|
|
94
|
-
[0, 7, 0, 0, 0, 0, 7, 0], //
|
|
95
|
-
[0, 0, 1, 1, 1, 1, 0, 0], //
|
|
96
|
-
[3,
|
|
97
|
-
[0, 1,
|
|
98
|
-
[0,
|
|
99
|
-
[0,
|
|
149
|
+
// ===== Crayfish LOD 8×6 — compact round body (eyes overlaid at row 2, cols 2/5) =====
|
|
150
|
+
export const CRAYFISH_LOD = [
|
|
151
|
+
[0, 7, 0, 0, 0, 0, 7, 0], // Antennae at corners
|
|
152
|
+
[0, 0, 1, 1, 1, 1, 0, 0], // Head crown
|
|
153
|
+
[3, 3, 1, 1, 1, 1, 4, 4], // Claws + body (eyes overlay row 2)
|
|
154
|
+
[0, 1, 1, 1, 1, 1, 1, 0], // Body
|
|
155
|
+
[0, 1, 1, 1, 1, 1, 1, 0], // Body
|
|
156
|
+
[0, 0, 5, 0, 0, 6, 0, 0], // Legs
|
|
100
157
|
];
|
|
101
158
|
const CF_LOD_COLS = 8;
|
|
102
159
|
const CF_LOD_ROWS = 6;
|
|
103
|
-
// ===== Jellyfish
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
[0, 0, 1, 1,
|
|
110
|
-
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0
|
|
111
|
-
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
|
112
|
-
[
|
|
113
|
-
[
|
|
114
|
-
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
|
115
|
-
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
116
|
-
[0, 0, 1, 1,
|
|
160
|
+
// ===== Jellyfish HD 24×24 — Codex cloud mascot aligned with SVG viewBox =====
|
|
161
|
+
export const JELLYFISH_GRID_HD = [
|
|
162
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
163
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
164
|
+
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
165
|
+
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
166
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
167
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
168
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
169
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
170
|
+
[0, 0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], // prompt `>`
|
|
171
|
+
[0, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
172
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
173
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
174
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
175
|
+
[0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
176
|
+
[0, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
177
|
+
[0, 0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0], // prompt `_`
|
|
178
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0],
|
|
179
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
180
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
181
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
182
|
+
[0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0], // bottom lobes are BREATHE_EDGE
|
|
183
|
+
[0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0],
|
|
184
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
185
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
117
186
|
];
|
|
118
|
-
const
|
|
119
|
-
const
|
|
187
|
+
const JF_HD_COLS = 24;
|
|
188
|
+
const JF_HD_ROWS = 24;
|
|
189
|
+
// ===== Jellyfish MD 13×11 — Codex mascot =====
|
|
190
|
+
export const JELLYFISH_GRID_MD = [
|
|
191
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], // Rounded top lobe
|
|
192
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
193
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
194
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
195
|
+
[3, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3], // `>` top stroke (2px thick)
|
|
196
|
+
[3, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 1, 3], // `>` point + long `_` prompt (4px)
|
|
197
|
+
[3, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3], // `>` bottom stroke
|
|
198
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
199
|
+
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
200
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
201
|
+
[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0] // Bottom cloud lobe, not dangling legs
|
|
202
|
+
];
|
|
203
|
+
const JF_GRID_MD_COLS = 13;
|
|
204
|
+
const JF_GRID_MD_ROWS = 11;
|
|
205
|
+
export const JELLYFISH_GRID = JELLYFISH_GRID_HD; // alias for compatibility
|
|
206
|
+
const JF_COLS = JF_HD_COLS;
|
|
207
|
+
const JF_ROWS = JF_HD_ROWS;
|
|
120
208
|
/** World width of jellyfish in normalized coords. */
|
|
121
|
-
export const JF_WORLD_W =
|
|
122
|
-
// ===== Jellyfish LOD 7
|
|
123
|
-
const JELLYFISH_LOD = [
|
|
124
|
-
[0, 1, 1,
|
|
125
|
-
[1, 1, 1, 1, 1, 1, 1],
|
|
126
|
-
[1,
|
|
127
|
-
[1, 1,
|
|
128
|
-
[
|
|
209
|
+
export const JF_WORLD_W = 13 / 64;
|
|
210
|
+
// ===== Jellyfish LOD 9×7 — compact for zoom < 1.3 =====
|
|
211
|
+
export const JELLYFISH_LOD = [
|
|
212
|
+
[0, 0, 1, 1, 1, 1, 1, 0, 0],
|
|
213
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
214
|
+
[1, 2, 2, 1, 1, 1, 1, 1, 1], // `>` upper stroke (2px)
|
|
215
|
+
[1, 1, 2, 2, 1, 2, 2, 2, 1], // `>` point + `_` prompt (3px)
|
|
216
|
+
[1, 2, 2, 1, 1, 1, 1, 1, 1], // `>` lower stroke
|
|
217
|
+
[0, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
218
|
+
[0, 0, 1, 1, 1, 1, 1, 0, 0],
|
|
129
219
|
];
|
|
130
|
-
const JF_LOD_COLS =
|
|
131
|
-
const JF_LOD_ROWS =
|
|
220
|
+
const JF_LOD_COLS = 9;
|
|
221
|
+
const JF_LOD_ROWS = 7;
|
|
132
222
|
// Cell type constants for jellyfish
|
|
133
223
|
const MARKING = 2;
|
|
134
224
|
const BREATHE_EDGE = 3;
|
|
135
|
-
// ===== OpenCode
|
|
225
|
+
// ===== OpenCode HD 24×24 — OpenCode mascot aligned with SVG viewBox =====
|
|
226
|
+
export const OPENCODE_GRID_HD = [
|
|
227
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
228
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
229
|
+
[0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0],
|
|
230
|
+
[0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0],
|
|
231
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
|
|
232
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
|
|
233
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
|
|
234
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0],
|
|
235
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
236
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
237
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
238
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
239
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
240
|
+
[0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
241
|
+
[0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
242
|
+
[0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9],
|
|
243
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
|
|
244
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
|
|
245
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
|
|
246
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
|
|
247
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
|
|
248
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
|
|
249
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
250
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
251
|
+
];
|
|
252
|
+
const OC_HD_COLS = 24;
|
|
253
|
+
const OC_HD_ROWS = 24;
|
|
254
|
+
// ===== OpenCode MD 10×10 — nested-square logo =====
|
|
136
255
|
const OPENCODE_FRAME = 8;
|
|
137
256
|
const OPENCODE_CORE = 9;
|
|
138
|
-
const
|
|
139
|
-
[8, 8, 8, 8, 8, 8,
|
|
140
|
-
[8, 0, 0, 0, 0,
|
|
141
|
-
[8, 0,
|
|
142
|
-
[8, 0,
|
|
143
|
-
[8, 0,
|
|
144
|
-
[8,
|
|
145
|
-
[
|
|
146
|
-
[
|
|
147
|
-
[
|
|
257
|
+
export const OPENCODE_GRID_MD = [
|
|
258
|
+
[8, 8, 8, 8, 8, 8, 0, 0, 0, 0],
|
|
259
|
+
[8, 0, 0, 0, 0, 8, 0, 0, 0, 0],
|
|
260
|
+
[8, 0, 0, 0, 0, 8, 0, 0, 0, 0],
|
|
261
|
+
[8, 0, 0, 0, 0, 8, 0, 0, 0, 0],
|
|
262
|
+
[8, 0, 0, 0, 9, 8, 9, 9, 9, 9],
|
|
263
|
+
[8, 8, 8, 8, 8, 8, 9, 9, 9, 9],
|
|
264
|
+
[0, 0, 0, 0, 9, 9, 9, 9, 9, 9],
|
|
265
|
+
[0, 0, 0, 0, 9, 9, 9, 9, 9, 9],
|
|
266
|
+
[0, 0, 0, 0, 9, 9, 9, 9, 9, 9],
|
|
267
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
148
268
|
];
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
[8,
|
|
156
|
-
[8, 0, 0,
|
|
157
|
-
[8,
|
|
269
|
+
const OC_GRID_MD_COLS = 10;
|
|
270
|
+
const OC_GRID_MD_ROWS = 10;
|
|
271
|
+
export const OPENCODE_GRID = OPENCODE_GRID_HD; // alias for compatibility
|
|
272
|
+
const OC_COLS = OC_HD_COLS;
|
|
273
|
+
const OC_ROWS = OC_HD_ROWS;
|
|
274
|
+
export const OPENCODE_LOD = [
|
|
275
|
+
[8, 8, 8, 8, 0, 0],
|
|
276
|
+
[8, 0, 0, 8, 0, 0],
|
|
277
|
+
[8, 0, 9, 8, 9, 9],
|
|
278
|
+
[8, 8, 8, 8, 9, 9],
|
|
279
|
+
[0, 0, 9, 9, 9, 9],
|
|
280
|
+
[0, 0, 9, 9, 9, 9],
|
|
158
281
|
];
|
|
159
282
|
const OC_LOD_COLS = 6;
|
|
160
283
|
const OC_LOD_ROWS = 6;
|
|
284
|
+
// ===== Antigravity — rainbow peak/arc mark =====
|
|
285
|
+
const ANTIGRAVITY_GRID = [
|
|
286
|
+
'....YOO....',
|
|
287
|
+
'....YOO....',
|
|
288
|
+
'...LYOOR...',
|
|
289
|
+
'...LTORR...',
|
|
290
|
+
'..LLTVPP...',
|
|
291
|
+
'..TTKKVPP..',
|
|
292
|
+
'.TQQK.KVU..',
|
|
293
|
+
'.QQK...KUU.',
|
|
294
|
+
'NQK.....KUU',
|
|
295
|
+
'NN.......UU',
|
|
296
|
+
'...........',
|
|
297
|
+
];
|
|
298
|
+
const AG_COLS = 11;
|
|
299
|
+
const AG_ROWS = 11;
|
|
300
|
+
function selectGrid(zoom, canvasWidth, hd, hdCols, hdRows, md, mdCols, mdRows, lod, lodCols, lodRows) {
|
|
301
|
+
if (canvasWidth <= 32) {
|
|
302
|
+
if (zoom < 1.3) {
|
|
303
|
+
return { grid: lod, cols: lodCols, rows: lodRows };
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// Zoomed-in on a 32px display (iDotMatrix): use the full HD grid, not MD. Now that cell size
|
|
307
|
+
// is resolution-aware (creatureCellSize), HD fits the 32px frame (~60% width) and keeps the
|
|
308
|
+
// creature's true silhouette instead of the coarser MD approximation. MD/LOD stay for the
|
|
309
|
+
// wide (zoom<1.3) shots where the creature is small and detail would just alias.
|
|
310
|
+
return { grid: hd, cols: hdCols, rows: hdRows };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
if (zoom < 1.3) {
|
|
315
|
+
return { grid: md, cols: mdCols, rows: mdRows };
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
return { grid: hd, cols: hdCols, rows: hdRows };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* On-screen creature width as a fraction of (zoom × output width), independent of LOD grid.
|
|
324
|
+
*
|
|
325
|
+
* A sprite spans `SPRITE_W_FRAC × zoom × w` pixels regardless of which HD/MD/LOD grid is selected
|
|
326
|
+
* (cellSz = span / cols, so the `cols` term cancels). Multiplying by `w` (32 or 64) is what makes
|
|
327
|
+
* sizing resolution-aware — at 32px a creature is exactly half the pixels it is at 64px, so it stays
|
|
328
|
+
* inside the iDotMatrix frame instead of overflowing. 0.1875 reproduces the legacy 64px HD size
|
|
329
|
+
* (24 cols × zoom×0.5 = 12×zoom px = 0.1875 × zoom × 64). See drawOpenCode for the original pattern.
|
|
330
|
+
*/
|
|
331
|
+
const SPRITE_W_FRAC = 0.1875;
|
|
332
|
+
/** Resolution-aware square cell size for a creature sprite.
|
|
333
|
+
*
|
|
334
|
+
* Rounded to a whole pixel so every grid cell is a uniform integer block. A
|
|
335
|
+
* fractional cell size makes adjacent cells round to alternating 1/2px widths,
|
|
336
|
+
* and on the 2×2 eye holes that intermittently opens a 1px body-coloured seam
|
|
337
|
+
* between the two eye rows while the creature moves — the eyes appear to "break"
|
|
338
|
+
* into dots. Integer cells render the eye as a solid block every frame.
|
|
339
|
+
*
|
|
340
|
+
* The sizing zoom is itself quantized to a coarse 0.25 step first. The raw zoom
|
|
341
|
+
* lerps continuously during camera transitions, so a cell size sitting right on
|
|
342
|
+
* a rounding threshold (e.g. zoom where `…/cols` ≈ x.5) flickers 1↔2px frame to
|
|
343
|
+
* frame — and since the eye sits at a fixed column, a cellSz change jumps it by
|
|
344
|
+
* `eyeCol` pixels. Snapping the zoom to discrete steps holds cellSz stable
|
|
345
|
+
* through a transition so the eye only ever moves by whole-creature translation. */
|
|
346
|
+
export function creatureCellSize(zoom, canvasW, cols) {
|
|
347
|
+
const zq = Math.round(zoom * 4) / 4; // 0.25 steps — kills threshold-straddle flicker
|
|
348
|
+
return Math.max(1, Math.round((SPRITE_W_FRAC * zq * canvasW) / cols));
|
|
349
|
+
}
|
|
350
|
+
/** Canonical `#rrggbb` → RGB tuple (LED frame buffers take tuples, not hex). */
|
|
351
|
+
function hexRgb(hex) {
|
|
352
|
+
return [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];
|
|
353
|
+
}
|
|
161
354
|
export const COLORS = {
|
|
162
355
|
// Octopus (terracotta — Android match)
|
|
163
356
|
octopusBody: [0xC0, 0x70, 0x58],
|
|
@@ -181,7 +374,7 @@ export const COLORS = {
|
|
|
181
374
|
// Jellyfish / Codex CLI (indigo — TUI/Android match)
|
|
182
375
|
jellyfishBody: [0x63, 0x66, 0xF1], // indigo #6366F1
|
|
183
376
|
jellyfishEdge: [0x4F, 0x46, 0xE5], // darker edge (breathe cells)
|
|
184
|
-
jellyfishMarking: [
|
|
377
|
+
jellyfishMarking: [0xF5, 0xF7, 0xFF], // near-white (>_ prompt)
|
|
185
378
|
jellyfishGlow: [0x31, 0x33, 0x78], // dim indigo glow halo
|
|
186
379
|
jellyfishPulse: [0xA5, 0xB4, 0xFC], // bioluminescent pulse
|
|
187
380
|
jellyfishSleeping: [0x3A, 0x3C, 0x90], // dimmed indigo
|
|
@@ -190,6 +383,17 @@ export const COLORS = {
|
|
|
190
383
|
opencodeInner: [0x4B, 0x46, 0x46], // dark core
|
|
191
384
|
opencodePulse: [0xCF, 0xCE, 0xCD], // pulse state
|
|
192
385
|
opencodeSleeping: [0x8A, 0x84, 0x84], // sleep dim
|
|
386
|
+
// Antigravity (reference rainbow peak/arc)
|
|
387
|
+
antigravityLime: [0x5C, 0xD6, 0x4D],
|
|
388
|
+
antigravityTeal: [0x1F, 0xC6, 0xB3],
|
|
389
|
+
antigravityCyan: [0x3A, 0xC7, 0xEB],
|
|
390
|
+
antigravityYellow: [0xF5, 0xCB, 0x24],
|
|
391
|
+
antigravityOrange: [0xFF, 0x84, 0x10],
|
|
392
|
+
antigravityRed: [0xFF, 0x52, 0x41],
|
|
393
|
+
antigravityPink: [0xB7, 0x5C, 0xB6],
|
|
394
|
+
antigravityViolet: [0x66, 0x6F, 0xE1],
|
|
395
|
+
antigravityBlue: [0x24, 0x7E, 0xFF],
|
|
396
|
+
antigravitySky: [0x29, 0xB8, 0xEE],
|
|
193
397
|
// Tetra (neon)
|
|
194
398
|
tetraNeon: [0x00, 0xE5, 0xFF],
|
|
195
399
|
tetraBody: [0x1E, 0x40, 0xAF],
|
|
@@ -235,11 +439,11 @@ export const COLORS = {
|
|
|
235
439
|
// Tank walls (above water)
|
|
236
440
|
tankWall: [0x06, 0x0A, 0x10],
|
|
237
441
|
tankWallEdge: [0x10, 0x14, 0x1C],
|
|
238
|
-
// State shimmer
|
|
239
|
-
stateIdle: [
|
|
240
|
-
stateProcessing: [
|
|
241
|
-
stateAwaiting: [
|
|
242
|
-
stateError: [0xEF, 0x44, 0x44],
|
|
442
|
+
// State shimmer — derived from the canonical shared palette
|
|
443
|
+
stateIdle: hexRgb(STATE_COLORS[State.IDLE]),
|
|
444
|
+
stateProcessing: hexRgb(STATE_COLORS[State.PROCESSING]),
|
|
445
|
+
stateAwaiting: hexRgb(STATE_COLORS[State.AWAITING_PERMISSION]),
|
|
446
|
+
stateError: [0xEF, 0x44, 0x44], // red — no canonical error state color exists
|
|
243
447
|
white: [0xFF, 0xFF, 0xFF],
|
|
244
448
|
black: [0x00, 0x00, 0x00],
|
|
245
449
|
};
|
|
@@ -288,27 +492,45 @@ export function getOpenCodePaletteForSession(sessionIndex = 0) {
|
|
|
288
492
|
pulse: scaleColor(COLORS.opencodePulse, tone),
|
|
289
493
|
};
|
|
290
494
|
}
|
|
495
|
+
export function getAntigravityPaletteForSession(sessionIndex = 0) {
|
|
496
|
+
const tone = SESSION_TONE_FACTORS[Math.max(0, Math.min(SESSION_TONE_FACTORS.length - 1, sessionIndex))];
|
|
497
|
+
return {
|
|
498
|
+
lime: scaleColor(COLORS.antigravityLime, tone),
|
|
499
|
+
teal: scaleColor(COLORS.antigravityTeal, tone),
|
|
500
|
+
cyan: scaleColor(COLORS.antigravityCyan, tone),
|
|
501
|
+
yellow: scaleColor(COLORS.antigravityYellow, tone),
|
|
502
|
+
orange: scaleColor(COLORS.antigravityOrange, tone),
|
|
503
|
+
red: scaleColor(COLORS.antigravityRed, tone),
|
|
504
|
+
pink: scaleColor(COLORS.antigravityPink, tone),
|
|
505
|
+
violet: scaleColor(COLORS.antigravityViolet, tone),
|
|
506
|
+
blue: scaleColor(COLORS.antigravityBlue, tone),
|
|
507
|
+
sky: scaleColor(COLORS.antigravitySky, tone),
|
|
508
|
+
cutout: COLORS.black,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
291
511
|
// ===== Pixel Operations =====
|
|
292
|
-
/** Set a pixel in the
|
|
512
|
+
/** Set a pixel in the dynamic RGB buffer. */
|
|
293
513
|
export function setPixel(buf, x, y, color) {
|
|
294
|
-
|
|
514
|
+
const w = Math.sqrt(buf.length / 3);
|
|
515
|
+
if (x < 0 || x >= w || y < 0 || y >= w)
|
|
295
516
|
return;
|
|
296
517
|
const ix = Math.round(x);
|
|
297
518
|
const iy = Math.round(y);
|
|
298
|
-
if (ix < 0 || ix >=
|
|
519
|
+
if (ix < 0 || ix >= w || iy < 0 || iy >= w)
|
|
299
520
|
return;
|
|
300
|
-
const idx = (iy *
|
|
521
|
+
const idx = (iy * w + ix) * 3;
|
|
301
522
|
buf[idx] = color[0];
|
|
302
523
|
buf[idx + 1] = color[1];
|
|
303
524
|
buf[idx + 2] = color[2];
|
|
304
525
|
}
|
|
305
526
|
/** Alpha-blend a pixel onto existing buffer content. */
|
|
306
527
|
export function blendPixel(buf, x, y, color, alpha) {
|
|
528
|
+
const w = Math.sqrt(buf.length / 3);
|
|
307
529
|
const ix = Math.round(x);
|
|
308
530
|
const iy = Math.round(y);
|
|
309
|
-
if (ix < 0 || ix >=
|
|
531
|
+
if (ix < 0 || ix >= w || iy < 0 || iy >= w || alpha <= 0)
|
|
310
532
|
return;
|
|
311
|
-
const idx = (iy *
|
|
533
|
+
const idx = (iy * w + ix) * 3;
|
|
312
534
|
const a = Math.min(1, alpha);
|
|
313
535
|
const inv = 1 - a;
|
|
314
536
|
buf[idx] = Math.min(255, Math.round(buf[idx] * inv + color[0] * a));
|
|
@@ -317,11 +539,12 @@ export function blendPixel(buf, x, y, color, alpha) {
|
|
|
317
539
|
}
|
|
318
540
|
/** Additive-blend (glow) a pixel. */
|
|
319
541
|
export function glowPixel(buf, x, y, color, intensity) {
|
|
542
|
+
const w = Math.sqrt(buf.length / 3);
|
|
320
543
|
const ix = Math.round(x);
|
|
321
544
|
const iy = Math.round(y);
|
|
322
|
-
if (ix < 0 || ix >=
|
|
545
|
+
if (ix < 0 || ix >= w || iy < 0 || iy >= w || intensity <= 0)
|
|
323
546
|
return;
|
|
324
|
-
const idx = (iy *
|
|
547
|
+
const idx = (iy * w + ix) * 3;
|
|
325
548
|
buf[idx] = Math.min(255, buf[idx] + Math.round(color[0] * intensity));
|
|
326
549
|
buf[idx + 1] = Math.min(255, buf[idx + 1] + Math.round(color[1] * intensity));
|
|
327
550
|
buf[idx + 2] = Math.min(255, buf[idx + 2] + Math.round(color[2] * intensity));
|
|
@@ -336,45 +559,52 @@ export function fillRect(buf, x, y, w, h, color) {
|
|
|
336
559
|
}
|
|
337
560
|
/** Fill a scaled cell (variable size rectangle) — edge rounding for correct PIXEL_ASPECT. */
|
|
338
561
|
function fillCell(buf, x, y, w, h, color) {
|
|
339
|
-
const
|
|
340
|
-
const
|
|
341
|
-
const
|
|
342
|
-
const
|
|
562
|
+
const ix1 = Math.round(x);
|
|
563
|
+
const iy1 = Math.round(y);
|
|
564
|
+
const ix2 = Math.round(x + w);
|
|
565
|
+
const iy2 = Math.round(y + h);
|
|
566
|
+
const iw = Math.max(1, ix2 - ix1);
|
|
567
|
+
const ih = Math.max(1, iy2 - iy1);
|
|
343
568
|
for (let dy = 0; dy < ih; dy++) {
|
|
344
569
|
for (let dx = 0; dx < iw; dx++) {
|
|
345
|
-
setPixel(buf,
|
|
570
|
+
setPixel(buf, ix1 + dx, iy1 + dy, color);
|
|
346
571
|
}
|
|
347
572
|
}
|
|
348
573
|
}
|
|
349
574
|
/** Fill a scaled cell and track drawn pixels for outline generation. */
|
|
350
575
|
function fillCellTracked(buf, x, y, w, h, color, pixels) {
|
|
351
|
-
const
|
|
352
|
-
const
|
|
353
|
-
const
|
|
354
|
-
const
|
|
576
|
+
const canvasW = Math.sqrt(buf.length / 3);
|
|
577
|
+
const ix1 = Math.round(x);
|
|
578
|
+
const iy1 = Math.round(y);
|
|
579
|
+
const ix2 = Math.round(x + w);
|
|
580
|
+
const iy2 = Math.round(y + h);
|
|
581
|
+
const iw = Math.max(1, ix2 - ix1);
|
|
582
|
+
const ih = Math.max(1, iy2 - iy1);
|
|
355
583
|
for (let dy = 0; dy < ih; dy++) {
|
|
356
584
|
for (let dx = 0; dx < iw; dx++) {
|
|
357
|
-
const px =
|
|
358
|
-
const py =
|
|
359
|
-
if (px >= 0 && px <
|
|
360
|
-
const idx = (py *
|
|
585
|
+
const px = ix1 + dx;
|
|
586
|
+
const py = iy1 + dy;
|
|
587
|
+
if (px >= 0 && px < canvasW && py >= 0 && py < canvasW) {
|
|
588
|
+
const idx = (py * canvasW + px) * 3;
|
|
361
589
|
buf[idx] = color[0];
|
|
362
590
|
buf[idx + 1] = color[1];
|
|
363
591
|
buf[idx + 2] = color[2];
|
|
364
|
-
pixels.add(py *
|
|
592
|
+
pixels.add(py * canvasW + px);
|
|
365
593
|
}
|
|
366
594
|
}
|
|
367
595
|
}
|
|
368
596
|
}
|
|
369
597
|
/** Glow-fill a scaled cell — edge rounding. */
|
|
370
598
|
function glowCell(buf, x, y, w, h, color, intensity) {
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
const
|
|
374
|
-
const
|
|
599
|
+
const ix1 = Math.round(x);
|
|
600
|
+
const iy1 = Math.round(y);
|
|
601
|
+
const ix2 = Math.round(x + w);
|
|
602
|
+
const iy2 = Math.round(y + h);
|
|
603
|
+
const iw = Math.max(1, ix2 - ix1);
|
|
604
|
+
const ih = Math.max(1, iy2 - iy1);
|
|
375
605
|
for (let dy = 0; dy < ih; dy++) {
|
|
376
606
|
for (let dx = 0; dx < iw; dx++) {
|
|
377
|
-
glowPixel(buf,
|
|
607
|
+
glowPixel(buf, ix1 + dx, iy1 + dy, color, intensity);
|
|
378
608
|
}
|
|
379
609
|
}
|
|
380
610
|
}
|
|
@@ -408,6 +638,7 @@ function drawCreatureGlow(buf, cx, cy, rx, ry, glowColor, intensity = 0.15, isLO
|
|
|
408
638
|
// ===== Creature Outline =====
|
|
409
639
|
/** 8-directional colored outline around tracked creature pixels. */
|
|
410
640
|
function drawCreatureOutline(buf, creaturePixels, bodyColor, alpha = 0.8) {
|
|
641
|
+
const w = Math.sqrt(buf.length / 3);
|
|
411
642
|
const outlineColor = [
|
|
412
643
|
Math.round(bodyColor[0] * 0.5),
|
|
413
644
|
Math.round(bodyColor[1] * 0.5),
|
|
@@ -415,17 +646,17 @@ function drawCreatureOutline(buf, creaturePixels, bodyColor, alpha = 0.8) {
|
|
|
415
646
|
];
|
|
416
647
|
const neighbors = [-1, 0, 1];
|
|
417
648
|
for (const key of creaturePixels) {
|
|
418
|
-
const cx = key %
|
|
419
|
-
const cy = Math.floor(key /
|
|
649
|
+
const cx = key % w;
|
|
650
|
+
const cy = Math.floor(key / w);
|
|
420
651
|
for (const ndx of neighbors) {
|
|
421
652
|
for (const ndy of neighbors) {
|
|
422
653
|
if (ndx === 0 && ndy === 0)
|
|
423
654
|
continue;
|
|
424
655
|
const nx = cx + ndx;
|
|
425
656
|
const ny = cy + ndy;
|
|
426
|
-
if (nx < 0 || nx >=
|
|
657
|
+
if (nx < 0 || nx >= w || ny < 0 || ny >= w)
|
|
427
658
|
continue;
|
|
428
|
-
if (!creaturePixels.has(ny *
|
|
659
|
+
if (!creaturePixels.has(ny * w + nx)) {
|
|
429
660
|
blendPixel(buf, nx, ny, outlineColor, alpha);
|
|
430
661
|
}
|
|
431
662
|
}
|
|
@@ -499,26 +730,37 @@ export function drawOctopus(buf, worldX, worldY, state, animFrame, cam, palette
|
|
|
499
730
|
if (!isVisible(worldX, worldY, cam, 0.15))
|
|
500
731
|
return;
|
|
501
732
|
const [scx, scy] = worldToScreen(worldX, worldY, cam);
|
|
733
|
+
const w = Math.sqrt(buf.length / 3);
|
|
502
734
|
// LOD selection
|
|
503
|
-
const
|
|
504
|
-
const grid =
|
|
505
|
-
const cols =
|
|
506
|
-
const rows =
|
|
507
|
-
//
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
const
|
|
735
|
+
const select = selectGrid(cam.zoom, w, OCTOPUS_GRID_HD, OCTO_HD_COLS, OCTO_HD_ROWS, OCTOPUS_GRID_MD, OCTO_GRID_MD_COLS, OCTO_GRID_MD_ROWS, OCTOPUS_LOD, OCTO_LOD_COLS, OCTO_LOD_ROWS);
|
|
736
|
+
const grid = select.grid;
|
|
737
|
+
const cols = select.cols;
|
|
738
|
+
const rows = select.rows;
|
|
739
|
+
// Resolution-aware cell size — sprite spans a fixed fraction of (zoom × output width),
|
|
740
|
+
// so it scales down on the 32px iDotMatrix instead of overflowing the frame.
|
|
741
|
+
const cellSz = creatureCellSize(cam.zoom, w, cols);
|
|
742
|
+
const spriteW = cols * cellSz;
|
|
743
|
+
const spriteH = rows * cellSz;
|
|
744
|
+
// Snap the sprite origin to whole pixels. The cells tile from this origin via
|
|
745
|
+
// round(base + col*cellSz); if `base` is fractional, a swimming creature's
|
|
746
|
+
// sub-pixel screen drift shifts the rounding phase every frame, so each cell
|
|
747
|
+
// oscillates ±1px (shimmer). On the small high-contrast eye holes that reads
|
|
748
|
+
// as the eyes "breaking up" while the octopus moves. Rounding the origin makes
|
|
749
|
+
// the cell pattern frame-stable (only an integer translation changes), so the
|
|
750
|
+
// creature moves in clean 1px steps with rock-steady eyes.
|
|
511
751
|
const baseX = Math.round(scx - spriteW / 2);
|
|
512
752
|
const baseY = Math.round(scy - spriteH / 2);
|
|
513
|
-
// Working: gentle vertical bob (
|
|
753
|
+
// Working: gentle vertical bob (scaled by cellSz)
|
|
514
754
|
const breathPx = state === 'working'
|
|
515
|
-
? Math.round(Math.sin(animFrame * 0.3) * 1.5)
|
|
755
|
+
? Math.round(Math.sin(animFrame * 0.3) * cellSz * 1.5)
|
|
516
756
|
: 0;
|
|
517
757
|
// Body color by state
|
|
518
758
|
const bodyColor = state === 'working' ? palette.starburst
|
|
519
759
|
: state === 'sleeping' ? palette.sleeping
|
|
520
760
|
: palette.body;
|
|
521
|
-
//
|
|
761
|
+
// Track drawn pixels for outline generation
|
|
762
|
+
const trackedPixels = new Set();
|
|
763
|
+
// Draw all cells
|
|
522
764
|
for (let row = 0; row < rows; row++) {
|
|
523
765
|
for (let col = 0; col < cols; col++) {
|
|
524
766
|
const cellType = grid[row][col];
|
|
@@ -541,16 +783,18 @@ export function drawOctopus(buf, worldX, worldY, state, animFrame, cam, palette
|
|
|
541
783
|
// Tentacle animation only — arms stay fixed (attached to body)
|
|
542
784
|
let dx = 0;
|
|
543
785
|
if (state !== 'sleeping' && (cellType === LEFT_LEG || cellType === RIGHT_LEG)) {
|
|
544
|
-
dx = Math.round(Math.sin(animFrame * 0.2 + col * 1.8) * 1.5);
|
|
786
|
+
dx = Math.round(Math.sin(animFrame * 0.2 + col * 1.8) * cellSz * 1.5);
|
|
545
787
|
}
|
|
546
|
-
|
|
788
|
+
fillCellTracked(buf, baseX + col * cellSz + dx, baseY + row * cellSz + breathPx, cellSz, cellSz, color, trackedPixels);
|
|
547
789
|
}
|
|
548
790
|
}
|
|
791
|
+
// Draw outline to separate octopus from blue water (matches crayfish style)
|
|
792
|
+
drawCreatureOutline(buf, trackedPixels, bodyColor, 0.8);
|
|
549
793
|
// "?" bubble when asking
|
|
550
794
|
if (state === 'asking') {
|
|
551
795
|
const bobY = Math.round(Math.sin(animFrame * 0.25));
|
|
552
796
|
const bx = scx;
|
|
553
|
-
const by = baseY - 3 + bobY;
|
|
797
|
+
const by = baseY - Math.round(cellSz * 3) + bobY;
|
|
554
798
|
const r = 3;
|
|
555
799
|
for (let dy = -r; dy <= r; dy++) {
|
|
556
800
|
for (let dx = -r; dx <= r; dx++) {
|
|
@@ -569,7 +813,7 @@ export function drawOctopus(buf, worldX, worldY, state, animFrame, cam, palette
|
|
|
569
813
|
// Starburst particles when working
|
|
570
814
|
if (state === 'working') {
|
|
571
815
|
const sparkPhase = animFrame * 0.35;
|
|
572
|
-
const dist = 5 + Math.sin(animFrame * 0.25) * 3;
|
|
816
|
+
const dist = (5 + Math.sin(animFrame * 0.25) * 3) * cellSz;
|
|
573
817
|
for (let i = 0; i < 6; i++) {
|
|
574
818
|
const angle = sparkPhase + (i * Math.PI * 2 / 6);
|
|
575
819
|
const sx = scx + Math.cos(angle) * dist;
|
|
@@ -589,28 +833,29 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
|
|
|
589
833
|
if (!isVisible(worldX, worldY, cam, 0.15))
|
|
590
834
|
return;
|
|
591
835
|
const [scx, scy] = worldToScreen(worldX, worldY, cam);
|
|
836
|
+
const w = Math.sqrt(buf.length / 3);
|
|
592
837
|
// LOD selection
|
|
593
|
-
const
|
|
594
|
-
const grid =
|
|
595
|
-
const cols =
|
|
596
|
-
const rows =
|
|
597
|
-
//
|
|
598
|
-
const cellSz =
|
|
599
|
-
const spriteW = cols;
|
|
600
|
-
const spriteH = rows;
|
|
601
|
-
const baseX =
|
|
602
|
-
const baseY =
|
|
838
|
+
const select = selectGrid(cam.zoom, w, JELLYFISH_GRID_HD, JF_HD_COLS, JF_HD_ROWS, JELLYFISH_GRID_MD, JF_GRID_MD_COLS, JF_GRID_MD_ROWS, JELLYFISH_LOD, JF_LOD_COLS, JF_LOD_ROWS);
|
|
839
|
+
const grid = select.grid;
|
|
840
|
+
const cols = select.cols;
|
|
841
|
+
const rows = select.rows;
|
|
842
|
+
// Resolution-aware cell size (see creatureCellSize) — scales with output width.
|
|
843
|
+
const cellSz = creatureCellSize(cam.zoom, w, cols);
|
|
844
|
+
const spriteW = cols * cellSz;
|
|
845
|
+
const spriteH = rows * cellSz;
|
|
846
|
+
const baseX = scx - spriteW / 2;
|
|
847
|
+
const baseY = scy - spriteH / 2;
|
|
603
848
|
// Bell pulse: contraction/expansion
|
|
604
849
|
const pulseSpeed = state === 'working' ? 0.25 : 0.06;
|
|
605
850
|
const pulsePhase = Math.sin(animFrame * pulseSpeed);
|
|
606
851
|
const contracting = pulsePhase < 0;
|
|
607
|
-
// Vertical bob when working
|
|
852
|
+
// Vertical bob when working (scaled by cellSz)
|
|
608
853
|
const breathPx = state === 'working'
|
|
609
|
-
? Math.round(Math.sin(animFrame * 0.3) * 1.5)
|
|
854
|
+
? Math.round(Math.sin(animFrame * 0.3) * cellSz * 1.5)
|
|
610
855
|
: 0;
|
|
611
856
|
// Bioluminescent body color pulse when working
|
|
612
857
|
const bodyColor = state === 'working'
|
|
613
|
-
? lerpColor(palette.body, palette.pulse, (Math.sin(animFrame * 0.2) + 1) * 0.
|
|
858
|
+
? lerpColor(palette.body, palette.pulse, 0.18 + ((Math.sin(animFrame * 0.2) + 1) * 0.18))
|
|
614
859
|
: state === 'sleeping' ? palette.sleeping
|
|
615
860
|
: palette.body;
|
|
616
861
|
// >_ marking blink
|
|
@@ -646,7 +891,7 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
|
|
|
646
891
|
if (state === 'asking') {
|
|
647
892
|
const bobY = Math.round(Math.sin(animFrame * 0.25));
|
|
648
893
|
const bx = scx;
|
|
649
|
-
const by = baseY - 3 + bobY;
|
|
894
|
+
const by = baseY - Math.round(cellSz * 3) + bobY;
|
|
650
895
|
const r = 3;
|
|
651
896
|
for (let dy = -r; dy <= r; dy++) {
|
|
652
897
|
for (let dx = -r; dx <= r; dx++) {
|
|
@@ -665,7 +910,7 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
|
|
|
665
910
|
// Orbiting glow particles when working (bioluminescent)
|
|
666
911
|
if (state === 'working') {
|
|
667
912
|
const orbitPhase = animFrame * 0.2;
|
|
668
|
-
const dist = 5 + Math.sin(animFrame * 0.15) * 2;
|
|
913
|
+
const dist = (5 + Math.sin(animFrame * 0.15) * 2) * cellSz;
|
|
669
914
|
for (let i = 0; i < 4; i++) {
|
|
670
915
|
const angle = orbitPhase + (i * Math.PI * 2 / 4);
|
|
671
916
|
const sx = scx + Math.cos(angle) * dist;
|
|
@@ -676,48 +921,103 @@ export function drawJellyfish(buf, worldX, worldY, state, animFrame, cam, palett
|
|
|
676
921
|
}
|
|
677
922
|
// ===== OpenCode — nested-square logo (simulator SSOT) =====
|
|
678
923
|
export function drawOpenCode(buf, worldX, worldY, state, animFrame, camera, palette) {
|
|
679
|
-
const
|
|
680
|
-
const scy =
|
|
924
|
+
const w = Math.sqrt(buf.length / 3);
|
|
925
|
+
const [scx, scy] = worldToScreen(worldX, worldY, camera);
|
|
926
|
+
// Canonical opencode mark: a single-color vertical rectangular RING (16:20) with a
|
|
927
|
+
// HOLLOW center — drawn procedurally so it scales cleanly to any panel (64px, 32px,
|
|
928
|
+
// iDotMatrix) instead of grid sprites that overflowed 32px or drew a filled shadow.
|
|
681
929
|
const worldW = 10 / 64;
|
|
682
|
-
const
|
|
683
|
-
const
|
|
930
|
+
const unit = Math.max(1, Math.round((worldW * camera.zoom * w) / OC_GRID_MD_COLS));
|
|
931
|
+
const outerW = Math.max(5, 6 * unit);
|
|
932
|
+
const outerH = Math.max(6, Math.round(outerW * 1.25)); // slightly tall (16:20)
|
|
933
|
+
const thick = Math.max(1, Math.round(outerW * 0.28)); // frame thickness ~ brand 4/16
|
|
684
934
|
const breathPx = state === 'working'
|
|
685
935
|
? Math.round(Math.sin(animFrame * 0.3) * 1.5)
|
|
686
936
|
: state === 'idle' ? Math.round(Math.sin(animFrame * 0.08) * 0.7) : 0;
|
|
687
|
-
const
|
|
688
|
-
const grid = useLod ? OPENCODE_LOD : OPENCODE_GRID;
|
|
689
|
-
const gc = useLod ? OC_LOD_COLS : OC_COLS;
|
|
690
|
-
const gr = useLod ? OC_LOD_ROWS : OC_ROWS;
|
|
691
|
-
const baseX = scx - Math.round((gc * pixW) / 2);
|
|
692
|
-
const baseY = scy + breathPx - Math.round((gr * pixH) / 2);
|
|
693
|
-
const outerColor = state === 'working'
|
|
937
|
+
const color = state === 'working'
|
|
694
938
|
? lerpColor(palette.outer, palette.pulse, 0.5 + Math.sin(animFrame * 0.2) * 0.5)
|
|
695
939
|
: state === 'sleeping' ? palette.sleeping : palette.outer;
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
for (let
|
|
699
|
-
for (let
|
|
700
|
-
const
|
|
701
|
-
if (
|
|
702
|
-
continue;
|
|
703
|
-
|
|
704
|
-
for (let dy = 0; dy < pixH; dy++) {
|
|
705
|
-
for (let dx = 0; dx < pixW; dx++) {
|
|
706
|
-
setPixel(buf, baseX + c * pixW + dx, baseY + r * pixH + dy, color);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
940
|
+
const x0 = scx - Math.round(outerW / 2);
|
|
941
|
+
const y0 = scy + breathPx - Math.round(outerH / 2);
|
|
942
|
+
for (let dy = 0; dy < outerH; dy++) {
|
|
943
|
+
for (let dx = 0; dx < outerW; dx++) {
|
|
944
|
+
const onFrame = dx < thick || dx >= outerW - thick || dy < thick || dy >= outerH - thick;
|
|
945
|
+
if (!onFrame)
|
|
946
|
+
continue; // hollow center
|
|
947
|
+
setPixel(buf, x0 + dx, y0 + dy, color);
|
|
709
948
|
}
|
|
710
949
|
}
|
|
711
950
|
if (state === 'asking') {
|
|
712
|
-
// Amber three-dot asking indicator
|
|
713
|
-
const dotCx = scx + Math.round(
|
|
714
|
-
const dotCy =
|
|
951
|
+
// Amber three-dot asking indicator above the mark
|
|
952
|
+
const dotCx = scx + Math.round(outerW / 2) + 3;
|
|
953
|
+
const dotCy = y0 - 2 + Math.round(Math.sin(animFrame * 0.26) * 1.4);
|
|
715
954
|
for (let i = -1; i <= 1; i++) {
|
|
716
955
|
blendPixel(buf, dotCx + i * 3, dotCy, COLORS.stateAwaiting, 0.75);
|
|
717
956
|
blendPixel(buf, dotCx + i * 3 + 1, dotCy, COLORS.stateAwaiting, 0.45);
|
|
718
957
|
}
|
|
719
958
|
}
|
|
720
959
|
}
|
|
960
|
+
function antigravityCellColor(ch, palette) {
|
|
961
|
+
switch (ch) {
|
|
962
|
+
case 'L': return palette.lime;
|
|
963
|
+
case 'T': return palette.teal;
|
|
964
|
+
case 'Q': return palette.cyan;
|
|
965
|
+
case 'Y': return palette.yellow;
|
|
966
|
+
case 'O': return palette.orange;
|
|
967
|
+
case 'R': return palette.red;
|
|
968
|
+
case 'P': return palette.pink;
|
|
969
|
+
case 'V': return palette.violet;
|
|
970
|
+
case 'U': return palette.blue;
|
|
971
|
+
case 'N': return palette.sky;
|
|
972
|
+
case 'K': return palette.cutout;
|
|
973
|
+
default: return null;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
function drawQuestionMark(buf, x, y, color) {
|
|
977
|
+
setPixel(buf, x, y, color);
|
|
978
|
+
setPixel(buf, x + 1, y, color);
|
|
979
|
+
setPixel(buf, x + 1, y + 1, color);
|
|
980
|
+
setPixel(buf, x, y + 2, color);
|
|
981
|
+
setPixel(buf, x, y + 4, color);
|
|
982
|
+
}
|
|
983
|
+
export function drawAntigravity(buf, worldX, worldY, state, animFrame, camera, palette) {
|
|
984
|
+
if (!isVisible(worldX, worldY, camera, 0.15))
|
|
985
|
+
return;
|
|
986
|
+
const [scx, scy] = worldToScreen(worldX, worldY, camera);
|
|
987
|
+
const w = Math.sqrt(buf.length / 3);
|
|
988
|
+
const cellSz = creatureCellSize(camera.zoom, w, AG_COLS);
|
|
989
|
+
const spriteW = AG_COLS * cellSz;
|
|
990
|
+
const spriteH = AG_ROWS * cellSz;
|
|
991
|
+
const breathPx = state === 'working'
|
|
992
|
+
? Math.round(Math.sin(animFrame * 0.28) * cellSz)
|
|
993
|
+
: state === 'idle' ? Math.round(Math.sin(animFrame * 0.08) * 0.5) : 0;
|
|
994
|
+
const cameraNudgeX = state === 'working' && ((animFrame >> 3) & 1) ? cellSz : 0;
|
|
995
|
+
const cameraNudgeY = state !== 'idle' && ((animFrame >> 2) & 1) ? -cellSz : 0;
|
|
996
|
+
const baseX = Math.round(scx - spriteW / 2) + cameraNudgeX;
|
|
997
|
+
const baseY = Math.round(scy - spriteH / 2) + breathPx + cameraNudgeY;
|
|
998
|
+
const trackedPixels = new Set();
|
|
999
|
+
for (let row = 0; row < AG_ROWS; row++) {
|
|
1000
|
+
const line = ANTIGRAVITY_GRID[row];
|
|
1001
|
+
for (let col = 0; col < AG_COLS; col++) {
|
|
1002
|
+
const color = antigravityCellColor(line[col], palette);
|
|
1003
|
+
if (!color)
|
|
1004
|
+
continue;
|
|
1005
|
+
fillCellTracked(buf, baseX + col * cellSz, baseY + row * cellSz, cellSz, cellSz, color, trackedPixels);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
drawCreatureOutline(buf, trackedPixels, palette.violet, 0.45);
|
|
1009
|
+
if (state === 'asking') {
|
|
1010
|
+
drawQuestionMark(buf, Math.round(scx + spriteW * 0.48), Math.round(baseY + spriteH * 0.18), COLORS.stateAwaiting);
|
|
1011
|
+
}
|
|
1012
|
+
if (state === 'working') {
|
|
1013
|
+
const sparkle = lerpColor(palette.yellow, COLORS.white, 0.35);
|
|
1014
|
+
const dist = Math.max(2, cellSz * 3);
|
|
1015
|
+
for (let i = 0; i < 4; i++) {
|
|
1016
|
+
const t = animFrame * 0.22 + i * Math.PI / 2;
|
|
1017
|
+
setPixel(buf, Math.round(scx + Math.cos(t) * dist), Math.round(baseY - 1 + Math.sin(t) * dist * 0.35), sparkle);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
721
1021
|
/**
|
|
722
1022
|
* Draw crayfish scaled by camera zoom.
|
|
723
1023
|
* @param worldX Crayfish center in normalized world X (0~1)
|
|
@@ -727,14 +1027,16 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
|
|
|
727
1027
|
if (!isVisible(worldX, worldY, cam, 0.15))
|
|
728
1028
|
return;
|
|
729
1029
|
const [scx, scy] = worldToScreen(worldX, worldY, cam);
|
|
1030
|
+
const w = Math.sqrt(buf.length / 3);
|
|
730
1031
|
// LOD selection — matches octopus threshold
|
|
731
|
-
const
|
|
732
|
-
const grid =
|
|
733
|
-
const cols =
|
|
734
|
-
const rows =
|
|
735
|
-
//
|
|
736
|
-
|
|
737
|
-
const
|
|
1032
|
+
const select = selectGrid(cam.zoom, w, CRAYFISH_GRID_HD, CF_HD_COLS, CF_HD_ROWS, CRAYFISH_GRID_MD, CF_GRID_MD_COLS, CF_GRID_MD_ROWS, CRAYFISH_LOD, CF_LOD_COLS, CF_LOD_ROWS);
|
|
1033
|
+
const grid = select.grid;
|
|
1034
|
+
const cols = select.cols;
|
|
1035
|
+
const rows = select.rows;
|
|
1036
|
+
// Resolution-aware square cells (see creatureCellSize). Square (cellH = cellW) — the old
|
|
1037
|
+
// cellH = cellW*1.5 stretched the round OpenClaw body into a tall pointed shape.
|
|
1038
|
+
const cellW = creatureCellSize(cam.zoom, w, cols);
|
|
1039
|
+
const cellH = cellW;
|
|
738
1040
|
const spriteW = cols * cellW;
|
|
739
1041
|
const spriteH = rows * cellH;
|
|
740
1042
|
const baseX = scx - spriteW / 2;
|
|
@@ -752,7 +1054,7 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
|
|
|
752
1054
|
// 1. Glow halo (before creature — stronger for LOD)
|
|
753
1055
|
const glowRx = spriteW / 2 + 2;
|
|
754
1056
|
const glowRy = spriteH / 2 + 2;
|
|
755
|
-
drawCreatureGlow(buf, scx, scy + breathPx, glowRx, glowRy, COLORS.crayfishGlow, 0.12,
|
|
1057
|
+
drawCreatureGlow(buf, scx, scy + breathPx, glowRx, glowRy, COLORS.crayfishGlow, 0.12, cols === 8);
|
|
756
1058
|
// 2. Draw cells with pixel tracking
|
|
757
1059
|
const trackedPixels = new Set();
|
|
758
1060
|
for (let row = 0; row < rows; row++) {
|
|
@@ -769,7 +1071,7 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
|
|
|
769
1071
|
const spd = routing ? 0.35 : 0.15;
|
|
770
1072
|
const wiggle = Math.sin(animFrame * spd + col * 3) * cellW * 1.5;
|
|
771
1073
|
// Random-ish twitch: sharp spike every ~30 frames
|
|
772
|
-
const twitch = ((animFrame + col * 17) % 60) < 4 ? cellW * 2 * (col <
|
|
1074
|
+
const twitch = ((animFrame + col * 17) % 60) < 4 ? cellW * 2 * (col < cols / 2 ? -1 : 1) : 0;
|
|
773
1075
|
dx = ensureMinAmplitude(wiggle + twitch, 1);
|
|
774
1076
|
}
|
|
775
1077
|
// Claw animation — routing: slower, wider clap; idle: gentle sway
|
|
@@ -800,11 +1102,11 @@ export function drawCrayfish(buf, worldX, worldY, routing, animFrame, cam, sick
|
|
|
800
1102
|
}
|
|
801
1103
|
// 3. Colored outline — moderate alpha for LOD
|
|
802
1104
|
const bodyColor = routing ? COLORS.crayfishRouting : COLORS.crayfishBody;
|
|
803
|
-
const outlineAlpha =
|
|
1105
|
+
const outlineAlpha = cols === 8 ? 0.6 : 0.8;
|
|
804
1106
|
drawCreatureOutline(buf, trackedPixels, bodyColor, outlineAlpha);
|
|
805
1107
|
// Fixed 3×3 eyes (teal center + black ring) — drawn at grid eye positions
|
|
806
|
-
const eyeRow =
|
|
807
|
-
const eyeCols =
|
|
1108
|
+
const eyeRow = cols === 24 ? 9 : (cols === 8 ? 2 : 3);
|
|
1109
|
+
const eyeCols = cols === 24 ? [8, 14] : (cols === 8 ? [2, 5] : [4, 7]);
|
|
808
1110
|
const sickEyeCenter = [0x44, 0x66, 0x60]; // dim teal for sick
|
|
809
1111
|
const eyeCenter = sick ? sickEyeCenter : COLORS.crayfishEye;
|
|
810
1112
|
const eyeRing = sick ? [0x44, 0x33, 0x33] : COLORS.crayfishEyeRing;
|
|
@@ -851,7 +1153,8 @@ export function drawTetra(buf, worldX, worldY, heading, cam) {
|
|
|
851
1153
|
if (!isVisible(worldX, worldY, cam, 0.08))
|
|
852
1154
|
return;
|
|
853
1155
|
const [sx, sy] = worldToScreen(worldX, worldY, cam);
|
|
854
|
-
const
|
|
1156
|
+
const w = Math.sqrt(buf.length / 3);
|
|
1157
|
+
const px = cam.zoom * (w / 64); // resolution-aware pixel scale (half-size on 32px iDotMatrix)
|
|
855
1158
|
// Body (2px-equivalent)
|
|
856
1159
|
const bw = Math.max(1, Math.round(px * 2));
|
|
857
1160
|
const bh = Math.max(1, Math.round(px));
|