@inneranimalmedia/agentsam-sdk 2.6.5 → 2.6.7
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/AGENTSAM.md +29 -0
- package/apps/README.md +5 -0
- package/apps/cad-creator/README.md +4 -4
- package/apps/cad-creator/shared/cad/package.json +1 -1
- package/apps/client-cms-editor/AGENTS.md +12 -7
- package/apps/client-cms-editor/README.md +76 -48
- package/apps/client-cms-editor/agentsam.app.json +26 -9
- package/apps/client-cms-editor/backend/package.json +20 -3
- package/apps/client-cms-editor/backend/src/api/client.ts +134 -101
- package/apps/client-cms-editor/backend/src/index.ts +1 -1
- package/apps/client-cms-editor/backend/src/model.ts +11 -115
- package/apps/client-cms-editor/backend/src/preview/bridge.ts +1 -1
- package/apps/client-cms-editor/backend/tsconfig.json +6 -1
- package/apps/client-cms-editor/bin/agentsam-cms.js +262 -0
- package/apps/client-cms-editor/bin/agentsam-cms.mjs +2 -228
- package/apps/client-cms-editor/frontend/package.json +16 -7
- package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +9 -41
- package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +19 -174
- package/apps/client-cms-editor/frontend/src/adapters/heuristic.ts +29 -0
- package/apps/client-cms-editor/frontend/src/adapters/http.ts +628 -0
- package/apps/client-cms-editor/frontend/src/adapters/memory.ts +510 -0
- package/apps/client-cms-editor/frontend/src/editor/CmsEditor.tsx +213 -0
- package/apps/client-cms-editor/frontend/src/editor/CmsEditorProvider.tsx +20 -0
- package/apps/client-cms-editor/frontend/src/editor/canvas/PreviewCanvas.tsx +53 -0
- package/apps/client-cms-editor/frontend/src/{CmsEditor.tsx → editor/legacy/CmsEditorPrototype.tsx} +52 -23
- package/apps/client-cms-editor/frontend/src/editor/useCmsEditorController.ts +218 -0
- package/apps/client-cms-editor/frontend/src/index.ts +19 -5
- package/apps/client-cms-editor/frontend/src/lib/agent-host.ts +24 -0
- package/apps/client-cms-editor/frontend/src/main.tsx +82 -8
- package/apps/client-cms-editor/frontend/src/mount.tsx +92 -30
- package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +1 -1
- package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +8 -8
- package/apps/client-cms-editor/frontend/src/styles/studio.css +55 -3
- package/apps/client-cms-editor/frontend/tsconfig.json +12 -1
- package/apps/client-cms-editor/frontend/vite.config.ts +12 -0
- package/apps/client-cms-editor/package-lock.json +973 -134
- package/apps/client-cms-editor/package.json +118 -6
- package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +201 -0
- package/apps/client-cms-editor/reference/harvest/PROVENANCE_HISTORY.txt +10 -0
- package/apps/client-cms-editor/reference/harvest/README.md +8 -0
- package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +135 -0
- package/apps/client-cms-editor/reference/harvest/plans/CHATGPT-IDENTITY-HARVEST.md +83 -0
- package/apps/client-cms-editor/reference/harvest/plans/CMS-EDITOR-HARVEST.md +166 -0
- package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +196 -0
- package/apps/client-cms-editor/reference/harvest/plans/INNERANIMALS-CONTENT-001.md +137 -0
- package/apps/client-cms-editor/reference/harvest/plans/NO-IMPLICIT-SIDE-EFFECTS.md +87 -0
- package/apps/client-cms-editor/reference/harvest/plans/STOREFRONT-SURFACE-MAP.md +140 -0
- package/apps/client-cms-editor/reference/harvest/plans/THEME-INNERANIMALS-HARVEST.md +137 -0
- package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +63 -0
- package/apps/client-cms-editor/reference/harvest/plans/harvest-manifest.json +97 -0
- package/apps/client-cms-editor/reference/harvest/plans/local-cms-harvest-schema.sql +328 -0
- package/apps/client-cms-editor/shared/cms/package.json +17 -7
- package/apps/client-cms-editor/shared/cms/src/adapter.ts +204 -0
- package/apps/client-cms-editor/shared/cms/src/agent-context.ts +7 -5
- package/apps/client-cms-editor/shared/cms/src/editor-types.ts +32 -0
- package/apps/client-cms-editor/shared/cms/src/host.ts +82 -0
- package/apps/client-cms-editor/shared/cms/src/index.ts +4 -0
- package/apps/client-cms-editor/shared/cms/src/map.ts +115 -0
- package/apps/client-cms-editor/shared/cms/src/starter-pack.ts +158 -0
- package/contracts/source-types.v1.json +353 -0
- package/docs/CMS_CONTEXTUAL_COMPOSER.md +1 -1
- package/docs/PRODUCT_LIFECYCLE.md +566 -0
- package/docs/THEME_REFINERY.md +2 -2
- package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +40 -20
- package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
- package/docs/plans/LOCAL_STUDIO_DESKTOP_RELEASE_2026-09-29.md +150 -0
- package/migrations/d1/0017_identity_native_oauth.sql +21 -0
- package/native/agentsam-machine/Cargo.lock +426 -0
- package/native/agentsam-machine/Cargo.toml +9 -0
- package/native/agentsam-machine/cli/Cargo.toml +14 -0
- package/native/agentsam-machine/cli/src/main.rs +77 -0
- package/native/agentsam-machine/core/Cargo.toml +15 -0
- package/native/agentsam-machine/core/src/assets.rs +790 -0
- package/native/agentsam-machine/core/src/bound.rs +298 -0
- package/native/agentsam-machine/core/src/enrich.rs +706 -0
- package/native/agentsam-machine/core/src/frontend.rs +442 -0
- package/native/agentsam-machine/core/src/lib.rs +1086 -0
- package/native/agentsam-machine/core/src/liquid.rs +528 -0
- package/native/agentsam-machine/core/src/source_types.rs +78 -0
- package/package.json +51 -21
- package/packages/agentsam-brand/README.md +1 -1
- package/packages/agentsam-brand/package.json +4 -4
- package/packages/agentsam-brand/src/index.js +1 -1
- package/packages/agentsam-contracts/package.json +13 -3
- package/packages/agentsam-contracts/src/tools.ts +22 -0
- package/packages/agentsam-errors/package.json +13 -3
- package/packages/agentsam-knowledge/package.json +18 -4
- package/packages/agentsam-knowledge/src/autorag/index.js +10 -3
- package/packages/agentsam-knowledge/src/backends/index.js +33 -1
- package/packages/agentsam-knowledge/test/autorag.test.mjs +60 -1
- package/packages/agentsam-nav/package.json +24 -6
- package/packages/agentsam-queue-control/README.md +1 -1
- package/packages/agentsam-queue-control/package.json +7 -2
- package/packages/agentsam-rapid-rust/src/main.rs +1 -1
- package/packages/agentsam-repository/package.json +13 -3
- package/packages/agentsam-repository/src/source-types.js +8 -0
- package/packages/agentsam-vault/package.json +14 -4
- package/packages/agentsam-workbench/package.json +15 -4
- package/packages/agentsam-workbench/src/agent/ToolPermissionRequest.tsx +94 -0
- package/packages/agentsam-workbench/src/agent/index.ts +2 -0
- package/packages/agentsam-workbench/src/agent/tool-permission.css +134 -0
- package/packages/connectors/cloudflare/package.json +13 -3
- package/packages/connectors/cloudflare/src/api-client.js +3 -2
- package/packages/connectors/cloudflare/src/families/vectorize.js +127 -0
- package/packages/connectors/cloudflare/src/index.js +1 -0
- package/packages/heuristic-theme/README.md +2 -2
- package/packages/heuristic-theme/package.json +8 -3
- package/packages/heuristic-theme/src/registry.js +2 -2
- package/packages/heuristic-theme/src/theme.js +1 -1
- package/packages/heuristic-theme/test/theme.test.mjs +1 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +27 -7
- package/packages/identity/README.md +2 -2
- package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +18 -15
- package/packages/identity/migrations/apply-portable.js +109 -0
- package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +1 -1
- package/packages/identity/migrations/sqlite/005_identity_company_native.sql +52 -0
- package/packages/identity/package.json +27 -6
- package/packages/identity/schema/agentsam.identity/manifest.json +101 -0
- package/packages/identity/scripts/local-identity-bridge.mjs +13 -7
- package/packages/identity/scripts/preview-auth-portal.mjs +6 -6
- package/packages/identity/src/adapters/cloudflare-d1/index.js +8 -401
- package/packages/identity/src/adapters/iam-compat/index.js +540 -0
- package/packages/identity/src/adapters/portable-d1/index.js +18 -0
- package/packages/identity/src/adapters/portable-store.js +479 -0
- package/packages/identity/src/adapters/sqlite/index.js +13 -312
- package/packages/identity/src/contracts/auth-config.js +0 -1
- package/packages/identity/src/contracts/identity-store.js +56 -5
- package/packages/identity/src/contracts/provider.js +1 -0
- package/packages/identity/src/contracts/sign-in-options.js +104 -0
- package/packages/identity/src/core/constants.js +1 -1
- package/packages/identity/src/core/session-policy.js +31 -0
- package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -6
- package/packages/identity/src/frontend/auth-portal/pages/reset.html +5 -5
- package/packages/identity/src/frontend/auth-portal/pages/signup.html +5 -5
- package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
- package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +6 -37
- package/packages/identity/src/index.js +28 -1
- package/packages/identity/src/migrations/README.md +23 -23
- package/packages/identity/src/oauth/README.md +1 -1
- package/packages/identity/src/oauth/credentials.js +10 -11
- package/packages/identity/src/oauth/google-desktop-exchange.js +195 -27
- package/packages/identity/src/oauth/iam-platform.js +21 -9
- package/packages/identity/src/oauth/native-handoff.js +136 -0
- package/packages/identity/src/providers/chatgpt/headers.js +65 -0
- package/packages/identity/src/providers/chatgpt/index.js +86 -0
- package/packages/identity/src/providers/chatgpt/mapper.js +43 -0
- package/packages/identity/src/providers/chatgpt/paths.js +45 -0
- package/packages/identity/src/providers/github/oauth.js +1 -2
- package/packages/identity/src/providers/github/profile.js +16 -2
- package/packages/identity/src/providers/index.js +11 -3
- package/packages/identity/src/server/identity-service.js +2 -1
- package/packages/identity/src/server/worker-router.js +71 -18
- package/packages/identity/tests/chatgpt-provider.test.mjs +75 -0
- package/packages/identity/tests/google-desktop-identity-login.test.mjs +210 -0
- package/packages/identity/tests/identity-init-scaffold.test.mjs +15 -4
- package/packages/identity/tests/lane1-identity-store-parity.test.mjs +527 -0
- package/packages/identity/tests/lane1-packed-consumer.test.mjs +175 -0
- package/packages/identity/tests/native-oauth-handoff.test.mjs +292 -0
- package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
- package/packages/identity/tests/preview-auth-portal.test.mjs +4 -2
- package/packages/identity/tests/session-routes-policy.test.mjs +13 -1
- package/packages/identity/tests/worker-runtime-boundary.test.mjs +11 -0
- package/packages/providers/completeful/package.json +7 -2
- package/packages/theme-church-site/package.json +17 -6
- package/packages/theme-church-site/src/harvest.json +4 -2
- package/packages/theme-church-site/src/index.js +7 -3
- package/packages/theme-church-site/test/theme.test.mjs +4 -2
- package/packages/theme-companions-site/package.json +17 -6
- package/packages/theme-companions-site/src/harvest.json +4 -2
- package/packages/theme-companions-site/src/index.js +7 -3
- package/packages/theme-companions-site/test/theme.test.mjs +4 -2
- package/packages/theme-floors-site/package.json +17 -6
- package/packages/theme-floors-site/src/harvest.json +4 -2
- package/packages/theme-floors-site/src/index.js +7 -3
- package/packages/theme-floors-site/test/theme.test.mjs +4 -2
- package/packages/theme-fuelnfree-site/package.json +17 -6
- package/packages/theme-fuelnfree-site/src/harvest.json +4 -2
- package/packages/theme-fuelnfree-site/src/index.js +7 -3
- package/packages/theme-fuelnfree-site/test/theme.test.mjs +4 -2
- package/packages/theme-handyman-site/package.json +17 -6
- package/packages/theme-handyman-site/src/harvest.json +4 -2
- package/packages/theme-handyman-site/src/index.js +7 -3
- package/packages/theme-handyman-site/test/theme.test.mjs +4 -2
- package/packages/theme-insurance-site/package.json +17 -6
- package/packages/theme-insurance-site/src/harvest.json +4 -2
- package/packages/theme-insurance-site/src/index.js +7 -3
- package/packages/theme-insurance-site/test/theme.test.mjs +4 -2
- package/packages/theme-scenes/README.md +1 -1
- package/packages/theme-scenes/package.json +4 -3
- package/packages/theme-scenes/src/registry.js +187 -0
- package/packages/theme-shinshu-site/package.json +17 -6
- package/packages/theme-shinshu-site/src/harvest.json +4 -2
- package/packages/theme-shinshu-site/src/index.js +7 -3
- package/packages/theme-shinshu-site/test/theme.test.mjs +4 -2
- package/packages/work-graph/package.json +14 -4
- package/protocol/FILEMETA_V1.md +2 -2
- package/protocol/capabilities/manifest.json +1 -1
- package/protocol/database/agentsam.schema-pack.v1.schema.json +72 -0
- package/protocol/presets/catalog.json +2 -2
- package/protocol/theme-refinery/lineage-registry.json +105 -0
- package/protocol/theme-refinery/product-metadata.schema.json +21 -1
- package/skills/grok-auth/SKILL.md +110 -0
- package/skills/grok-auth/agentsam.skill.json +25 -0
- package/skills/grok-auth/references/grok-identity.md +64 -0
- package/skills/grok-auth/references/per-user-data.md +49 -0
- package/skills/grok-auth/references/prewired-and-env.md +54 -0
- package/skills/grok-auth/references/session-ui.md +133 -0
- package/skills/grok-auth/references/sign-in-methods.md +55 -0
- package/skills/grok-auth/references/wiring.md +71 -0
- package/skills/grok-building-games/SKILL.md +213 -0
- package/skills/grok-building-games/agentsam.skill.json +25 -0
- package/skills/grok-building-games/references/3d-libs.md +64 -0
- package/skills/grok-building-games/references/ai-pathfinding.md +122 -0
- package/skills/grok-building-games/references/audio.md +129 -0
- package/skills/grok-building-games/references/babylon.md +202 -0
- package/skills/grok-building-games/references/collision-physics.md +122 -0
- package/skills/grok-building-games/references/ecs-architecture.md +111 -0
- package/skills/grok-building-games/references/game-feel-juice.md +110 -0
- package/skills/grok-building-games/references/genres/board-card-chess.md +89 -0
- package/skills/grok-building-games/references/genres/endless-runner.md +83 -0
- package/skills/grok-building-games/references/genres/fps.md +105 -0
- package/skills/grok-building-games/references/genres/platformer-2d.md +97 -0
- package/skills/grok-building-games/references/genres/puzzle-match3-tetris.md +131 -0
- package/skills/grok-building-games/references/genres/racing-kart.md +106 -0
- package/skills/grok-building-games/references/genres/topdown-twin-stick.md +97 -0
- package/skills/grok-building-games/references/genres/tower-defense.md +84 -0
- package/skills/grok-building-games/references/genres/voxel-minecraft.md +89 -0
- package/skills/grok-building-games/references/input.md +139 -0
- package/skills/grok-building-games/references/phaser.md +277 -0
- package/skills/grok-building-games/references/procedural-generation.md +141 -0
- package/skills/grok-building-games/references/save-persistence.md +108 -0
- package/skills/grok-building-games/references/threejs-foundational.md +148 -0
- package/skills/grok-controls/SKILL.md +274 -0
- package/skills/grok-controls/agentsam.skill.json +25 -0
- package/skills/grok-design-ui/SKILL.md +122 -0
- package/skills/grok-design-ui/agentsam.skill.json +25 -0
- package/skills/grok-design-ui/references/animations.md +379 -0
- package/skills/grok-design-ui/references/performance.md +88 -0
- package/skills/grok-design-ui/references/refined-ui.md +384 -0
- package/skills/grok-design-ui/references/surfaces.md +247 -0
- package/skills/grok-design-ui/references/typography.md +123 -0
- package/skills/grok-game-animation-frames/SKILL.md +84 -0
- package/skills/grok-game-animation-frames/agentsam.skill.json +25 -0
- package/skills/grok-game-asset-core/SKILL.md +69 -0
- package/skills/grok-game-asset-core/agentsam.skill.json +25 -0
- package/skills/grok-game-character-consistency/SKILL.md +69 -0
- package/skills/grok-game-character-consistency/agentsam.skill.json +25 -0
- package/skills/grok-game-tilesets/SKILL.md +65 -0
- package/skills/grok-game-tilesets/agentsam.skill.json +25 -0
- package/skills/grok-game-ui-icons/SKILL.md +56 -0
- package/skills/grok-game-ui-icons/agentsam.skill.json +25 -0
- package/skills/grok-generate2dmap/LICENSE +21 -0
- package/skills/grok-generate2dmap/SKILL.md +120 -0
- package/skills/grok-generate2dmap/SOURCE.md +6 -0
- package/skills/grok-generate2dmap/agentsam.skill.json +25 -0
- package/skills/grok-generate2dmap/references/deliverables.md +101 -0
- package/skills/grok-generate2dmap/references/layered-map-contract.md +209 -0
- package/skills/grok-generate2dmap/references/map-strategies.md +341 -0
- package/skills/grok-generate2dmap/references/object-production-gate.md +24 -0
- package/skills/grok-generate2dmap/references/pipeline.md +149 -0
- package/skills/grok-generate2dmap/references/prop-pack-contract.md +174 -0
- package/skills/grok-generate2dmap/references/side-scroll-stages.md +29 -0
- package/skills/grok-generate2dmap/scripts/compose_layered_preview.py +157 -0
- package/skills/grok-generate2dmap/scripts/extract_prop_pack.py +369 -0
- package/skills/grok-generate2dsprite/LICENSE +21 -0
- package/skills/grok-generate2dsprite/SKILL.md +313 -0
- package/skills/grok-generate2dsprite/SOURCE.md +6 -0
- package/skills/grok-generate2dsprite/agentsam.skill.json +25 -0
- package/skills/grok-generate2dsprite/references/modes.md +124 -0
- package/skills/grok-generate2dsprite/references/prompt-rules.md +315 -0
- package/skills/grok-generate2dsprite/scripts/generate2dsprite.py +1031 -0
- package/skills/grok-generate2dsprite/scripts/make_layout_guide.py +109 -0
- package/skills/grok-imagine/SKILL.md +201 -0
- package/skills/grok-imagine/agentsam.skill.json +25 -0
- package/skills/grok-multiplayer-p2p/SKILL.md +111 -0
- package/skills/grok-multiplayer-p2p/agentsam.skill.json +25 -0
- package/skills/grok-multiplayer-p2p/references/react-binding.md +144 -0
- package/skills/grok-multiplayer-p2p/references/signaling-relay.md +269 -0
- package/skills/grok-neon/SKILL.md +157 -0
- package/skills/grok-neon/agentsam.skill.json +25 -0
- package/skills/grok-og/SKILL.md +102 -0
- package/skills/grok-og/agentsam.skill.json +25 -0
- package/skills/grok-og/references/brand-pass.md +53 -0
- package/skills/grok-og/references/custom-card.md +107 -0
- package/skills/grok-og/references/favicon-and-icons.md +48 -0
- package/skills/grok-og/references/og-type-contract.md +18 -0
- package/skills/grok-og/references/placeholder-card.md +21 -0
- package/skills/grok-og/references/x-banner.md +41 -0
- package/skills/grok-threejs/SKILL.md +67 -0
- package/skills/grok-threejs/agentsam.skill.json +25 -0
- package/skills/grok-threejs/references/llms-full.txt +2780 -0
- package/skills/grok-video2dsprite/LICENSE +21 -0
- package/skills/grok-video2dsprite/SKILL.md +236 -0
- package/skills/grok-video2dsprite/SOURCE.md +6 -0
- package/skills/grok-video2dsprite/agentsam.skill.json +25 -0
- package/skills/grok-video2dsprite/references/pipeline.md +90 -0
- package/skills/grok-video2dsprite/references/prompt-rules.md +63 -0
- package/skills/grok-video2dsprite/scripts/video2dsprite.py +490 -0
- package/skills/grok-xai-api/SKILL.md +152 -0
- package/skills/grok-xai-api/agentsam.skill.json +25 -0
- package/src/cli/command-catalog.js +46 -4
- package/src/cli/dispatch.js +1 -0
- package/src/cli.js +10 -0
- package/src/cloudflare/runtime-status.js +2 -1
- package/src/commands/autorag.js +67 -13
- package/src/commands/cheat-sheet.js +29 -4
- package/src/commands/codebaseindex.js +45 -2
- package/src/commands/knowledge.js +59 -2
- package/src/commands/machine-binary.js +103 -0
- package/src/commands/machine.js +137 -0
- package/src/commands/security.js +67 -4
- package/src/commands/setup.js +33 -16
- package/src/commands/status.js +8 -1
- package/src/features/resolve.js +1 -0
- package/src/knowledge/engine.js +21 -2
- package/src/knowledge/runtime-discovery.js +127 -0
- package/src/lib/identity-scaffold.js +53 -13
- package/src/lib/setup/recipes.js +1 -1
- package/src/models/discovery.js +40 -22
- package/src/models/inventory-core.d.ts +105 -0
- package/src/models/inventory-core.js +54 -6
- package/src/security/logs.js +3 -0
- package/src/ui/cli/help.js +41 -0
- package/test/apps-scaffold-contract.test.mjs +3 -2
- package/test/cli/api-key-env-whoami.test.mjs +2 -2
- package/test/cli/catalog-authority.test.mjs +64 -0
- package/test/cli/machine-command.test.mjs +34 -0
- package/test/fixtures/project-authority/customer-app/.agentsam/app.json +5 -0
- package/test/fixtures/project-authority/customer-app/apps/my-product/index.ts +1 -0
- package/test/fixtures/project-authority/customer-app/wrangler.toml +17 -0
- package/test/integration/autorag-project-authority.integration.test.mjs +51 -0
- package/test/integration/autorag-runtime-discovery.test.mjs +107 -0
- package/test/integration/cms-app-scaffold.test.mjs +37 -31
- package/test/integration/cms-site-tenancy-contract.test.mjs +28 -17
- package/test/integration/codebaseindex-ingest.test.mjs +41 -0
- package/test/integration/database-hosted-honesty.test.mjs +49 -0
- package/test/integration/desktop-keychain-boundary.test.mjs +45 -0
- package/test/integration/hosted-work-host-authority.test.mjs +47 -0
- package/test/integration/inventory-eligibility.test.mjs +59 -0
- package/test/integration/local-provider-bridge.test.mjs +65 -0
- package/test/integration/local-studio-settings-production.test.mjs +40 -0
- package/test/integration/lockfile-sync.test.mjs +26 -0
- package/test/integration/provider-env-cli.test.mjs +13 -3
- package/test/integration/runtime-status.integration.test.mjs +16 -1
- package/test/integration/theme-package-registry.test.mjs +31 -0
- package/test/repository-snapshot-view.test.mjs +2 -2
- package/test/security.test.mjs +14 -0
- package/test/terminal-machine-identity.test.mjs +2 -2
- package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +0 -145
- package/packages/agentsam-nav/dist/Nav.d.ts +0 -43
- package/packages/agentsam-nav/dist/NavCollapsible.d.ts +0 -10
- package/packages/agentsam-nav/dist/NavContent.d.ts +0 -2
- package/packages/agentsam-nav/dist/NavFooter.d.ts +0 -2
- package/packages/agentsam-nav/dist/NavGroup.d.ts +0 -3
- package/packages/agentsam-nav/dist/NavHeader.d.ts +0 -2
- package/packages/agentsam-nav/dist/NavMenu.d.ts +0 -22
- package/packages/agentsam-nav/dist/NavProvider.d.ts +0 -35
- package/packages/agentsam-nav/dist/NavTrigger.d.ts +0 -2
- package/packages/agentsam-nav/dist/Surfaces.d.ts +0 -41
- package/packages/agentsam-nav/dist/browser.d.ts +0 -11
- package/packages/agentsam-nav/dist/browser.global.js +0 -132
- package/packages/agentsam-nav/dist/browser.js +0 -613
- package/packages/agentsam-nav/dist/contracts.d.ts +0 -76
- package/packages/agentsam-nav/dist/index.d.ts +0 -11
- package/packages/agentsam-nav/dist/index.js +0 -614
- package/packages/agentsam-nav/dist/legacy.css +0 -83
- package/packages/agentsam-nav/dist/shell.css +0 -113
- package/packages/agentsam-nav/dist/theme.css +0 -113
- package/packages/agentsam-nav/dist/utils.d.ts +0 -1
- package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +0 -670
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Unified Input for Browser Games (keyboard + mouse + touch + Gamepad, action mapping, deadzones, buffering)
|
|
2
|
+
|
|
3
|
+
Consolidated from MDN (Gamepad API, Pointer Events, KeyboardEvent) and fighting/platformer input-design canon (see Sources). Focus: what an AI builder needs so games are **playable on every device with one code path**, controls are rebindable, and input feels responsive (buffering) rather than dropped.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Architecture: one action layer over all devices
|
|
8
|
+
|
|
9
|
+
**Never scatter `if (keys.KeyW)` gameplay checks through your code.** Build two layers:
|
|
10
|
+
1. **Raw device state** — updated from events (keyboard/pointer) and polling (gamepad).
|
|
11
|
+
2. **Abstract actions** — a fixed set like `moveX`, `moveY`, `aimX`, `jump`, `attack`, `pause`. Gameplay reads only actions. Any device can drive any action → keyboard, touch, and gamepad all "just work," and rebinding is trivial.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const actions = { moveX:0, moveY:0, jump:false, attack:false /* ... */ };
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Compute actions once per frame in an `updateInput()` called at the top of the loop, then gameplay consumes `actions` (and derived `justPressed` edges).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Keyboard: use `event.code`, track state, don't act on the event
|
|
22
|
+
|
|
23
|
+
- **Track pressed keys in a `Set`** on `keydown`/`keyup`; read the set in the loop. **Do NOT run movement/gameplay directly in the event handler** (event repeat rate ≠ frame rate → frame-dependent, laggy movement).
|
|
24
|
+
- **Use `event.code`** (physical key, e.g. `'KeyW'`, `'Space'`, `'ArrowLeft'`) not `event.key` (layout/locale-dependent) and not the **deprecated `keyCode`**. `event.code` keeps WASD in the same physical spot on AZERTY/QWERTZ.
|
|
25
|
+
- **`preventDefault()` for game keys** (Space, arrows) so the page doesn't scroll; but don't blanket-block everything (leave F5, devtools, tab-out).
|
|
26
|
+
- **Clear the key set on `blur`/`visibilitychange`** — otherwise a key held while tabbing out gets "stuck down" forever (very common bug).
|
|
27
|
+
- For text-entry vs gameplay, gate input by focus/mode.
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const keys = new Set();
|
|
31
|
+
addEventListener('keydown', e => { keys.add(e.code); if (GAME_KEYS.has(e.code)) e.preventDefault(); });
|
|
32
|
+
addEventListener('keyup', e => keys.delete(e.code));
|
|
33
|
+
addEventListener('blur', () => keys.clear());
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 3. Pointer Events: unify mouse + touch + pen
|
|
39
|
+
|
|
40
|
+
- **Use Pointer Events (`pointerdown/move/up/cancel`) instead of separate mouse + touch listeners.** One API covers mouse, touch, and stylus, with `pointerId` for multitouch and `pressure`/`pointerType`.
|
|
41
|
+
- Handle **`pointercancel`** (OS steals the touch) as a release — forgetting it strands buttons/joysticks "held."
|
|
42
|
+
- Set CSS **`touch-action: none`** on the canvas and `preventDefault()` to stop scroll/zoom/pull-to-refresh eating input.
|
|
43
|
+
- Convert client coords to canvas/world coords using `getBoundingClientRect()` and `devicePixelRatio`; don't assume clientX == canvas pixel.
|
|
44
|
+
- Mouse-look for FPS uses the **Pointer Lock API** (`requestPointerLock` on a gesture; read `movementX/Y`) — see the Three.js controls skill.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 4. Touch controls (mobile)
|
|
49
|
+
|
|
50
|
+
- **Virtual joystick** for movement (e.g. **nipplejs**, static or dynamic) mapped to `moveX/moveY` normalized −1..1; **on-screen buttons** or a right-side aim/tap zone for actions. Split-screen: left = move, right = act/aim.
|
|
51
|
+
- **Minimum 44px hit targets** (Apple HIG). Give visual feedback on press.
|
|
52
|
+
- Support multitouch: track pointers by `pointerId` so moving the joystick doesn't cancel a jump button.
|
|
53
|
+
- Normalize touch → the same **actions** as keyboard/gamepad; apply with delta time.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 5. Gamepad API: poll every frame, use standard mapping + deadzones
|
|
58
|
+
|
|
59
|
+
- **Poll `navigator.getGamepads()` every frame** in the loop. The `gamepadconnected`/`gamepaddisconnected` events only tell you a pad exists — **do not cache the `Gamepad` object**; it's a snapshot. Get fresh state each frame.
|
|
60
|
+
- A pad often only appears **after the user presses a button** ("waking"). Handle connect/disconnect gracefully mid-game.
|
|
61
|
+
- Prefer **`gamepad.mapping === 'standard'`** — browsers remap Xbox/PS/etc. to a consistent layout:
|
|
62
|
+
- Buttons: 0=A/Cross, 1=B/Circle, 2=X/Square, 3=Y/Triangle, 4/5=bumpers, 6/7=triggers (**use `.value` 0–1**, analog), 8=Back/Select, 9=Start, 10/11=stick presses, 12–15=D-pad U/D/L/R.
|
|
63
|
+
- Axes (−1..1): 0=LX, 1=LY (**−1 is up**), 2=RX, 3=RY.
|
|
64
|
+
- Read digital with `buttons[i].pressed`, analog triggers with `buttons[i].value > threshold`.
|
|
65
|
+
- Non-standard pads (`mapping !== 'standard'`) have unpredictable indices — offer a rebinding screen as fallback.
|
|
66
|
+
- Haptics via `gamepad.vibrationActuator?.playEffect('dual-rumble', {...})` where supported (progressive enhancement).
|
|
67
|
+
|
|
68
|
+
### Deadzones (required for analog sticks)
|
|
69
|
+
Sticks never rest at exactly 0. Apply a deadzone or you get drift ("character walks by itself").
|
|
70
|
+
- **Radial deadzone** (correct for sticks — treat X/Y together, not per-axis) with re-normalization so full tilt still reaches magnitude 1:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
function radialDeadzone(x, y, dz = 0.15) {
|
|
74
|
+
const m = Math.hypot(x, y);
|
|
75
|
+
if (m < dz) return { x: 0, y: 0 };
|
|
76
|
+
const scale = ((m - dz) / (1 - dz)) / m; // re-normalize
|
|
77
|
+
return { x: x * scale, y: y * scale };
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
- Typical deadzone 0.1–0.25. Also deadzone trigger `.value`. Per-axis deadzones make diagonals feel wrong — prefer radial.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 6. Action mapping & rebinding
|
|
85
|
+
|
|
86
|
+
- Store bindings as **data** (action → list of physical inputs): `{ jump: ['Space','KeyW', {pad:0}], attack: ['Mouse0', {pad:2}] }`. `updateInput()` resolves bindings into `actions`.
|
|
87
|
+
- **Rebinding UI:** capture the next input event, write it into the binding table, persist to localStorage (see `save-persistence.md`). Detect and warn on conflicts.
|
|
88
|
+
- Support **multiple simultaneous devices** and per-action multiple bindings (WASD *and* arrows *and* stick all drive `moveX`).
|
|
89
|
+
- Keep a **`justPressed` / `justReleased` edge set**: compare this frame's action booleans to last frame's for one-shot actions (jump, shoot, menu confirm). Reading level-triggered state for these causes repeat-fire bugs.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 7. Input buffering (responsiveness / fairness)
|
|
94
|
+
|
|
95
|
+
- **Buffer discrete actions for a short window** (~100–150ms / ~6–8 frames) so an input pressed slightly before it's actionable still fires. Essential for platformers and fighting games — makes controls feel "tight" instead of "eating" inputs.
|
|
96
|
+
- E.g. jump pressed 4 frames before landing → still jump on landing.
|
|
97
|
+
- **Coyote time:** allow jump for a few frames *after* walking off a ledge — pairs with jump buffering; both dramatically improve platformer feel.
|
|
98
|
+
- Store buffered inputs with a timestamp/frame stamp; consume when the action becomes valid, and expire after the window.
|
|
99
|
+
- **Poll gamepad and sample input at a fixed rate tied to your fixed-timestep** update for deterministic gameplay/netcode; buffering + fixed step is what makes combos/prediction reproducible.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 8. Bug-prevention checklist
|
|
104
|
+
- **Movement in the keydown handler** → frame-rate-dependent, jerky; set flags, move in the loop with `dt`.
|
|
105
|
+
- **Using `event.key`/`keyCode`** → breaks on non-US layouts / deprecated; use `event.code`.
|
|
106
|
+
- **Stuck keys after tab-out** → clear key set on `blur`/`visibilitychange`.
|
|
107
|
+
- **Caching the `Gamepad` object** → stale input; call `getGamepads()` every frame.
|
|
108
|
+
- **No stick deadzone** → character drifts; apply radial deadzone with re-normalization.
|
|
109
|
+
- **Per-axis deadzone** → mushy/wrong diagonals; use radial.
|
|
110
|
+
- **Ignoring `pointercancel`** → stuck touch buttons/joysticks on mobile.
|
|
111
|
+
- **Page scrolls/zooms during play** → missing `touch-action:none` / `preventDefault`.
|
|
112
|
+
- **Repeat-firing one-shot actions** → reading level state instead of `justPressed` edges.
|
|
113
|
+
- **"Ate my jump" feel** → no input buffering / coyote time.
|
|
114
|
+
- **Gamepad never detected** → user hasn't pressed a button to wake it; handle connect event + prompt.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Defaults to apply
|
|
119
|
+
- **Always generate the two-layer input system**: raw devices → normalized `actions`, with `justPressed`/`justReleased` edges. Gameplay reads only actions, so keyboard+mouse, touch (nipplejs + buttons), and gamepad all work from one code path.
|
|
120
|
+
- **Bake in the safety defaults**: `event.code`, clear keys on blur, poll gamepad each frame, radial deadzone (~0.15) with re-normalization, `touch-action:none`, handle `pointercancel`.
|
|
121
|
+
- **Include jump/input buffering (~120ms) + coyote time** in platformers/action templates by default — biggest feel win for cheap.
|
|
122
|
+
- **Make bindings data-driven and persisted** so a rebinding screen is a small add, and auto-detect the active device to show correct button prompts.
|
|
123
|
+
- **Semantic signs for move/steer/flight** are **not** defined here — open the
|
|
124
|
+
**`controls` skill** (`.grok/skills/controls/SKILL.md`) so `steer`/`roll`
|
|
125
|
+
mean player-left correctly. This file is plumbing; that skill is the sign
|
|
126
|
+
convention + mandatory A/D self-test.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Sources
|
|
131
|
+
- MDN — Gamepad API: https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API ; Using the Gamepad API: https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API ; Implementing controls: https://developer.mozilla.org/en-US/docs/Games/Techniques/Controls_Gamepad_API
|
|
132
|
+
- MDN — `Navigator.getGamepads()`: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getGamepads ; `Gamepad` / `GamepadButton`: https://developer.mozilla.org/en-US/docs/Web/API/Gamepad
|
|
133
|
+
- MDN — Pointer Events: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events ; `touch-action`: https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
|
|
134
|
+
- MDN — `KeyboardEvent.code` + code values: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code ; https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values
|
|
135
|
+
- MDN — Pointer Lock API: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
|
|
136
|
+
- W3C — Gamepad spec (standard mapping): https://w3c.github.io/gamepad/#remapping
|
|
137
|
+
- Josh Sunshine / gamepad mapping deep dive: https://adamjones.me/blog/gamepad-mapping/
|
|
138
|
+
- nipplejs (virtual joystick): https://github.com/yoannmoinet/nipplejs
|
|
139
|
+
- Input buffering & coyote time (game feel): GDC/《Celeste》 & fighting-game input design write-ups, e.g. https://www.gamedeveloper.com/design/how-to-add-input-buffering-to-your-game and Celeste physics notes.
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Phaser 3 / 4 — Deep Engine Guide (scenes, physics, scale, tilemaps, pooling, architecture)
|
|
2
|
+
|
|
3
|
+
Phaser is the default choice for **2D browser games**. This file goes deep on the parts that actually break in generated games. It assumes the general loop/mobile/perf rules in `threejs-foundational.md` and does **not** repeat them.
|
|
4
|
+
|
|
5
|
+
> **Version note (verified):** Phaser **3** is the mature, ubiquitous line (3.90+). Phaser **4** shipped in 2025 with a rewritten WebGL renderer (internally "Beam") and optional **WebGPU** path, plus the core split into modular packages. Phaser 4 is *intentionally near-API-compatible with Phaser 3* — the Scene lifecycle, Arcade/Matter physics, Scale Manager, Loader and GameObject APIs described here carry over. Prefer Phaser 3 (3.90.x) for maximum stability/plugin compatibility today; use Phaser 4 when you want the faster renderer/WebGPU and can tolerate a younger ecosystem. When unsure, target v3 API surface — it runs on both.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Scene model — the thing to get right first
|
|
10
|
+
|
|
11
|
+
A Phaser game is a **stack of Scenes**, each an almost self-contained world (own display list, cameras, input, tweens, physics, clock). The `Phaser.Game` instance owns only truly global systems: **Renderer, Animation Manager, global Cache, Registry (global DataManager), Input Manager, Scene Manager, Sound Manager, TimeStep**. Everything else (`this.add`, `this.tweens`, `this.physics`, `this.input`, `this.cameras`) is **per-scene**. A tween created in Scene A is unrelated to one in Scene B.
|
|
12
|
+
|
|
13
|
+
### Lifecycle callbacks (exact order)
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
class GameScene extends Phaser.Scene {
|
|
17
|
+
constructor() { super('game'); } // unique string key
|
|
18
|
+
init(data) {} // (1) reset per-run state HERE, receives data from start/launch
|
|
19
|
+
preload() {} // (2) queue asset loads only
|
|
20
|
+
create(data) {} // (3) build objects; assets from preload are now ready
|
|
21
|
+
update(time, delta){} // (4) every tick while RUNNING (delta in ms)
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Status flow: `PENDING → INIT (booted) → START → LOADING → CREATING → RUNNING`. `RUNNING` can go to `PAUSED` (renders, no update) or `SLEEPING` (no update, no render), then back. `stop()` → `SHUTDOWN` (can restart). `remove()` → `DESTROYED` (gone).
|
|
26
|
+
|
|
27
|
+
### The scene stack pattern (best-practice architecture)
|
|
28
|
+
|
|
29
|
+
Run **multiple scenes at once**, layered bottom→top by config order:
|
|
30
|
+
|
|
31
|
+
- **Boot** — load the tiny assets needed for the loading screen (bar image, logo); set Scale/registry defaults; `this.scene.start('preload')`.
|
|
32
|
+
- **Preload** — load everything with a visible progress bar (`this.load.on('progress', v => …)`), then `start('menu')`.
|
|
33
|
+
- **Menu / Title** — UI, start button.
|
|
34
|
+
- **Game** — actual gameplay.
|
|
35
|
+
- **UI / HUD** — run **in parallel** over Game via `this.scene.launch('ui')`; keeps HUD stable while Game restarts. Communicate via events/registry, never direct references.
|
|
36
|
+
- **Pause / GameOver** — overlay scenes launched over Game.
|
|
37
|
+
|
|
38
|
+
### Scene control verbs (get these exactly right — a top bug source)
|
|
39
|
+
|
|
40
|
+
| Method | Effect on target | Effect on caller |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `start('k')` | **stops** then starts k | **stops** caller |
|
|
43
|
+
| `launch('k')` | starts k (parallel) | caller keeps running |
|
|
44
|
+
| `switch('k')` | starts or **wakes** k | **sleeps** caller |
|
|
45
|
+
| `run('k')` | resume if paused / wake if sleeping / restart if running / else start | caller keeps running |
|
|
46
|
+
| `pause`/`resume` | freeze update, keep render | — |
|
|
47
|
+
| `sleep`/`wake` | freeze update + render | — |
|
|
48
|
+
| `stop` | shutdown | — |
|
|
49
|
+
|
|
50
|
+
Rules of thumb: gameplay you replay from scratch → **start/stop** or **sleep/wake**; a modal (pause menu, shop) over live gameplay → **pause/resume** or **launch** an overlay; menus you revisit → **sleep/wake** is easier to reason about than start/stop.
|
|
51
|
+
|
|
52
|
+
### The #1 Phaser correctness bug: state not reset on restart
|
|
53
|
+
|
|
54
|
+
Scenes are **booted once, started many times**. Module-level or constructor-set flags persist across restarts.
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// BROKEN: gameOver stays true after restart → instant game over
|
|
58
|
+
class S extends Phaser.Scene { constructor(){ super('s'); this.gameOver = false; } }
|
|
59
|
+
|
|
60
|
+
// CORRECT: reset run state in init()
|
|
61
|
+
class S extends Phaser.Scene {
|
|
62
|
+
constructor(){ super('s'); }
|
|
63
|
+
init(){ this.gameOver = false; this.score = 0; } // runs on every start
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Related: arrays of destroyed game objects survive restart. **Clean up on SHUTDOWN**:
|
|
68
|
+
```js
|
|
69
|
+
this.events.once('shutdown', () => { this.enemies.length = 0; });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Cleanup rule
|
|
73
|
+
|
|
74
|
+
On `shutdown`, Phaser auto-destroys the scene's display list, tweens, timers and input. But **external references, global timers, DOM listeners, EventEmitter subscriptions on cross-scene emitters, and pooled arrays are yours to clear**. A destroyed GameObject still registered on an emitter is a classic crash. Always `this.events.once('shutdown', cleanup)`.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 2. Physics: Arcade vs Matter (choose deliberately)
|
|
79
|
+
|
|
80
|
+
Phaser ships two physics systems. **Pick Arcade unless you specifically need Matter** — mixing/over-choosing is a common performance and complexity mistake.
|
|
81
|
+
|
|
82
|
+
### Arcade Physics — AABB, fast, for platformers/shooters/arcade
|
|
83
|
+
|
|
84
|
+
- **Only axis-aligned bounding boxes (rectangles) and circles.** No rotation of bodies, no slopes, no polygons. Extremely fast; handles thousands of bodies.
|
|
85
|
+
- Two body types: **Dynamic** (`this.physics.add.sprite`) responds to velocity/gravity/collisions; **Static** (`this.physics.add.staticGroup`) never moves (platforms, walls) and is cheaper.
|
|
86
|
+
- Core calls: `body.setVelocity(x,y)`, `setGravityY`, `setBounce`, `setCollideWorldBounds(true)`, `setImmovable(true)`, `setAllowGravity(false)`.
|
|
87
|
+
- **Collide vs Overlap:**
|
|
88
|
+
- `this.physics.add.collider(a, b, cb)` — separates bodies (they block each other), optional callback.
|
|
89
|
+
- `this.physics.add.overlap(a, b, cb)` — detects overlap without separation (pickups, hitboxes, triggers).
|
|
90
|
+
- Both accept Groups, arrays, or single objects; add a `processCallback` (4th arg) to conditionally allow/deny a collision.
|
|
91
|
+
- Config: `physics: { default: 'arcade', arcade: { gravity: { y: 300 }, debug: true } }`. Turn on `debug` while building — it draws body outlines and velocity vectors and reveals 90% of "collision doesn't work" issues.
|
|
92
|
+
- Set body size explicitly for non-rectangular sprites: `sprite.body.setSize(w,h).setOffset(x,y)` or `setCircle(r)`.
|
|
93
|
+
- **Fast small objects tunnel through thin walls** (AABB, discrete step). Mitigate: thicker colliders, cap max velocity, or set world bounds; Arcade has limited continuous collision.
|
|
94
|
+
|
|
95
|
+
### Matter.js — full rigid-body, for physics-toys/ragdolls/complex shapes
|
|
96
|
+
|
|
97
|
+
- Real rigid-body dynamics: rotation, arbitrary convex/compound polygons, constraints/joints, springs, restitution, friction, sleeping bodies.
|
|
98
|
+
- Use when the *physics itself is the game* (Angry-Birds-like, stacking, contraptions, vehicles) or you need realistic collisions/rotation.
|
|
99
|
+
- Config: `physics: { default: 'matter', matter: { gravity: { y: 1 }, debug: true } }`.
|
|
100
|
+
- `this.matter.add.sprite(x,y,key,null,{ shape:'circle', restitution:0.6 })`. Use collision events: `this.matter.world.on('collisionstart', (e)=>{ for (const p of e.pairs){…} })`, plus **collision filters/categories** for what hits what.
|
|
101
|
+
- Heavier than Arcade; watch body counts on mobile. Enable body sleeping for idle stacks.
|
|
102
|
+
|
|
103
|
+
**Decision:** platformer, top-down shooter, endless runner, breakout, most casual games → **Arcade**. Physics sandbox, realistic stacking/rotation, joints → **Matter**. Don't reach for Matter just for "better collisions" — 90% of 2D games are Arcade games.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 3. Scale Manager — responsive/mobile without stretching
|
|
108
|
+
|
|
109
|
+
Set once in game config. The two modes that matter:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
scale: {
|
|
113
|
+
mode: Phaser.Scale.FIT, // letterbox: keep aspect, fit inside parent
|
|
114
|
+
autoCenter: Phaser.Scale.CENTER_BOTH,
|
|
115
|
+
width: 800, height: 600, // your fixed design resolution
|
|
116
|
+
parent: 'game',
|
|
117
|
+
// min/max clamp the FIT scaling:
|
|
118
|
+
min: { width: 400, height: 300 }, max: { width: 1600, height: 1200 }
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- **`FIT`** — canvas scales to fit the parent preserving aspect ratio; letterbox bars appear. Your game logic runs at a **fixed design resolution** (800×600 here) — simplest and most predictable. Best default for most games.
|
|
123
|
+
- **`RESIZE`** — canvas always matches parent size; **no fixed resolution**, game world dimensions change. You must handle the `resize` event and reposition/relayout UI yourself (`this.scale.on('resize', (gameSize)=>{…})`). Use for games that should truly fill the screen (strategy, infinite canvas) where you can reflow layout.
|
|
124
|
+
- `ENVELOP` — cover the parent (may crop). `WIDTH_CONTROLS_HEIGHT` / `HEIGHT_CONTROLS_WIDTH` — lock one axis.
|
|
125
|
+
- `NONE` — fixed, no scaling.
|
|
126
|
+
|
|
127
|
+
**Orientation:** listen `this.scale.on('orientationchange', o => …)` or check `this.scale.isPortrait`. For a landscape-only game, show a "rotate your device" overlay in portrait rather than fighting the layout. Combine with `viewport` meta + `touch-action: none` (see three.js file §7).
|
|
128
|
+
|
|
129
|
+
**Retina/DPR:** Phaser respects `resolution`/DPR via the renderer; for pixel-art keep `pixelArt: true` (sets nearest-neighbor + disables antialias) and `roundPixels: true` to avoid sub-pixel shimmer. Rendering at a modest internal resolution and letting FIT upscale is a legit perf win on mobile.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 4. Sprites, atlases & animations
|
|
134
|
+
|
|
135
|
+
- **Always pack sprites into a texture atlas** (TexturePacker → JSON Hash/Array, or free tools). One atlas = one texture bind = fewer draw calls and no seams. `this.load.atlas('sheet','sheet.png','sheet.json')`, then `this.add.sprite(x,y,'sheet','frameName')`.
|
|
136
|
+
- Uniform grid frames → `this.load.spritesheet('run','run.png',{ frameWidth:32, frameHeight:32 })`.
|
|
137
|
+
- Animations are **global** (stored on the Animation Manager), defined once, reused by any sprite in any scene:
|
|
138
|
+
```js
|
|
139
|
+
this.anims.create({ key:'run', frames:this.anims.generateFrameNames('sheet',{prefix:'run_',start:0,end:7,zeroPad:2}), frameRate:12, repeat:-1 });
|
|
140
|
+
sprite.play('run');
|
|
141
|
+
```
|
|
142
|
+
- Batching: sprites sharing the **same texture** batch into few draw calls. Interleaving textures (sprite from atlas A, then B, then A) breaks the batch. Group same-texture sprites and set depth thoughtfully.
|
|
143
|
+
- Bitmap fonts (`load.bitmapFont`) render far cheaper than lots of dynamic `Text` objects (each `Text` is its own canvas texture — expensive to update every frame). For frequently-changing text prefer bitmap fonts or update sparingly.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 5. Tilemaps
|
|
148
|
+
|
|
149
|
+
For levels, use **Tiled** (`.tmx`/exported JSON) rather than hand-placing sprites.
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
// preload
|
|
153
|
+
this.load.image('tiles','tileset.png');
|
|
154
|
+
this.load.tilemapTiledJSON('map','level1.json');
|
|
155
|
+
// create
|
|
156
|
+
const map = this.make.tilemap({ key:'map' });
|
|
157
|
+
const tileset = map.addTilesetImage('tilesetNameInTiled','tiles');
|
|
158
|
+
const ground = map.createLayer('Ground', tileset, 0, 0);
|
|
159
|
+
ground.setCollisionByProperty({ collides:true }); // set per-tile in Tiled
|
|
160
|
+
this.physics.add.collider(player, ground);
|
|
161
|
+
// object layer for spawns/enemies:
|
|
162
|
+
const objs = map.getObjectLayer('Objects').objects;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- **Static layers** (`createLayer`) are fast (culled, batched). Dynamic tile edits use the same layer API (`putTileAt`, `removeTileAt`) — layers are dynamic in current Phaser.
|
|
166
|
+
- Set collision by **property** or by tile index/array; visualize with `layer.renderDebug(graphics)`.
|
|
167
|
+
- Cull off-screen tiles automatically via the camera; keep tile layers reasonably sized, split huge worlds into chunks.
|
|
168
|
+
- Use **object layers** for spawn points, triggers, and entity placement instead of hardcoding coordinates.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 6. Object pooling with Groups (the mobile perf lever)
|
|
173
|
+
|
|
174
|
+
Creating/destroying bullets, enemies, particles every frame thrashes GC and stutters. **Pool them.**
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
this.bullets = this.physics.add.group({
|
|
178
|
+
classType: Bullet, maxSize: 64, runChildUpdate: true
|
|
179
|
+
});
|
|
180
|
+
// fire:
|
|
181
|
+
const b = this.bullets.get(x, y); // reuse dead one or make new (up to maxSize)
|
|
182
|
+
if (!b) return; // pool exhausted → skip
|
|
183
|
+
b.enableBody(true, x, y, true, true); // reactivate + show
|
|
184
|
+
// on expire/off-screen: DON'T destroy — recycle:
|
|
185
|
+
b.disableBody(true, true); // deactivate + hide, returns to pool
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- `get()` returns an **inactive** member (recycled) or creates a new one until `maxSize`; returns `null` when full — always null-check.
|
|
189
|
+
- `killAndHide(child)` / `disableBody(true,true)` recycles; avoid `destroy()` for pooled objects.
|
|
190
|
+
- `runChildUpdate:true` calls each active child's `preUpdate`/`update` — put per-object logic in the class.
|
|
191
|
+
- Only iterate **active** members: `this.bullets.getChildren()` and skip `!child.active`, or use `group.getMatching('active', true)`.
|
|
192
|
+
- Reuse tween/particle emitters too; destroy emitters and tweens on `SHUTDOWN`.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 7. Input
|
|
197
|
+
|
|
198
|
+
- Keyboard: `this.cursors = this.input.keyboard.createCursorKeys()` (arrows + space/shift), or `this.keys = this.input.keyboard.addKeys('W,A,S,D')`. **Read state in `update` via `.isDown`**; use `Phaser.Input.Keyboard.JustDown(key)` for single-press actions. Don't drive movement off `keydown` events.
|
|
199
|
+
- Pointer/touch: `this.input.on('pointerdown', p => …)`; enable object interaction with `sprite.setInteractive()` then `sprite.on('pointerdown', …)`. Set explicit hit areas for irregular sprites.
|
|
200
|
+
- Unify keyboard + touch + **Gamepad API** (`this.input.gamepad`) into one input-state object your `update` reads, so control code doesn't branch per device.
|
|
201
|
+
- Drag: `this.input.setDraggable(sprite)` + `'drag'` event. For virtual joysticks on mobile, integrate nipplejs or the rex virtual joystick plugin and feed normalized -1..1 into your input state.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 8. Cameras & tweens
|
|
206
|
+
|
|
207
|
+
- Each scene has a main camera (`this.cameras.main`). Follow the player: `camera.startFollow(player, true, 0.08, 0.08)` (lerp for smooth follow), set `camera.setBounds(0,0,mapW,mapH)` and `setZoom()`. `setDeadzone()` avoids jitter around the player.
|
|
208
|
+
- Effects for free polish: `camera.shake(200, 0.01)`, `camera.flash()`, `camera.fade()`. Multiple cameras enable split-screen / minimaps (`camera.ignore(objects)` to exclude).
|
|
209
|
+
- Tweens for juice: `this.tweens.add({ targets:sprite, scale:1.2, yoyo:true, duration:120, ease:'Sine.easeInOut' })`. **Always destroy long-lived/looping tweens on SHUTDOWN** or they reference dead objects.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 9. Cross-scene communication (avoid spaghetti)
|
|
214
|
+
|
|
215
|
+
- **Registry** (global DataManager): `this.registry.set('score', 0)` / `get` — shared across all scenes; emits `changedata` events.
|
|
216
|
+
- **Scene events / a shared EventEmitter** ("EventsCenter"): create one `new Phaser.Events.EventEmitter()` in a module, import into scenes, emit/subscribe. Lets the UI scene react to Game scene events with **zero direct coupling**. Always `off()` listeners on shutdown.
|
|
217
|
+
- `this.scene.get('ui').events.emit(...)` works but couples scenes — prefer the shared emitter/registry.
|
|
218
|
+
- Pass one-shot data via `this.scene.start('game', { level: 3 })` → arrives in `init(data)` / `create(data)`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 10. Performance checklist (Phaser-specific)
|
|
223
|
+
|
|
224
|
+
- **Object pooling** for anything spawned repeatedly (bullets, enemies, particles, floating text).
|
|
225
|
+
- **Texture atlases** everywhere; group same-texture draws to keep batches intact.
|
|
226
|
+
- Turn **off physics debug** in production; only add bodies to objects that need them.
|
|
227
|
+
- Process **only active** objects; set `sprite.active=false`/`visible=false` for off-screen entities instead of updating them.
|
|
228
|
+
- Prefer **static** physics bodies / static tile layers for non-moving geometry.
|
|
229
|
+
- Minimize per-frame `Text` updates and `Graphics` re-draws (redrawing a Graphics every frame is expensive) — cache to a texture (`generateTexture`) or use bitmap fonts.
|
|
230
|
+
- Avoid per-frame allocations in `update`; reuse vectors/objects.
|
|
231
|
+
- Destroy tweens, timers (`this.time` events), and particle emitters on `SHUTDOWN`; clear pooled arrays.
|
|
232
|
+
- Lazy-load per-level assets in that level's preload; unload with `this.textures.remove` / `this.cache` when leaving big levels.
|
|
233
|
+
- Smaller canvas + FIT upscale on low-end mobile; consider `Phaser.CANVAS` fallback only if WebGL is unavailable (WebGL is default and much faster).
|
|
234
|
+
- Profile with the browser FPS meter and `game.loop.actualFps`; Phaser 4's WebGPU path reduces CPU draw-call overhead further.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 11. Common Phaser pitfalls (checklist)
|
|
239
|
+
|
|
240
|
+
- Run-state not reset in `init()` → instant game-over / carried-over score after restart.
|
|
241
|
+
- Not cleaning arrays/emitter listeners on `shutdown` → crashes on destroyed objects after restart.
|
|
242
|
+
- Using `this.scene.start` when you meant `launch` (start stops the caller — kills your HUD).
|
|
243
|
+
- Movement/animation not scaled by `delta` (Phaser `update(time, delta)` gives delta in **milliseconds**).
|
|
244
|
+
- Physics body doesn't match sprite art → set `body.setSize/offset`; wrong/no collider group; forgetting `setCollideWorldBounds`.
|
|
245
|
+
- Fast objects tunneling through thin walls in Arcade → thicker walls / velocity cap.
|
|
246
|
+
- Text objects updated every frame tanking FPS → bitmap fonts / update on change only.
|
|
247
|
+
- Creating instead of pooling bullets/enemies → GC stutter on mobile.
|
|
248
|
+
- `RESIZE` scale mode without repositioning UI → misaligned HUD; use `FIT` unless you handle `resize`.
|
|
249
|
+
- Loading assets in `create` instead of `preload` → assets not ready / race conditions.
|
|
250
|
+
- Mixing Matter + Arcade needlessly, or choosing Matter when Arcade suffices → perf and complexity cost.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Defaults to apply
|
|
255
|
+
|
|
256
|
+
- **Default to Phaser (v3 API) for any 2D game**; scaffold the **Boot → Preload(progress bar) → Menu → Game → parallel UI → Pause/GameOver** scene stack automatically.
|
|
257
|
+
- **Always put per-run state reset in `init()`** and register a `this.events.once('shutdown', cleanup)` in every gameplay scene — this alone eliminates the most common generated-game bug (broken restart).
|
|
258
|
+
- **Choose Arcade physics by default; only emit Matter when the physics is the game.** Encode the decision table above in the builder's engine-selection step.
|
|
259
|
+
- **Emit object pools (Groups with `maxSize` + recycle via `disableBody`) for bullets/enemies/particles** rather than create/destroy, and **texture atlases** for all sprites.
|
|
260
|
+
- Use **`Scale.FIT` + fixed design resolution + `CENTER_BOTH`** as the responsive default; add a rotate-device overlay for orientation-locked games.
|
|
261
|
+
- Load levels from **Tiled JSON tilemaps** with collision-by-property and object layers for spawns, instead of hardcoded coordinates.
|
|
262
|
+
- Decouple scenes via a **shared EventEmitter + Registry**, never direct scene references.
|
|
263
|
+
- Turn **physics `debug` on during generation/testing, off in the shipped build.**
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Sources
|
|
268
|
+
- Phaser official docs — Scenes (lifecycle, systems, control methods, restart pitfalls): https://docs.phaser.io/phaser/concepts/scenes
|
|
269
|
+
- Phaser docs — Arcade Physics: https://docs.phaser.io/phaser/concepts/physics/arcade ; Matter Physics: https://docs.phaser.io/phaser/concepts/physics/matter
|
|
270
|
+
- Phaser docs — Scale Manager: https://docs.phaser.io/phaser/concepts/scale-manager
|
|
271
|
+
- Phaser docs — Tilemaps: https://docs.phaser.io/phaser/concepts/tilemaps ; Loader: https://docs.phaser.io/phaser/concepts/loader
|
|
272
|
+
- Phaser docs — Groups & object pooling: https://docs.phaser.io/phaser/concepts/gameobjects/group ; Animations: https://docs.phaser.io/phaser/concepts/animations
|
|
273
|
+
- Phaser docs — Input (keyboard/pointer/gamepad): https://docs.phaser.io/phaser/concepts/input
|
|
274
|
+
- Phaser 4 announcement / release: https://phaser.io/news (Phaser 4 + Beam renderer / WebGPU) and https://github.com/phaserjs/phaser/releases
|
|
275
|
+
- API reference: https://newdocs.phaser.io/docs/latest
|
|
276
|
+
- franzeus.medium.com — "How I optimized my Phaser 3 action game" (pooling, atlases, active-only processing)
|
|
277
|
+
- joshmorony.com — Phaser scaling / responsive guides
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Procedural Generation (seeded RNG, noise, dungeons/mazes/terrain, wave function collapse)
|
|
2
|
+
|
|
3
|
+
Consolidated from Red Blob Games, the simplex-noise library, and WFC primary sources (see Sources). Focus: what an AI builder needs to generate **varied but reproducible** worlds without the classic bugs — non-deterministic output, disconnected dungeons, blobby/banded terrain.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Seeded RNG is the foundation — never use bare `Math.random()`
|
|
8
|
+
|
|
9
|
+
`Math.random()` is **not seedable**, so you can't reproduce a level, share a seed, debug a bad map, or sync generation across clients in multiplayer. **Always drive procedural generation from a seeded PRNG.**
|
|
10
|
+
|
|
11
|
+
**mulberry32** — the go-to small, fast, good-quality 32-bit seeded PRNG:
|
|
12
|
+
```js
|
|
13
|
+
function mulberry32(seed) {
|
|
14
|
+
return function () {
|
|
15
|
+
let t = (seed += 0x6D2B79F5);
|
|
16
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
17
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
18
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296; // [0,1)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const rng = mulberry32(12345); // same seed → identical sequence forever
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Rules:
|
|
25
|
+
- **Hash string seeds to an int first** (e.g. `xmur3`) so `"cave-42"` maps to a stable number.
|
|
26
|
+
- **Use separate RNG streams for independent systems** (terrain vs loot vs enemy placement) so changing one doesn't reshuffle the others (`mulberry32(seed)`, `mulberry32(seed ^ 0x9e3779b9)`, …).
|
|
27
|
+
- Add helpers: `randRange(a,b)=a+rng()*(b-a)`, `randInt`, `pick(arr)`, `shuffle` (Fisher–Yates using `rng`).
|
|
28
|
+
- For **networked/deterministic** generation, both sides must run the *same* PRNG and consume it in the *same order* (deterministic multiplayer is out of scope on this deploy target). Order-of-consumption bugs are the #1 desync source.
|
|
29
|
+
- Alternatives: `alea`, PCG. mulberry32 is plenty for games.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 2. Noise: value / Perlin / simplex (for terrain, textures, organic fields)
|
|
34
|
+
|
|
35
|
+
Random-per-cell is white noise (harsh, uncorrelated). **Coherent noise** produces smooth, natural-looking fields where nearby points are similar.
|
|
36
|
+
|
|
37
|
+
- **Value noise** — cheap, interpolate random lattice values. OK for simple stuff.
|
|
38
|
+
- **Perlin noise** — classic gradient noise; can show axis-aligned artifacts.
|
|
39
|
+
- **Simplex / OpenSimplex** — fewer directional artifacts, scales better to higher dimensions; preferred default. Use the **`simplex-noise` npm package**.
|
|
40
|
+
- **v4 API changed:** no more `new SimplexNoise()`. Use factory functions and pass your seeded PRNG:
|
|
41
|
+
```js
|
|
42
|
+
import { createNoise2D } from 'simplex-noise';
|
|
43
|
+
const noise2D = createNoise2D(mulberry32(12345)); // returns ~[-1, 1]
|
|
44
|
+
const v = noise2D(x * 0.01, y * 0.01); // scale coords = "frequency"
|
|
45
|
+
```
|
|
46
|
+
(Also `createNoise3D`/`createNoise4D`; passing a PRNG makes output reproducible — without one it uses `Math.random()` = non-reproducible.)
|
|
47
|
+
|
|
48
|
+
**Key techniques (from Red Blob Games "Making maps with noise"):**
|
|
49
|
+
- **Frequency:** multiply input coords by a scale; small scale = large smooth features.
|
|
50
|
+
- **Octaves / fBm:** sum several noise layers at increasing frequency and decreasing amplitude (`amplitude *= persistence(≈0.5)`, `frequency *= lacunarity(≈2)`) for natural detail.
|
|
51
|
+
- **Normalize** noise from [−1,1] to [0,1] before thresholding.
|
|
52
|
+
- **Redistribution:** `elev = Math.pow(elev, k)` to bias toward valleys/plains vs peaks.
|
|
53
|
+
- **Island mask:** multiply by a radial falloff so edges are water.
|
|
54
|
+
- **Biomes:** sample **two independent noise fields** (elevation + moisture) and look up a Whittaker-style biome table — don't derive moisture from elevation (they'd correlate and look wrong).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. Dungeon / room generation
|
|
59
|
+
|
|
60
|
+
Common approaches, pick per game:
|
|
61
|
+
- **Rooms + corridors (BSP or random placement):** place non-overlapping rooms, then connect. BSP recursively splits space and puts a room in each leaf; connect sibling leaves → guaranteed structure. Random placement: drop rooms, reject overlaps, connect with L-shaped/A* corridors.
|
|
62
|
+
- **Cellular automata caves:** fill grid randomly (~45% wall), run several smoothing passes (`cell = neighbors≥5 ? wall : floor`) → organic caverns. Then **flood-fill and keep only the largest connected region** (or carve tunnels to connect regions).
|
|
63
|
+
- **Drunkard's walk / random walk:** carve floors along a random walk for winding caves.
|
|
64
|
+
|
|
65
|
+
**Critical rule: guarantee connectivity.** After generation, **flood-fill from the entrance and verify every important tile (exit, key rooms, loot) is reachable.** Discard/regenerate or carve connectors otherwise. "Unreachable exit / locked-in player" is the #1 procgen bug. Also validate: min room count, spawn ≠ exit, no soft-locks.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 4. Maze generation
|
|
70
|
+
|
|
71
|
+
- Grid where each cell has walls; carve passages.
|
|
72
|
+
- **Recursive backtracker (randomized DFS):** deep, winding mazes with long corridors — most common.
|
|
73
|
+
- **Randomized Prim's / Kruskal's:** more uniform, bushier mazes.
|
|
74
|
+
- All produce a "perfect maze" (exactly one path between any two cells). Add **loops** by knocking out extra walls if you want multiple routes.
|
|
75
|
+
- Use your seeded `rng` for neighbor choice so the maze is reproducible.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 5. Terrain generation
|
|
80
|
+
|
|
81
|
+
- **Heightmap from fBm simplex** (see §2): sample noise per cell → elevation → color/mesh. Threshold into water/sand/grass/rock/snow bands.
|
|
82
|
+
- **Domain warping** (offset sample coords by another noise) for more natural, less "blobby" coastlines.
|
|
83
|
+
- For 3D: feed the heightmap into a plane geometry's vertex Y (Three.js `PlaneGeometry` + displace vertices) and `computeVertexNormals()`; consider chunking + LOD for large worlds.
|
|
84
|
+
- Keep generation **deterministic per chunk**: derive each chunk's local seed from `(worldSeed, chunkX, chunkY)` so infinite/streamed worlds are stable and reproducible.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 6. Wave Function Collapse (WFC) basics
|
|
89
|
+
|
|
90
|
+
WFC is a constraint-solver that generates output where every local neighborhood matches examples/adjacency rules — great for tile maps, textures, and levels that must "fit together."
|
|
91
|
+
|
|
92
|
+
Algorithm (overlapping or simple-tiled):
|
|
93
|
+
1. Every cell starts in **superposition** (all tiles possible).
|
|
94
|
+
2. **Observe:** pick the lowest-entropy cell (fewest remaining options) and **collapse** it to one tile (weighted-random).
|
|
95
|
+
3. **Propagate:** remove now-impossible neighbors' options based on adjacency rules; repeat until stable.
|
|
96
|
+
4. Loop until all cells collapsed, or **backtrack/restart on contradiction** (a cell with zero options).
|
|
97
|
+
|
|
98
|
+
Rules & gotchas:
|
|
99
|
+
- Adjacency rules come from a sample image (overlapping model) or hand-authored tile edges (simple-tiled model).
|
|
100
|
+
- **Contradictions happen** — implement restart or backtracking; without it, generation hangs/throws.
|
|
101
|
+
- Weight tiles for aesthetics; use noise (from §2) to bias regional weights for large-scale structure, then WFC for coherent local detail.
|
|
102
|
+
- For JS: **mxgmn/WaveFunctionCollapse** (original), Boris the Brave's write-up + **DeBroglie**, or ndarray-based JS ports.
|
|
103
|
+
|
|
104
|
+
> Note: Red Blob Games does **not** have a WFC tutorial (a common false citation). Cite Maxim Gumin's repo and Boris the Brave instead. Red Blob Games *is* the canonical source for **noise maps** and **A\*/grids**.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 7. Bug-prevention checklist
|
|
109
|
+
- **`Math.random()` for generation** → non-reproducible, unshareable, undebuggable, desyncs multiplayer; use a seeded PRNG.
|
|
110
|
+
- **String seed used directly** → NaN/garbage; hash to int (xmur3) first.
|
|
111
|
+
- **Shared single RNG stream across systems** → tweaking one system reshuffles all; use separate streams.
|
|
112
|
+
- **No connectivity check** → unreachable exits / trapped player; flood-fill validate & regenerate.
|
|
113
|
+
- **Forgetting to seed simplex-noise (v4)** → falls back to `Math.random()`, non-reproducible; pass your PRNG to `createNoise2D`.
|
|
114
|
+
- **Single-octave noise** → smooth but featureless; add fBm octaves.
|
|
115
|
+
- **Deriving moisture from elevation** → correlated, unrealistic biomes; use independent noise fields.
|
|
116
|
+
- **WFC with no contradiction handling** → infinite loop / crash; add restart/backtrack.
|
|
117
|
+
- **Per-frame regeneration or huge synchronous gen** → jank; generate in chunks / a web worker / on level load.
|
|
118
|
+
- **Non-deterministic chunk seeds** → seams/flicker in streamed worlds; derive chunk seed from `(worldSeed, cx, cy)`.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Defaults to apply
|
|
123
|
+
- **Every generated procgen game gets a visible/optional seed** driven by **mulberry32** (with xmur3 for string seeds), plus `randRange/randInt/pick/shuffle` helpers and **separate RNG streams** per system. Reproducibility is free QA, shareable content, and multiplayer-safe.
|
|
124
|
+
- **Terrain/organic → seeded `simplex-noise` v4 (`createNoise2D(rng)`) with fBm octaves + redistribution + island mask + independent elevation/moisture** per Red Blob Games.
|
|
125
|
+
- **Dungeons → rooms+corridors or cellular-automata caves, ALWAYS followed by a flood-fill connectivity guarantee** (regenerate on failure). Ship this validation step by default — it prevents the worst procgen bug.
|
|
126
|
+
- **Offer WFC** for tile-based levels/textures (with contradiction restart), optionally seeded by noise for macro structure. Do heavy generation in a worker/on load, not per frame.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Sources
|
|
131
|
+
- Red Blob Games — Making maps with noise functions: https://www.redblobgames.com/maps/terrain-from-noise/
|
|
132
|
+
- Red Blob Games — Noise (value/Perlin/simplex intro): https://www.redblobgames.com/articles/noise/introduction.html
|
|
133
|
+
- Red Blob Games — Procedural map/dungeon & grids index: https://www.redblobgames.com/
|
|
134
|
+
- simplex-noise (npm, v4 `createNoise2D` API): https://www.npmjs.com/package/simplex-noise , https://github.com/jwagner/simplex-noise.js
|
|
135
|
+
- mulberry32 / xmur3 seeded PRNGs (bryc's gist): https://github.com/bryc/code/blob/master/jshash/PRNGs.md
|
|
136
|
+
- Stefan Gustavson — Simplex noise demystified (PDF): https://weber.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
|
|
137
|
+
- Ken Perlin — Improving Noise (SIGGRAPH 2002): https://mrl.cs.nyu.edu/~perlin/paper445.pdf
|
|
138
|
+
- Cellular-automata cave generation (RogueBasin): https://www.roguebasin.com/index.php/Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels
|
|
139
|
+
- BSP dungeon generation (RogueBasin): https://www.roguebasin.com/index.php/Basic_BSP_Dungeon_generation
|
|
140
|
+
- Maze generation algorithms — Jamis Buck ("Buckblog") & "Mazes for Programmers": https://weblog.jamisbuck.org/2011/2/7/maze-generation-algorithm-recap
|
|
141
|
+
- Wave Function Collapse — Maxim Gumin (original): https://github.com/mxgmn/WaveFunctionCollapse ; Boris the Brave, "WFC explained": https://www.boristhebrave.com/2020/04/13/wave-function-collapse-explained/
|