summer-engine 3.7.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -13
- package/.claude-plugin/plugin.json +25 -22
- package/.codex-plugin/plugin.json +8 -14
- package/.cursor-plugin/plugin.json +7 -13
- package/.factory-plugin/plugin.json +7 -13
- package/AGENTS.md +19 -12
- package/CHANGELOG.md +112 -0
- package/GEMINI.md +3 -3
- package/README.md +111 -31
- package/assets/icon.png +0 -0
- package/commands/start.md +71 -0
- package/commands/summer.md +5 -3
- package/dist/cli/banner.js +3 -0
- package/dist/cli/commands/login.d.ts +27 -0
- package/dist/cli/commands/login.js +58 -12
- package/dist/cli/commands/ops.d.ts +47 -0
- package/dist/cli/commands/ops.js +301 -0
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.js +37 -12
- package/dist/cli/commands/skills.js +11 -7
- package/dist/cli/index.js +7 -0
- package/dist/core/agent-host.d.ts +65 -0
- package/dist/core/agent-host.js +193 -0
- package/dist/core/capabilities/agent-playbook.d.ts +3 -0
- package/dist/core/capabilities/agent-playbook.js +54 -2
- package/dist/core/capabilities/apps.d.ts +341 -0
- package/dist/core/capabilities/apps.js +353 -0
- package/dist/core/capabilities/doctor.d.ts +3 -0
- package/dist/core/capabilities/doctor.js +20 -15
- package/dist/core/capabilities/enable-plugin.d.ts +27 -0
- package/dist/core/capabilities/enable-plugin.js +75 -0
- package/dist/core/capabilities/fabricate-mesh.d.ts +2 -2
- package/dist/core/capabilities/game-task-plan.d.ts +12 -6
- package/dist/core/capabilities/game-task-plan.js +80 -5
- package/dist/core/capabilities/motion-generation.js +2 -2
- package/dist/core/capabilities/navigation/open.d.ts +6 -2
- package/dist/core/capabilities/navigation/open.js +3 -0
- package/dist/core/capabilities/publish-build.js +2 -0
- package/dist/core/capabilities/runtime-control.d.ts +4 -4
- package/dist/core/capabilities/scene-script-target.d.ts +17 -0
- package/dist/core/capabilities/scene-script-target.js +70 -0
- package/dist/core/capabilities/sfx-choices.d.ts +4 -0
- package/dist/core/capabilities/sfx-choices.js +13 -6
- package/dist/core/capabilities/tool-dispatch.d.ts +12 -3
- package/dist/core/capabilities/tool-dispatch.js +130 -46
- package/dist/core/capability-skew.js +4 -0
- package/dist/core/embeddings.d.ts +2 -0
- package/dist/core/embeddings.js +1 -1
- package/dist/core/error-codes.d.ts +62 -0
- package/dist/core/error-codes.js +97 -0
- package/dist/core/format.d.ts +2 -0
- package/dist/core/format.js +2 -1
- package/dist/core/hosted-mcp.d.ts +22 -0
- package/dist/core/hosted-mcp.js +56 -1
- package/dist/core/library-read.js +5 -0
- package/dist/core/library-search.js +2 -1
- package/dist/core/oauth.js +4 -0
- package/dist/core/ops/catalog.d.ts +41 -0
- package/dist/core/ops/catalog.js +145 -0
- package/dist/core/ops/cloud.d.ts +56 -0
- package/dist/core/ops/cloud.js +175 -0
- package/dist/core/ops/contract.d.ts +119 -0
- package/dist/core/ops/contract.js +142 -0
- package/dist/core/ops/engine-answer.d.ts +25 -0
- package/dist/core/ops/engine-answer.js +117 -0
- package/dist/core/ops/engine.d.ts +55 -0
- package/dist/core/ops/engine.js +649 -0
- package/dist/core/ops/save-files.d.ts +78 -0
- package/dist/core/ops/save-files.js +419 -0
- package/dist/core/ops/search.d.ts +9 -0
- package/dist/core/ops/search.js +55 -0
- package/dist/core/skills-registry.d.ts +16 -1
- package/dist/core/skills-registry.js +35 -2
- package/dist/core/telemetry.d.ts +32 -0
- package/dist/core/telemetry.js +38 -10
- package/dist/core/tool-profiles.d.ts +25 -0
- package/dist/core/tool-profiles.js +41 -0
- package/dist/installer/agent-config.d.ts +17 -4
- package/dist/installer/agent-config.js +22 -6
- package/dist/installer/agent-table.d.ts +3 -0
- package/dist/installer/agent-table.js +10 -7
- package/dist/installer/setup.d.ts +2 -0
- package/dist/installer/setup.js +9 -4
- package/dist/installer/version-check.d.ts +29 -9
- package/dist/installer/version-check.js +81 -28
- package/dist/mcp/command.js +7 -0
- package/dist/mcp/hosted-mount.d.ts +27 -2
- package/dist/mcp/hosted-mount.js +66 -24
- package/dist/mcp/legacy-toolset.d.ts +27 -0
- package/dist/mcp/legacy-toolset.js +43 -0
- package/dist/mcp/ops-surface.d.ts +59 -0
- package/dist/mcp/ops-surface.js +443 -0
- package/dist/mcp/server.d.ts +35 -11
- package/dist/mcp/server.js +236 -89
- package/dist/mcp/tool-results.d.ts +26 -0
- package/dist/mcp/tool-results.js +81 -0
- package/dist/mcp/tool-surface.d.ts +65 -0
- package/dist/mcp/tool-surface.js +317 -0
- package/dist/mcp/tools/app-tools.d.ts +2 -0
- package/dist/mcp/tools/app-tools.js +71 -0
- package/dist/mcp/tools/asset-tools.js +55 -53
- package/dist/mcp/tools/audit-tools.js +21 -21
- package/dist/mcp/tools/debug-tools.js +6 -6
- package/dist/mcp/tools/fabricate-tools.js +6 -6
- package/dist/mcp/tools/generate-tools.js +76 -70
- package/dist/mcp/tools/project-tools.d.ts +9 -4
- package/dist/mcp/tools/project-tools.js +16 -10
- package/dist/mcp/tools/scene-tools.js +12 -23
- package/dist/mcp/tools/script-tools.js +9 -5
- package/dist/mcp/tools/with-engine.d.ts +6 -0
- package/dist/mcp/tools/with-engine.js +19 -0
- package/gemini-extension.json +6 -12
- package/hooks/pre-commit-doctor.sh +5 -4
- package/hooks/session-start.sh +24 -28
- package/integrations/claude-desktop/README.md +1 -1
- package/integrations/copilot-jetbrains/README.md +1 -1
- package/integrations/junie/README.md +1 -1
- package/integrations/lm-studio/README.md +1 -1
- package/integrations/trae/README.md +1 -1
- package/integrations/visual-studio/README.md +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +85 -35
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/product-map/resource.yaml +2 -2
- package/library/skills/3d-lighting/SKILL.md +53 -28
- package/library/skills/3d-lighting/resource.yaml +1 -1
- package/library/skills/README.md +126 -116
- package/library/skills/adaptive-music/SKILL.md +19 -23
- package/library/skills/adaptive-music/resource.yaml +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/agent-playtesting/resource.yaml +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/resource.yaml +1 -1
- package/library/skills/ambient-bed/SKILL.md +22 -13
- package/library/skills/ambient-bed/resource.yaml +1 -1
- package/library/skills/animated-loop/SKILL.md +41 -32
- package/library/skills/animated-loop/resource.yaml +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/animation-tree/resource.yaml +1 -1
- package/library/skills/art-direction/SKILL.md +3 -3
- package/library/skills/art-direction/resource.yaml +1 -1
- package/library/skills/asset-strategy/SKILL.md +21 -4
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +10 -10
- package/library/skills/audio-direction/resource.yaml +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/resource.yaml +1 -1
- package/library/skills/bounded-engine-runs/SKILL.md +1 -1
- package/library/skills/bounded-engine-runs/resource.yaml +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorm-game/resource.yaml +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/brainstorming/resource.yaml +1 -1
- package/library/skills/browse-templates/SKILL.md +1 -1
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +2 -2
- package/library/skills/camera-rigs/resource.yaml +1 -1
- package/library/skills/celeste-momentum-platforming/resource.yaml +1 -0
- package/library/skills/character-animation-wiring/SKILL.md +4 -4
- package/library/skills/character-animation-wiring/resource.yaml +1 -1
- package/library/skills/character-model/SKILL.md +55 -47
- package/library/skills/character-model/resource.yaml +1 -1
- package/library/skills/character-movement/SKILL.md +3 -3
- package/library/skills/character-movement/resource.yaml +1 -1
- package/library/skills/character-portrait/SKILL.md +24 -19
- package/library/skills/character-portrait/resource.yaml +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +58 -46
- package/library/skills/cinematic-cutscene/resource.yaml +1 -1
- package/library/skills/combat-basics/SKILL.md +1 -1
- package/library/skills/combat-basics/resource.yaml +1 -1
- package/library/skills/compatibility-renderer-traps/SKILL.md +1 -1
- package/library/skills/compatibility-renderer-traps/resource.yaml +1 -1
- package/library/skills/concept-art/SKILL.md +16 -12
- package/library/skills/concept-art/resource.yaml +1 -1
- package/library/skills/concept-to-level/SKILL.md +239 -0
- package/library/skills/concept-to-level/references/technical-rules.md +150 -0
- package/library/skills/concept-to-level/resource.yaml +44 -0
- package/library/skills/create-asset-sheet/SKILL.md +19 -13
- package/library/skills/create-asset-sheet/resource.yaml +1 -1
- package/library/skills/debug/SKILL.md +2 -2
- package/library/skills/debug/resource.yaml +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/resource.yaml +1 -1
- package/library/skills/design-level/SKILL.md +23 -35
- package/library/skills/design-level/resource.yaml +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-mechanic/resource.yaml +1 -1
- package/library/skills/design-npc/SKILL.md +22 -24
- package/library/skills/design-npc/resource.yaml +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/resource.yaml +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/resource.yaml +1 -1
- package/library/skills/environment-kit/SKILL.md +20 -16
- package/library/skills/environment-kit/resource.yaml +1 -1
- package/library/skills/export-and-ship/SKILL.md +6 -6
- package/library/skills/export-and-ship/resource.yaml +1 -1
- package/library/skills/fabricating-assets/SKILL.md +7 -7
- package/library/skills/fabricating-assets/resource.yaml +3 -3
- package/library/skills/facial-and-lipsync/SKILL.md +14 -15
- package/library/skills/facial-and-lipsync/resource.yaml +1 -1
- package/library/skills/fps-controller/SKILL.md +33 -41
- package/library/skills/fps-controller/resource.yaml +1 -1
- package/library/skills/game-feel/SKILL.md +8 -8
- package/library/skills/game-feel/resource.yaml +1 -1
- package/library/skills/gameskill/SKILL.md +4 -4
- package/library/skills/gameskill/resource.yaml +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/resource.yaml +1 -1
- package/library/skills/generate-motion/SKILL.md +30 -21
- package/library/skills/generate-motion/resource.yaml +1 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/godogen-visual-proof-game-generation/resource.yaml +1 -0
- package/library/skills/grow-analytics/SKILL.md +3 -3
- package/library/skills/grow-analytics/resource.yaml +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/headless-scripting/resource.yaml +1 -1
- package/library/skills/in-game-purchases/SKILL.md +1 -1
- package/library/skills/in-game-purchases/resource.yaml +1 -1
- package/library/skills/instantiate-asset-pack/SKILL.md +2 -2
- package/library/skills/instantiate-asset-pack/resource.yaml +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/resource.yaml +1 -0
- package/library/skills/make-game/SKILL.md +4 -4
- package/library/skills/make-game/references/2d-platformer.md +1 -1
- package/library/skills/make-game/references/3d-fps.md +1 -1
- package/library/skills/make-game/references/3d-horror.md +1 -1
- package/library/skills/make-game/references/3d-third-person.md +2 -2
- package/library/skills/make-game/resource.yaml +1 -1
- package/library/skills/multiplayer/SKILL.md +1 -1
- package/library/skills/multiplayer/resource.yaml +1 -1
- package/library/skills/multiplayer-movement/SKILL.md +1 -1
- package/library/skills/multiplayer-movement/resource.yaml +1 -1
- package/library/skills/multiplayer-project/SKILL.md +1 -1
- package/library/skills/multiplayer-project/resource.yaml +1 -1
- package/library/skills/multiplayer-publish/SKILL.md +8 -7
- package/library/skills/multiplayer-publish/resource.yaml +3 -3
- package/library/skills/multiplayer-state/SKILL.md +1 -1
- package/library/skills/multiplayer-state/resource.yaml +1 -1
- package/library/skills/multiplayer-testing/SKILL.md +1 -1
- package/library/skills/multiplayer-testing/resource.yaml +1 -1
- package/library/skills/music-track/SKILL.md +22 -13
- package/library/skills/music-track/resource.yaml +1 -1
- package/library/skills/navigate-summer/SKILL.md +11 -9
- package/library/skills/navigate-summer/resource.yaml +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/new-project/resource.yaml +1 -1
- package/library/skills/organic-model/SKILL.md +24 -18
- package/library/skills/organic-model/resource.yaml +1 -1
- package/library/skills/pixel-art/SKILL.md +27 -23
- package/library/skills/pixel-art/resource.yaml +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/play/resource.yaml +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/resource.yaml +1 -1
- package/library/skills/procedural-animation/SKILL.md +20 -16
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +25 -19
- package/library/skills/prop-model/resource.yaml +1 -1
- package/library/skills/psx-retro-rendering/resource.yaml +1 -0
- package/library/skills/publish-your-game/SKILL.md +16 -6
- package/library/skills/publish-your-game/resource.yaml +3 -3
- package/library/skills/realtime-wet-surfaces/resource.yaml +1 -0
- package/library/skills/release-readiness/SKILL.md +1 -1
- package/library/skills/release-readiness/resource.yaml +1 -1
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/remote-deploy/resource.yaml +1 -1
- package/library/skills/retarget/SKILL.md +23 -20
- package/library/skills/retarget/resource.yaml +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/resource.yaml +1 -1
- package/library/skills/save-load/SKILL.md +1 -1
- package/library/skills/save-load/resource.yaml +1 -1
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-composition/resource.yaml +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +2 -2
- package/library/skills/scene-hierarchy-design/resource.yaml +1 -1
- package/library/skills/scene-scripting/SKILL.md +66 -636
- package/library/skills/scene-scripting/references/ctx-api.md +184 -0
- package/library/skills/scene-scripting/references/recipes-2d-ui.md +193 -0
- package/library/skills/scene-scripting/references/recipes-3d.md +280 -0
- package/library/skills/scene-scripting/resource.yaml +4 -4
- package/library/skills/scene-to-level/SKILL.md +2 -2
- package/library/skills/scene-to-level/resource.yaml +1 -1
- package/library/skills/skill-create/resource.yaml +1 -0
- package/library/skills/skill-improve/resource.yaml +1 -0
- package/library/skills/skill-test/SKILL.md +2 -2
- package/library/skills/skill-test/resource.yaml +1 -0
- package/library/skills/skintokens-auto-rigging/resource.yaml +1 -0
- package/library/skills/skybox-panorama/SKILL.md +27 -21
- package/library/skills/skybox-panorama/resource.yaml +1 -1
- package/library/skills/sound-effect/SKILL.md +31 -20
- package/library/skills/sound-effect/resource.yaml +1 -1
- package/library/skills/spatial-placement/SKILL.md +14 -9
- package/library/skills/spatial-placement/resource.yaml +1 -1
- package/library/skills/sprite-sheet/SKILL.md +32 -27
- package/library/skills/sprite-sheet/resource.yaml +1 -1
- package/library/skills/store-art/SKILL.md +3 -3
- package/library/skills/store-art/resource.yaml +2 -2
- package/library/skills/store-listing/SKILL.md +3 -3
- package/library/skills/store-listing/resource.yaml +2 -2
- package/library/skills/summer-analytics/SKILL.md +7 -5
- package/library/skills/summer-analytics/resource.yaml +2 -2
- package/library/skills/summer-apps/SKILL.md +168 -0
- package/library/skills/summer-apps/manifest.md +57 -0
- package/library/skills/summer-apps/minimal-app.md +183 -0
- package/library/skills/summer-apps/resource.yaml +36 -0
- package/library/skills/summer-apps/sdk-reference.md +135 -0
- package/library/skills/summer-apps-design/SKILL.md +197 -0
- package/library/skills/summer-apps-design/resource.yaml +23 -0
- package/library/skills/summer-assets/SKILL.md +56 -0
- package/library/skills/summer-assets/resource.yaml +34 -0
- package/library/skills/summer-store/SKILL.md +1 -1
- package/library/skills/text-to-motion/SKILL.md +30 -23
- package/library/skills/text-to-motion/prompts-and-controls.md +5 -5
- package/library/skills/text-to-motion/resource.yaml +1 -1
- package/library/skills/text-to-motion/rigging-for-text-motion.md +2 -2
- package/library/skills/tileable-texture/SKILL.md +19 -15
- package/library/skills/tileable-texture/resource.yaml +1 -1
- package/library/skills/trailer-shot/SKILL.md +28 -24
- package/library/skills/trailer-shot/resource.yaml +1 -1
- package/library/skills/tune-performance/SKILL.md +6 -6
- package/library/skills/tune-performance/resource.yaml +1 -1
- package/library/skills/ui-basics/SKILL.md +41 -36
- package/library/skills/ui-basics/resource.yaml +1 -1
- package/library/skills/ui-graphics/SKILL.md +47 -38
- package/library/skills/ui-graphics/resource.yaml +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/use-widget-asset/resource.yaml +1 -1
- package/library/skills/using-summer/SKILL.md +7 -5
- package/library/skills/using-summer/resource.yaml +1 -1
- package/library/skills/vehicle-model/SKILL.md +65 -57
- package/library/skills/vehicle-model/resource.yaml +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verification-before-completion/resource.yaml +1 -1
- package/library/skills/verifying-scenes/SKILL.md +2 -2
- package/library/skills/verifying-scenes/resource.yaml +1 -1
- package/library/skills/vfx-dissolve/SKILL.md +2 -2
- package/library/skills/vfx-dissolve/resource.yaml +1 -1
- package/library/skills/vfx-fire/SKILL.md +15 -41
- package/library/skills/vfx-fire/resource.yaml +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/resource.yaml +1 -1
- package/library/skills/vfx-lightning/SKILL.md +7 -16
- package/library/skills/vfx-lightning/resource.yaml +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +23 -32
- package/library/skills/vfx-magic-glow/resource.yaml +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +20 -35
- package/library/skills/vfx-muzzle-flash/resource.yaml +1 -1
- package/library/skills/vfx-smoke/SKILL.md +13 -31
- package/library/skills/vfx-smoke/resource.yaml +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +12 -31
- package/library/skills/vfx-water-ripple/resource.yaml +1 -1
- package/library/skills/voice-line/SKILL.md +71 -40
- package/library/skills/voice-line/resource.yaml +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/resource.yaml +1 -1
- package/library/skills/web-compatibility-look/SKILL.md +1 -1
- package/library/skills/web-compatibility-look/resource.yaml +1 -1
- package/library/skills/world-building-3d/SKILL.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +1 -1
- package/library/skills/writing-skills/resource.yaml +1 -0
- package/library/tools/add-node/GUIDE.md +14 -0
- package/library/tools/align-distribute-3d/GUIDE.md +7 -0
- package/library/tools/app-call/resource.yaml +59 -0
- package/library/tools/app-create/resource.yaml +67 -0
- package/library/tools/app-data/resource.yaml +74 -0
- package/library/tools/app-dock/resource.yaml +59 -0
- package/library/tools/app-events/resource.yaml +68 -0
- package/library/tools/app-get/resource.yaml +46 -0
- package/library/tools/app-guide/resource.yaml +55 -0
- package/library/tools/app-list/resource.yaml +48 -0
- package/library/tools/app-page/resource.yaml +74 -0
- package/library/tools/app-publish/resource.yaml +90 -0
- package/library/tools/app-report-gap/resource.yaml +58 -0
- package/library/tools/app-screenshot/resource.yaml +103 -0
- package/library/tools/app-tools/resource.yaml +46 -0
- package/library/tools/app-update/resource.yaml +68 -0
- package/library/tools/attach-to-surface/GUIDE.md +15 -0
- package/library/tools/batch/GUIDE.md +27 -0
- package/library/tools/camera-bookmark/GUIDE.md +12 -0
- package/library/tools/capture-gameplay/GUIDE.md +3 -0
- package/library/tools/capture-gameplay/resource.yaml +3 -3
- package/library/tools/check-job/resource.yaml +1 -1
- package/library/tools/clear-console/GUIDE.md +3 -0
- package/library/tools/connect-ports/GUIDE.md +9 -0
- package/library/tools/connect-signal/GUIDE.md +14 -0
- package/library/tools/create-debug-report/GUIDE.md +10 -0
- package/library/tools/create-scene/GUIDE.md +10 -0
- package/library/tools/debug-views/GUIDE.md +7 -0
- package/library/tools/enable-plugin/GUIDE.md +3 -0
- package/library/tools/enable-plugin/resource.yaml +53 -0
- package/library/tools/export-game/GUIDE.md +3 -0
- package/library/tools/export-game/resource.yaml +3 -3
- package/library/tools/export-templates/GUIDE.md +3 -0
- package/library/tools/fabricate-3d/GUIDE.md +19 -0
- package/library/tools/frame-nodes/GUIDE.md +9 -0
- package/library/tools/frame-shot/GUIDE.md +9 -0
- package/library/tools/game-control/GUIDE.md +9 -0
- package/library/tools/game-input/GUIDE.md +9 -0
- package/library/tools/game-probe/GUIDE.md +9 -0
- package/library/tools/generate-3d/resource.yaml +3 -3
- package/library/tools/generate-motion/resource.yaml +2 -2
- package/library/tools/get-console/GUIDE.md +9 -0
- package/library/tools/get-console/resource.yaml +5 -0
- package/library/tools/get-debugger-errors/GUIDE.md +7 -0
- package/library/tools/get-debugger-errors/resource.yaml +5 -0
- package/library/tools/get-debugger-warnings/GUIDE.md +7 -0
- package/library/tools/get-debugger-warnings/resource.yaml +5 -0
- package/library/tools/get-diagnostics/GUIDE.md +13 -0
- package/library/tools/get-project-context/GUIDE.md +25 -0
- package/library/tools/get-runtime-tree/GUIDE.md +7 -0
- package/library/tools/get-runtime-tree/resource.yaml +5 -0
- package/library/tools/get-scene-tree/GUIDE.md +11 -0
- package/library/tools/get-scene-tree/resource.yaml +5 -0
- package/library/tools/get-script-errors/GUIDE.md +5 -0
- package/library/tools/grep/GUIDE.md +11 -0
- package/library/tools/import-asset/GUIDE.md +8 -0
- package/library/tools/import-asset-by-id/GUIDE.md +12 -0
- package/library/tools/import-from-url/GUIDE.md +10 -0
- package/library/tools/import-from-url-batch/GUIDE.md +3 -0
- package/library/tools/import-hdri/GUIDE.md +16 -0
- package/library/tools/input-map-bind/GUIDE.md +11 -0
- package/library/tools/inspect-asset/GUIDE.md +14 -0
- package/library/tools/inspect-asset/resource.yaml +5 -0
- package/library/tools/inspect-node/GUIDE.md +11 -0
- package/library/tools/inspect-node/resource.yaml +5 -0
- package/library/tools/inspect-resource/GUIDE.md +7 -0
- package/library/tools/inspect-resource/resource.yaml +5 -0
- package/library/tools/inspect-runtime-node/GUIDE.md +7 -0
- package/library/tools/inspect-runtime-node/resource.yaml +5 -0
- package/library/tools/instantiate-scene/GUIDE.md +12 -0
- package/library/tools/is-running/GUIDE.md +3 -0
- package/library/tools/measure/GUIDE.md +8 -0
- package/library/tools/navigation-probe/GUIDE.md +9 -0
- package/library/tools/open/resource.yaml +2 -2
- package/library/tools/open-main-scene/GUIDE.md +6 -0
- package/library/tools/open-scene/GUIDE.md +8 -0
- package/library/tools/place-adjacent/GUIDE.md +7 -0
- package/library/tools/play/GUIDE.md +13 -0
- package/library/tools/project-setting/GUIDE.md +9 -0
- package/library/tools/publish-build/resource.yaml +6 -6
- package/library/tools/raycast/GUIDE.md +7 -0
- package/library/tools/read-file/GUIDE.md +10 -0
- package/library/tools/read-library/resource.yaml +5 -0
- package/library/tools/recent-events/GUIDE.md +7 -0
- package/library/tools/remove-node/GUIDE.md +3 -0
- package/library/tools/repeat-along/GUIDE.md +5 -0
- package/library/tools/replace-node/GUIDE.md +9 -0
- package/library/tools/replace-text/GUIDE.md +5 -0
- package/library/tools/run-editor-script/GUIDE.md +16 -0
- package/library/tools/run-script/GUIDE.md +30 -0
- package/library/tools/run-script/resource.yaml +6 -3
- package/library/tools/runtime-animate/GUIDE.md +7 -0
- package/library/tools/runtime-call/GUIDE.md +7 -0
- package/library/tools/runtime-set/GUIDE.md +9 -0
- package/library/tools/runtime-spawn/GUIDE.md +9 -0
- package/library/tools/save-scene/GUIDE.md +3 -0
- package/library/tools/scene-audit/GUIDE.md +29 -0
- package/library/tools/screenshot/GUIDE.md +17 -0
- package/library/tools/select-node/GUIDE.md +3 -0
- package/library/tools/set-prop/GUIDE.md +23 -0
- package/library/tools/set-resource-property/GUIDE.md +8 -0
- package/library/tools/shot-sheet/GUIDE.md +7 -0
- package/library/tools/snap-to-surface/GUIDE.md +17 -0
- package/library/tools/snapshot-diff/GUIDE.md +7 -0
- package/library/tools/starcast/GUIDE.md +12 -0
- package/library/tools/stop/GUIDE.md +3 -0
- package/library/tools/test-placement/GUIDE.md +7 -0
- package/library/tools/ui-actions/GUIDE.md +12 -0
- package/library/tools/ui-activate/GUIDE.md +11 -0
- package/library/tools/ui-screenshot/GUIDE.md +9 -0
- package/library/tools/ui-tree/GUIDE.md +10 -0
- package/library/tools/wait-for-event/GUIDE.md +11 -0
- package/library/tools/world-snapshot/GUIDE.md +9 -0
- package/library/tools/write-file/GUIDE.md +5 -0
- package/library/tools/zoom/GUIDE.md +5 -0
- package/package.json +6 -4
- package/registry/generated/aliases.json +15 -0
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +6 -12
- package/registry/generated/index.json +1155 -408
- package/registry/generated/marketplace.claude.json +2 -13
- package/registry/generated/plugin.claude.json +25 -22
- package/registry/generated/plugin.codex.json +8 -14
- package/registry/generated/plugin.cursor.json +7 -13
- package/registry/generated/plugin.factory.json +7 -13
- package/registry/generated/server.json +34 -0
- package/registry/generated/skills-index.md +126 -116
- package/registry/generated/skills-registry.json +178 -113
- package/registry/operations/fixtures/invalid-deprecated-without-replacement.json +60 -0
- package/registry/operations/fixtures/invalid-name.json +60 -0
- package/registry/operations/fixtures/invalid-publish-without-owner.json +82 -0
- package/registry/operations/fixtures/invalid-spend-without-estimate.json +42 -0
- package/registry/operations/fixtures/invalid-unknown-field.json +61 -0
- package/registry/operations/fixtures/valid-player-facing.json +82 -0
- package/registry/operations/fixtures/valid-read.json +60 -0
- package/registry/operations/fixtures/valid-spend.json +43 -0
- package/registry/operations/legacy-names.json +1206 -0
- package/registry/schemas/allowed-hosts.json +4 -2
- package/registry/schemas/operation.schema.json +318 -0
- package/registry/schemas/skill.schema.json +4 -0
- package/CLAUDE.md +0 -7
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scene-scripting
|
|
3
|
-
description: "
|
|
3
|
+
description: "Build scenes, levels, HUDs and wiring with one editor GDScript (summer_run_script) instead of call chains."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Scene Scripting
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
`summer_run_script`
|
|
10
|
+
`summer_run_script` runs one GDScript **inside the live editor, against the scene named by `scenePath`**. When that scene is not the edited one, it is opened first (a request of its own) and the person's tab comes back after the run; if it cannot be opened, the script does not run. One script replaces a chain of `summer_add_node` / `summer_set_prop` calls, and it does what single calls cannot: loops, randomness, math, procedural geometry, reading existing nodes before deciding.
|
|
11
11
|
|
|
12
|
-
**The rule: 3 or more related
|
|
12
|
+
**The rule: 3 or more related scene edits, or ANY computed placement → one script.** A single property tweak → `summer_set_prop`. A cold project-wide batch job → `summer_run_editor_script` (below).
|
|
13
13
|
|
|
14
14
|
## The contract
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```gdscript
|
|
16
|
+
```
|
|
17
|
+
summer_run_script(scenePath="res://levels/level_01.tscn", source="""
|
|
19
18
|
func run(ctx):
|
|
20
19
|
var root = ctx.get_scene_root()
|
|
21
20
|
var node := MeshInstance3D.new()
|
|
22
21
|
node.mesh = BoxMesh.new()
|
|
23
22
|
root.add_child(node)
|
|
24
|
-
ctx.set_owner_recursive(node) # REQUIRED
|
|
23
|
+
ctx.set_owner_recursive(node) # REQUIRED: see owner rules
|
|
25
24
|
ctx.report("added", node.name)
|
|
25
|
+
ctx.save_scene()
|
|
26
|
+
""")
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
- `
|
|
29
|
-
- `
|
|
30
|
-
- `ctx.report(key, value)` — structured results back
|
|
31
|
-
- `print(...)` — captured into `output`.
|
|
29
|
+
- `scenePath` — the exact `res://` scene the script changes (from `summer_get_project_context` or the scene tree). Always pass it; without it the script runs against whichever tab happens to be open. The result's `scene_target` says whether the scene was opened and which tab came back.
|
|
30
|
+
- `source` — just the function: no `extends`, no `@tool` (missing ones are added and reported in `normalizations`).
|
|
31
|
+
- `ctx.get_scene_root()` — root node of that scene. `ctx.set_owner_recursive(node)` — stamp a created subtree with the scene-root owner. `ctx.report(key, value)` — structured results back (`reports`). `print(...)` — captured into `output`.
|
|
32
32
|
- Values are real GDScript: `Vector3(0, 10, 0)`, `Color(1, 0, 0, 1)`. The quoted variant-string convention (`"Vector3(0, 10, 0)"`) belongs to `summer_set_prop`, not to script code.
|
|
33
|
-
-
|
|
34
|
-
-
|
|
33
|
+
- `ctx` is untyped, so its results are untyped: write `var wall = ctx.add_mesh(...)`. `var wall := ctx.add_mesh(...)` does not compile ("Cannot infer the type").
|
|
34
|
+
- Saving: `ctx.save_scene()` writes the scene (after an ownership audit). Without it the change stays in the editor, unsaved.
|
|
35
|
+
- Budget: `max_seconds` default 20, clamp 5–120. The script blocks the editor between frames; on newer engines the budget is a HARD deadline (`"Summer script budget exceeded (Ns)"`, `budget_enforced: true`). Split the work into smaller scripts — never resubmit the same oversized one.
|
|
36
|
+
- Transactions: newer engines wrap the run in ONE named undo action (`undo: "action"`, the default) and roll it back on a mid-script runtime error — the result carries `rolled_back: true` and the scene is untouched. `undo: "none"` keeps partial mutations. Older engines ignore the param; treat any `errors` there as a possible half-applied mutation.
|
|
35
37
|
|
|
36
|
-
## The ctx
|
|
38
|
+
## The core ctx API
|
|
37
39
|
|
|
38
|
-
Creation helpers
|
|
40
|
+
Creation helpers **set the owner for you** and return the created node — prefer them over `new()` + `add_child` + `set_owner_recursive`. Frozen signatures:
|
|
39
41
|
|
|
40
42
|
```gdscript
|
|
41
43
|
add_node(type: String, name: String, parent: Node = null, props: Dictionary = {}) -> Node
|
|
@@ -63,183 +65,18 @@ save_scene(path: String = "") -> bool # ownership audit first; false +
|
|
|
63
65
|
frames_budget_exceeded() -> bool
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
`props`
|
|
67
|
-
|
|
68
|
-
## The geometry & authoring stdlib (geometry tier)
|
|
69
|
-
|
|
70
|
-
Newer engines extend ctx with a second tier: real geometry (booleans, lathe, sweep, terrain), mesh post-processing (smooth, decimate, UVs, mirror, collision), keyframe animation, and text shaders. Frozen signatures:
|
|
71
|
-
|
|
72
|
-
```gdscript
|
|
73
|
-
boolean(a: Node, b: Node, op: String = "union") -> MeshInstance3D
|
|
74
|
-
# op: union|difference|intersection. Manifold-exact CSG, baked to a plain
|
|
75
|
-
# ArrayMesh — no live CSG nodes remain. a and b are CONSUMED (freed) by
|
|
76
|
-
# default; pass keep_inputs: bool = true as the final param to keep them.
|
|
77
|
-
extrude_polygon(points: PackedVector2Array, depth: float, name: String,
|
|
78
|
-
parent: Node = null) -> MeshInstance3D # 2D footprint -> prism, baked
|
|
79
|
-
lathe(points: PackedVector2Array, name: String, parent: Node = null,
|
|
80
|
-
spin_degrees: float = 360.0, sides: int = 32) -> MeshInstance3D # revolve a profile, baked
|
|
81
|
-
sweep(points: PackedVector2Array, path_points: PackedVector3Array,
|
|
82
|
-
name: String, parent: Node = null) -> MeshInstance3D # profile swept along a 3D path, baked
|
|
83
|
-
# add_mesh shapes extended: torus | text (props.text: String, TextMesh)
|
|
84
|
-
set_smooth(node: Node, angle_deg: float = 30.0) -> bool # shade smooth by angle
|
|
85
|
-
terrain(size: Vector2, height: float, seed: int, name: String,
|
|
86
|
-
parent: Node = null, image_path: String = "") -> MeshInstance3D
|
|
87
|
-
# noise heightfield (or heightmap image via image_path); adds
|
|
88
|
-
# HeightMapShape3D collision under a StaticBody3D child automatically
|
|
89
|
-
decimate(node: Node, ratio: float) -> bool # simplify mesh to ~ratio of triangles
|
|
90
|
-
convex_collision(node: Node, decompose: bool = false) -> Node
|
|
91
|
-
# single convex hull, or multi-shape convex decomposition when decompose;
|
|
92
|
-
# ALL created bodies are owned — they survive the save
|
|
93
|
-
uv_planar(node: Node, axis: String = "y", scale: float = 1.0) -> bool
|
|
94
|
-
uv_box(node: Node, scale: float = 1.0) -> bool # make textures land on generated geometry
|
|
95
|
-
mirror(node: Node, axis: String = "x") -> Node # mirrored duplicate
|
|
96
|
-
animate(node: Node, property: String, keys: Array, anim_name: String = "",
|
|
97
|
-
loop: bool = false, player: AnimationPlayer = null) -> AnimationPlayer
|
|
98
|
-
# keys: [[time_s, value], ...]; gets-or-creates the AnimationPlayer and
|
|
99
|
-
# library; position/rotation/scale get dedicated 3D tracks (rotation keys
|
|
100
|
-
# are converted for you — never hand-build quaternion tracks)
|
|
101
|
-
make_shader(code: String, params: Dictionary = {}) -> ShaderMaterial
|
|
102
|
-
# builds Shader + ShaderMaterial from source and sets shader parameters;
|
|
103
|
-
# compile errors come back VERBATIM in the result (the `make_shader_errors`
|
|
104
|
-
# report entry, plus errors[]) — read them
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Conventions, same as the rest of the stdlib: creation helpers set the owner and return the created node; bad input produces a per-helper `report` entry plus a `null`/`false` return, never a crash; every baked mesh carries generated normals (and tangents where UVs exist). On an older engine these helpers are missing — the CSG-node fallback recipe below still works everywhere.
|
|
108
|
-
|
|
109
|
-
## The animation stdlib (animation tier)
|
|
110
|
-
|
|
111
|
-
Character-animation USAGE on top of `animate()` — state machines, method tracks, bone poses, head tracking. Same conventions. Frozen signatures:
|
|
112
|
-
|
|
113
|
-
```gdscript
|
|
114
|
-
animate(...) v2 — additive extensions, signature unchanged:
|
|
115
|
-
# keys entries also accept {time, value, interpolation: "nearest"|"linear"|"cubic"}
|
|
116
|
-
# property may target bones: "<skeleton_node>:<bone_name>/position|rotation|scale"
|
|
117
|
-
# -> creates bone position_3d/rotation_3d/scale_3d tracks (path "Skeleton:bone")
|
|
118
|
-
animate_method(node: Node, calls: Array, anim_name: String = "",
|
|
119
|
-
loop: bool = false, player: AnimationPlayer = null) -> AnimationPlayer
|
|
120
|
-
# calls: [[time_s, method_name, args_array], ...] -> one method-call track
|
|
121
|
-
anim_state_machine(target: Node, spec: Dictionary,
|
|
122
|
-
player: AnimationPlayer = null) -> AnimationTree
|
|
123
|
-
# spec: { states: {name: clip_name}, transitions: [[from, to, {auto?: bool,
|
|
124
|
-
# blend_s?: float}], ...], start: name }
|
|
125
|
-
# get-or-create AnimationTree wired to the (found or given) player,
|
|
126
|
-
# AnimationNodeStateMachine, active=true; unknown clip names -> report
|
|
127
|
-
# entry listing the player's actual clips (never silent)
|
|
128
|
-
bone_pose(skeleton: Node, bone: String, pose: Dictionary) -> bool
|
|
129
|
-
# pose keys position/rotation/scale -> set_bone_pose_* on Skeleton3D;
|
|
130
|
-
# unknown bone -> false + report listing bone names (capped 64)
|
|
131
|
-
look_at_modifier(node: Node, target: Node, props: Dictionary = {}) -> Node
|
|
132
|
-
# LookAtModifier3D under the skeleton/node, owned
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Blend shapes key through plain `animate()` with property `"blend_shapes/<name>"` (value tracks) — no separate helper. The end-to-end recipe (inspect an imported rig's real clips/bones, locomotion wiring, footstep method tracks, root motion, the raw-GDScript fallback for engines without these helpers) lives in `character-animation-wiring` — one hop, not duplicated here.
|
|
136
|
-
|
|
137
|
-
## The game-completeness stdlib (game-completeness tier: 2D, UI, gameplay code)
|
|
138
|
-
|
|
139
|
-
Everything a script needs to produce a GAME, not just a 3D scene: 2D levels, Control trees, persisted signal connections, attached scripts, prefabs, project settings. Same conventions (owner-by-default, return the node, `prop_warnings`, structured report entries on failure — never silent). 2D helpers work in any edited scene (root may be `Node2D` or `Node`); Control helpers parent under the edited root unless a parent is given. Frozen signatures:
|
|
140
|
-
|
|
141
|
-
```gdscript
|
|
142
|
-
# 2D
|
|
143
|
-
add_sprite(texture_path: String, name: String, parent: Node = null,
|
|
144
|
-
props: Dictionary = {}) -> Sprite2D # missing texture -> report + null
|
|
145
|
-
add_animated_sprite(frames: Dictionary, name: String, parent: Node = null,
|
|
146
|
-
autoplay: String = "") -> AnimatedSprite2D
|
|
147
|
-
# frames: { anim_name: { frames: [texture_paths], fps: float, loop: bool } } -> SpriteFrames
|
|
148
|
-
add_tilemap(tileset_path: String, name: String, parent: Node = null) -> TileMapLayer
|
|
149
|
-
paint_tiles(layer: TileMapLayer, cells: Array, source_id: int,
|
|
150
|
-
atlas_coords: Vector2i = Vector2i.ZERO) -> int # cells: [Vector2i]; returns count
|
|
151
|
-
paint_rect(layer: TileMapLayer, rect: Rect2i, source_id: int,
|
|
152
|
-
atlas_coords: Vector2i = Vector2i.ZERO) -> int
|
|
153
|
-
add_body_2d(kind: String, name: String, parent: Node = null,
|
|
154
|
-
shape: Dictionary = {}, props: Dictionary = {}) -> Node
|
|
155
|
-
# kind: static|rigid|character|area ; shape: {type: rect|circle|capsule, size|radius|height}
|
|
156
|
-
# -> body + owned CollisionShape2D child. Empty shape = a 32x32 rect (confessed in
|
|
157
|
-
# prop_warnings); size may be a float (square); props try the body, then the shape
|
|
158
|
-
add_camera_2d(position: Vector2, zoom: Vector2 = Vector2.ONE, make_current: bool = true,
|
|
159
|
-
limits: Rect2i = Rect2i()) -> Camera2D # empty limits = unlimited
|
|
160
|
-
# Camera2D has no saved "current" flag: make_current persists as `enabled` (+ a live
|
|
161
|
-
# make_current()); make_current: false saves the camera disabled
|
|
162
|
-
add_parallax(layers: Array, name: String, parent: Node = null) -> Node
|
|
163
|
-
# layers: [{texture: path, motion_scale: Vector2, repeat: Vector2i}] -> one Parallax2D per
|
|
164
|
-
# layer under one Node2D group; repeat -> repeat_size. Parallax2D is in the engine tree
|
|
165
|
-
# (no ParallaxBackground fallback); a layer whose texture fails to load is skipped + reported
|
|
166
|
-
|
|
167
|
-
# UI (Control)
|
|
168
|
-
add_ui(kind: String, name: String, parent: Node = null, props: Dictionary = {}) -> Control
|
|
169
|
-
# kind: label|button|panel|vbox|hbox|grid|margin|texture_rect|progress_bar|line_edit|
|
|
170
|
-
# rich_text|color_rect|center ; panel -> PanelContainer (container semantics for HUD
|
|
171
|
-
# layouts; a plain Panel is add_node("Panel", ...)). props.anchor: full_rect|center|
|
|
172
|
-
# top_left|top_right|bottom_left|bottom_right|top_wide|bottom_wide (+ the other
|
|
173
|
-
# Control::PRESET_* names) -> set_anchors_and_offsets_preset, applied BEFORE the other
|
|
174
|
-
# props so an explicit size/position wins; props.text/custom_minimum_size/etc via set();
|
|
175
|
-
# props.texture as a "res://" path is loaded for you. A Control parented under a Node2D
|
|
176
|
-
# gets a prop_warning (it scrolls with the camera — HUDs go under add_canvas_layer)
|
|
177
|
-
add_canvas_layer(name: String, layer: int = 1, parent: Node = null) -> CanvasLayer
|
|
178
|
-
set_theme_overrides(control: Control, overrides: Dictionary) -> int
|
|
179
|
-
# {font_size: int, font_color: Color, <theme_item>: value} -> add_theme_*_override; count applied
|
|
180
|
-
# dispatch by value: Color or "#hex" -> color; int/float -> font_size when the key is/ends with
|
|
181
|
-
# font_size, else constant; StyleBox/Font/Texture2D (or a "res://" path) -> style/font/icon.
|
|
182
|
-
# An item the control's theme type does not define is stored but confessed in prop_warnings
|
|
183
|
-
connect_signal(emitter: Node, signal_name: String, target: Node, method: String,
|
|
184
|
-
binds: Array = []) -> bool # PERSIST flag so it saves in .tscn;
|
|
185
|
-
# unknown signal/method -> report, false
|
|
186
|
-
# NOT `connect` — a ctx method of that name would shadow Object.connect on the ctx object,
|
|
187
|
-
# so nothing named `connect` is bound. Method gate: target.has_method, else its script
|
|
188
|
-
# has_method, else the script SOURCE declares `func <method>(` (attached this run, not yet
|
|
189
|
-
# compiled). Already connected -> true (+ already_connected in the report). Both ends must
|
|
190
|
-
# be owned by the edited root for the .tscn to carry the connection (prop_warning otherwise)
|
|
191
|
-
|
|
192
|
-
# Gameplay-code lane
|
|
193
|
-
attach_script(node: Node, source: String, path: String = "") -> Script
|
|
194
|
-
# compile-validate first (GDScriptLanguage::validate; parse errors -> `attach_script_errors`
|
|
195
|
-
# report entry {node, path, errors: [{line, column, message}]}, null, nothing written);
|
|
196
|
-
# path empty -> res://scripts/<SceneName>/<NodeName>.gd (dirs created; the root node's name
|
|
197
|
-
# when the scene was never saved, prop_warning); writes the file, FS rescan, set_script.
|
|
198
|
-
# An existing file at path IS overwritten; its previous sha is recorded in the report (the
|
|
199
|
-
# pre-run checkpoint covers rollback). Gate: the script's native base (`extends`) must be an
|
|
200
|
-
# ancestor of the node's class — else the file is written but NOT attached (report, null)
|
|
201
|
-
make_prefab(node: Node, path: String, replace_with_instance: bool = true) -> Node
|
|
202
|
-
# pack the subtree to res:// path as a PackedScene (owner fix-up first), save; when
|
|
203
|
-
# replace_with_instance the node is swapped for an instance of the new file (same
|
|
204
|
-
# name/transform/parent; outgoing persisted connections re-established) and the instance
|
|
205
|
-
# is returned; else returns node. REFUSES the scene root and a path open in an editor tab.
|
|
206
|
-
# The node's own position is zeroed for the pack (prefab authored at its origin, like the
|
|
207
|
-
# editor's Save Branch) and restored / copied onto the instance; rotation and scale stay in
|
|
208
|
-
# the prefab. A failure AFTER the file was written keeps + returns the original node (report)
|
|
209
|
-
add_autoload(name: String, path: String) -> bool # ProjectSettings autoload/<name>="*res://…"
|
|
210
|
-
# same gates as the editor's autoload dock: valid identifier, no engine/global-class
|
|
211
|
-
# collision, existing res:// path to a Script or PackedScene
|
|
212
|
-
add_input_action(action: String, events: Array) -> bool
|
|
213
|
-
# events: [{type: key, keycode: String|int} | {type: mouse_button, button_index: int} |
|
|
214
|
-
# {type: joypad_button, button_index: int} | {type: joypad_axis, axis:int, value:float}]
|
|
215
|
-
# writes input/<action> = {deadzone, events} in ProjectSettings + saves; every event is
|
|
216
|
-
# built BEFORE anything is written — unknown shape -> report, false, nothing half-written.
|
|
217
|
-
# New action: deadzone = the engine default 0.2. Existing action: keeps its deadzone and
|
|
218
|
-
# gains only the events it does not already match. Keycode strings go through
|
|
219
|
-
# find_keycode ("Space", "Left", "Ctrl+S" — modifiers become the event's modifier flags)
|
|
220
|
-
set_main_scene(path: String) -> bool # application/run/main_scene + save
|
|
221
|
-
# path must exist and be a PackedScene
|
|
222
|
-
# Project settings are written in the res:// PATH form ("*res://…", "res://…"); the editor UI
|
|
223
|
-
# writes uid:// forms — both resolve. Do not "fix" one into the other.
|
|
224
|
-
```
|
|
68
|
+
`props` apply via `set()` per key; unknown keys are collected into a `prop_warnings` report entry, never silently dropped — read it. On an **older engine** a missing helper is a plain GDScript error (`Invalid call to method 'add_mesh'…`): fall back to the manual form, which works everywhere.
|
|
225
69
|
|
|
226
|
-
|
|
70
|
+
Newer engines add three more tiers. Their frozen signatures, conventions and fallbacks are in `references/ctx-api.md` (read it with `summer_docs_read` id `skill/scene-scripting/references/ctx-api.md`):
|
|
227
71
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
| `add_sprite` / `add_animated_sprite` / `add_body_2d` / `add_camera_2d` / `add_parallax` | manual `.new()` + `add_child` + `ctx.set_owner_recursive` |
|
|
232
|
-
| `add_ui` / `add_canvas_layer` / `set_theme_overrides` | `.new()` + `set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)` + `add_theme_constant_override(...)`, owned as above — or the `ui-basics` skill's `summer_add_node` / `summer_set_prop` path |
|
|
233
|
-
| `connect_signal` | `summer_connect_signal` (persists in the `.tscn`); a raw `emitter.pressed.connect(...)` inside the run script does NOT persist |
|
|
234
|
-
| `attach_script` | `summer_write_file` the source, `summer_get_script_errors` on it, then `node.set_script(load(path))` in a script |
|
|
235
|
-
| `make_prefab` | `PackedScene.new().pack(node)` + `ResourceSaver.save(...)` (pack includes only OWNED descendants — owner rules again), then `summer_instantiate_scene` |
|
|
236
|
-
| `add_input_action` / `add_autoload` / `set_main_scene` | `summer_input_map_bind`; `summer_project_setting` (`autoload/<Name>`, `application/run/main_scene`) |
|
|
72
|
+
- **Geometry:** `boolean`, `extrude_polygon`, `lathe`, `sweep`, `terrain`, `set_smooth`, `decimate`, `convex_collision`, `uv_planar` / `uv_box`, `mirror`, `animate`, `make_shader`; `add_mesh` also takes `torus` and `text`.
|
|
73
|
+
- **Animation:** `animate_method`, `anim_state_machine`, `bone_pose`, `look_at_modifier` (the full recipe lives in `character-animation-wiring`).
|
|
74
|
+
- **Game completeness (2D, UI, gameplay code):** `add_sprite`, `add_animated_sprite`, `add_tilemap`, `paint_tiles` / `paint_rect`, `add_body_2d`, `add_camera_2d`, `add_parallax`, `add_ui`, `add_canvas_layer`, `set_theme_overrides`, `connect_signal`, `attach_script`, `make_prefab`, `add_autoload`, `add_input_action`, `set_main_scene`.
|
|
237
75
|
|
|
238
|
-
**
|
|
76
|
+
**Engine posture.** Not every engine build exposes every tier. On an engine with RunSceneScript that predates a tier, a missing helper is a plain GDScript error and, under the default `undo: "action"`, the whole run rolls back. `references/ctx-api.md` has the table of what works on every engine.
|
|
239
77
|
|
|
240
78
|
## Owner rules
|
|
241
79
|
|
|
242
|
-
<EXTREMELY-IMPORTANT>
|
|
243
80
|
**Every node you create must be owned by the scene root, or it silently vanishes when the scene saves.** Descendants too — being under an owned node is NOT enough. `add_child` succeeds, the screenshot even shows it, and the saved `.tscn` is missing it.
|
|
244
81
|
|
|
245
82
|
```gdscript
|
|
@@ -247,474 +84,78 @@ root.add_child(node)
|
|
|
247
84
|
ctx.set_owner_recursive(node) # node + every descendant
|
|
248
85
|
```
|
|
249
86
|
|
|
250
|
-
|
|
251
|
-
</EXTREMELY-IMPORTANT>
|
|
87
|
+
The ctx creation helpers own what they create. For anything you build with `new()`, call `ctx.set_owner_recursive(node)` (or set `node.owner = root` on the node AND each descendant) AFTER `add_child` — owner assignment fails on a node not yet in the tree. Re-stamp the owner after any `reparent`.
|
|
252
88
|
|
|
253
89
|
## The loop
|
|
254
90
|
|
|
255
91
|
1. `summer_world_snapshot` — the structured BEFORE baseline; keep its `snapshot_id`.
|
|
256
92
|
2. `summer_api_docs` — verify every property/method name you are not certain of.
|
|
257
|
-
3. `summer_run_script` — run the script.
|
|
258
|
-
4. Read `errors`, `reports`, and `
|
|
93
|
+
3. `summer_run_script` with `scenePath` — run the script.
|
|
94
|
+
4. Read `errors`, `reports`, `rolled_back` and `scene_target` in the result — `rolled_back: true` means a runtime error undid everything (fix and re-run); on older engines / `undo: "none"`, an error can leave a partial mutation.
|
|
259
95
|
5. `summer_snapshot_diff from_id:<the id>` — the structural receipt: exactly the nodes you meant to add were added, nothing else changed, nothing vanished (an unowned node dropping on save shows up HERE).
|
|
260
96
|
6. `summer_screenshot` — LOOK at it. Use `target:"scene" framing:"camera"` when the change touched lighting, environment, or emissive materials — the preset framings render a flat substitute environment and cannot show those.
|
|
261
|
-
7. Iterate. Never claim visual success without steps 5–6 (
|
|
97
|
+
7. Iterate. Never claim visual success without steps 5–6 (a capture is a fact; describing an unseen frame is fabrication). The full perception discipline lives in `verifying-scenes`.
|
|
262
98
|
|
|
263
99
|
## Verify names with summer_api_docs — never guess
|
|
264
100
|
|
|
265
101
|
Wrong property names fail silently or throw mid-script after half the mutation applied. Before using an unfamiliar class:
|
|
266
102
|
|
|
267
103
|
```
|
|
268
|
-
summer_api_docs
|
|
269
|
-
summer_api_docs
|
|
104
|
+
summer_api_docs(class_name="CylinderMesh") # all properties/methods
|
|
105
|
+
summer_api_docs(class_name="BoxShape3D", member="size") # {type:"Vector3", default:"Vector3(1, 1, 1)"}
|
|
270
106
|
```
|
|
271
107
|
|
|
272
108
|
Entries list only members **declared on that class** — `position` lives on `Node3D`, not `MeshInstance3D`. Walk `inherits` upward when a member seems missing.
|
|
273
109
|
|
|
274
|
-
##
|
|
110
|
+
## Two examples
|
|
275
111
|
|
|
276
|
-
|
|
112
|
+
Scatter 40 trees with deterministic randomness:
|
|
277
113
|
|
|
278
|
-
```
|
|
114
|
+
```
|
|
115
|
+
summer_run_script(scenePath="res://levels/forest.tscn", source="""
|
|
279
116
|
func run(ctx):
|
|
280
|
-
var root = ctx.get_scene_root()
|
|
281
117
|
var rng := RandomNumberGenerator.new()
|
|
282
|
-
rng.seed = 12345 #
|
|
283
|
-
var source: PackedScene = load("res://props/tree.tscn")
|
|
118
|
+
rng.seed = 12345 # same seed, same forest on a re-run
|
|
284
119
|
for i in range(40):
|
|
285
|
-
var tree
|
|
286
|
-
root.add_child(tree)
|
|
287
|
-
ctx.set_owner_recursive(tree) # owns the whole instanced subtree
|
|
120
|
+
var tree = ctx.instance_scene("res://props/tree.tscn", null, "Tree%d" % i)
|
|
288
121
|
tree.position = Vector3(rng.randf_range(-20, 20), 0, rng.randf_range(-20, 20))
|
|
289
122
|
tree.rotation.y = rng.randf_range(0, TAU)
|
|
290
123
|
var s := rng.randf_range(0.8, 1.3)
|
|
291
124
|
tree.scale = Vector3(s, s, s)
|
|
292
125
|
ctx.report("scattered", 40)
|
|
126
|
+
ctx.save_scene()
|
|
127
|
+
""")
|
|
293
128
|
```
|
|
294
129
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
```gdscript
|
|
298
|
-
func run(ctx):
|
|
299
|
-
var root = ctx.get_scene_root()
|
|
300
|
-
var st := SurfaceTool.new()
|
|
301
|
-
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
|
302
|
-
st.set_normal(Vector3.UP)
|
|
303
|
-
st.add_vertex(Vector3(0, 0, 0))
|
|
304
|
-
st.add_vertex(Vector3(1, 0, 0))
|
|
305
|
-
st.add_vertex(Vector3(0, 0, 1))
|
|
306
|
-
var mesh := st.commit() # ArrayMesh
|
|
307
|
-
var mi := MeshInstance3D.new()
|
|
308
|
-
mi.mesh = mesh
|
|
309
|
-
mi.name = "GeneratedMesh"
|
|
310
|
-
root.add_child(mi)
|
|
311
|
-
ctx.set_owner_recursive(mi)
|
|
312
|
-
ctx.report("surfaces", mesh.get_surface_count())
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
For grids/heightfields, build `PackedVector3Array`s and feed `ArrayMesh.add_surface_from_arrays` directly. Call `st.generate_normals()` before `commit()` when you did not set normals by hand.
|
|
316
|
-
|
|
317
|
-
### CSG primitives (fast blockouts)
|
|
318
|
-
|
|
319
|
-
```gdscript
|
|
320
|
-
func run(ctx):
|
|
321
|
-
var root = ctx.get_scene_root()
|
|
322
|
-
var body := CSGCombiner3D.new()
|
|
323
|
-
body.name = "Blockout"
|
|
324
|
-
root.add_child(body)
|
|
325
|
-
var box := CSGBox3D.new()
|
|
326
|
-
box.size = Vector3(6, 3, 6)
|
|
327
|
-
body.add_child(box)
|
|
328
|
-
var hole := CSGCylinder3D.new()
|
|
329
|
-
hole.operation = CSGShape3D.OPERATION_SUBTRACTION
|
|
330
|
-
hole.radius = 1.0
|
|
331
|
-
hole.height = 7.0
|
|
332
|
-
body.add_child(hole)
|
|
333
|
-
ctx.set_owner_recursive(body) # owner is the SCENE root, not the parent
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
Live CSG nodes are the **older-engine fallback** — they re-evaluate every frame and should be converted before shipping (`scene-composition`). On a geometry-tier engine prefer `ctx.boolean(...)`, which bakes to a plain ArrayMesh and leaves no CSG nodes behind.
|
|
337
|
-
|
|
338
|
-
### Boolean blockout — carve a doorway (geometry tier)
|
|
339
|
-
|
|
340
|
-
Box minus box, baked. The inputs are consumed; only the result remains.
|
|
341
|
-
|
|
342
|
-
```gdscript
|
|
343
|
-
func run(ctx):
|
|
344
|
-
var wall := ctx.add_mesh("box", "Wall", null, {"size": Vector3(6, 3, 0.3)})
|
|
345
|
-
var hole := ctx.add_mesh("box", "DoorHole", null, {"size": Vector3(1.0, 2.1, 0.5)})
|
|
346
|
-
hole.position = Vector3(0, -0.45, 0) # sink the opening to floor level
|
|
347
|
-
var carved := ctx.boolean(wall, hole, "difference")
|
|
348
|
-
if carved == null:
|
|
349
|
-
return # reports carry the reason — read them
|
|
350
|
-
carved.name = "WallWithDoorway"
|
|
351
|
-
ctx.uv_box(carved) # boolean output needs UVs to take a texture
|
|
352
|
-
ctx.report("doorway", str(carved.get_path()))
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
Then `summer_snapshot_diff` must show `Wall` and `DoorHole` GONE and `WallWithDoorway` added — leftover inputs mean the boolean failed and returned null. Screenshot to check the opening is where you meant.
|
|
356
|
-
|
|
357
|
-
### Lathe a goblet / pillar (geometry tier)
|
|
358
|
-
|
|
359
|
-
`lathe` revolves a 2D profile (x = radius, y = height) around Y. Design the profile from the axis outward.
|
|
360
|
-
|
|
361
|
-
```gdscript
|
|
362
|
-
func run(ctx):
|
|
363
|
-
var profile := PackedVector2Array([
|
|
364
|
-
Vector2(0.0, 0.0), Vector2(0.45, 0.0), # foot
|
|
365
|
-
Vector2(0.08, 0.1), Vector2(0.08, 0.55), # stem
|
|
366
|
-
Vector2(0.35, 0.7), Vector2(0.4, 1.1), # bowl
|
|
367
|
-
Vector2(0.0, 1.15),
|
|
368
|
-
])
|
|
369
|
-
var goblet := ctx.lathe(profile, "Goblet", null, 360.0, 48)
|
|
370
|
-
ctx.set_smooth(goblet, 40.0) # lathes look faceted without it
|
|
371
|
-
ctx.apply_material(goblet, ctx.make_material({"albedo_color": Color(0.85, 0.7, 0.2), "metallic": 0.8, "roughness": 0.25}))
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
A square-ish profile with `sides: 8` and `spin_degrees: 360` makes a chamfered pillar; `spin_degrees: 180` makes an apse/half-dome.
|
|
375
|
-
|
|
376
|
-
### Sweep a rail / pipe (geometry tier)
|
|
377
|
-
|
|
378
|
-
`sweep` extrudes a 2D cross-section along a 3D polyline.
|
|
379
|
-
|
|
380
|
-
```gdscript
|
|
381
|
-
func run(ctx):
|
|
382
|
-
var section := PackedVector2Array([ # small circle-ish octagon, the pipe wall
|
|
383
|
-
Vector2(0.05, 0), Vector2(0.035, 0.035), Vector2(0, 0.05), Vector2(-0.035, 0.035),
|
|
384
|
-
Vector2(-0.05, 0), Vector2(-0.035, -0.035), Vector2(0, -0.05), Vector2(0.035, -0.035),
|
|
385
|
-
])
|
|
386
|
-
var path := PackedVector3Array([
|
|
387
|
-
Vector3(0, 1, 0), Vector3(4, 1, 0), Vector3(6, 1, 2), Vector3(6, 3, 6),
|
|
388
|
-
])
|
|
389
|
-
var pipe := ctx.sweep(section, path, "SteamPipe")
|
|
390
|
-
ctx.set_smooth(pipe, 45.0)
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
Rails, roads, cables, gutters — same recipe, different cross-section. Keep path points a reasonable distance apart; hairpin corners self-intersect.
|
|
394
|
-
|
|
395
|
-
### Terrain with collision (geometry tier)
|
|
396
|
-
|
|
397
|
-
```gdscript
|
|
398
|
-
func run(ctx):
|
|
399
|
-
var ground := ctx.terrain(Vector2(64, 64), 6.0, 1337, "Terrain")
|
|
400
|
-
ctx.apply_material(ground, ctx.make_material({"albedo_color": Color(0.35, 0.5, 0.25), "roughness": 1.0}))
|
|
401
|
-
ctx.report("terrain", ctx.summary())
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
Collision arrives automatically (a `StaticBody3D` + `HeightMapShape3D` child), already owned. Same seed → same terrain, so re-runs are deterministic. Pass `image_path` to drive heights from a grayscale heightmap instead of noise. Verify with `summer_screenshot framing:"camera"` — terrain reads wrong from preset framings' top-down angles.
|
|
405
|
-
|
|
406
|
-
### Text signage (geometry tier)
|
|
407
|
-
|
|
408
|
-
`add_mesh` accepts `torus` and `text` on geometry-tier engines:
|
|
409
|
-
|
|
410
|
-
```gdscript
|
|
411
|
-
func run(ctx):
|
|
412
|
-
var sign := ctx.add_mesh("text", "ExitSign", null, {"text": "EXIT", "depth": 0.08})
|
|
413
|
-
sign.position = Vector3(0, 2.6, -4)
|
|
414
|
-
ctx.apply_material(sign, ctx.make_material({"albedo_color": Color(1, 0.2, 0.2), "emission_enabled": true, "emission": Color(1, 0.1, 0.1), "emission_energy_multiplier": 3.0}))
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
Emissive text only proves itself in a `framing:"camera"` screenshot — preset framings substitute the environment and mute emission.
|
|
418
|
-
|
|
419
|
-
### Decimate for LOD, convex collision for props (geometry tier)
|
|
420
|
-
|
|
421
|
-
Dense generated/imported meshes (Meshy-class output) want both before they ship:
|
|
422
|
-
|
|
423
|
-
```gdscript
|
|
424
|
-
func run(ctx):
|
|
425
|
-
var prop := ctx.find("AncientStatue") # e.g. a generated import
|
|
426
|
-
if prop == null:
|
|
427
|
-
ctx.report("error", "AncientStatue not found")
|
|
428
|
-
return
|
|
429
|
-
ctx.decimate(prop, 0.35) # keep ~35% of the triangles
|
|
430
|
-
var body := ctx.convex_collision(prop, true) # V-HACD multi-shape for concave props
|
|
431
|
-
ctx.report("collision_body", str(body.get_path()))
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
`decimate` returns `false` (with a report entry) instead of ruining the mesh when the ratio is out of range. `convex_collision` owns every body it creates — the diff after `save_scene` is your receipt that nothing was silently dropped. Simple convex props (crates, rocks): leave `decompose` false, one hull is cheaper.
|
|
435
|
-
|
|
436
|
-
### Mirror symmetry (geometry tier)
|
|
437
|
-
|
|
438
|
-
```gdscript
|
|
439
|
-
func run(ctx):
|
|
440
|
-
var left_tower := ctx.find("Tower")
|
|
441
|
-
var right_tower := ctx.mirror(left_tower, "x") # mirrored duplicate, owned
|
|
442
|
-
right_tower.name = "TowerMirrored"
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
The mirror is a real duplicate with flipped winding/normals — edit either side independently afterward.
|
|
446
|
-
|
|
447
|
-
### Animate — camera flythrough and a door-open (geometry tier)
|
|
448
|
-
|
|
449
|
-
`animate` collapses the AnimationPlayer/library/track boilerplate into one call per property. Multiple calls with the same `anim_name` append tracks to the same clip.
|
|
450
|
-
|
|
451
|
-
```gdscript
|
|
452
|
-
func run(ctx):
|
|
453
|
-
var cam := ctx.add_camera(Vector3(0, 4, 12), Vector3.ZERO, true)
|
|
454
|
-
ctx.animate(cam, "position", [
|
|
455
|
-
[0.0, Vector3(0, 4, 12)],
|
|
456
|
-
[4.0, Vector3(8, 5, 4)],
|
|
457
|
-
[8.0, Vector3(0, 6, -10)],
|
|
458
|
-
], "flythrough")
|
|
459
|
-
ctx.animate(cam, "rotation_degrees", [
|
|
460
|
-
[0.0, Vector3(-10, 0, 0)],
|
|
461
|
-
[4.0, Vector3(-12, 60, 0)],
|
|
462
|
-
[8.0, Vector3(-15, 175, 0)],
|
|
463
|
-
], "flythrough") # same clip — second track appended
|
|
464
|
-
|
|
465
|
-
var door := ctx.find("Door")
|
|
466
|
-
ctx.animate(door, "rotation_degrees", [
|
|
467
|
-
[0.0, Vector3(0, 0, 0)],
|
|
468
|
-
[0.8, Vector3(0, 110, 0)],
|
|
469
|
-
], "open")
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
Pass plain `position` / `rotation_degrees` values — the helper picks the right track type and handles the quaternion conversion for rotation. Verify by playing: `summer_play`, trigger the clip (or set autoplay), `summer_screenshot target:"game"`, `summer_stop`.
|
|
473
|
-
|
|
474
|
-
### make_shader — dissolve / glow FX with the compile-error loop (geometry tier)
|
|
475
|
-
|
|
476
|
-
`make_shader` is the safe lane for text shaders because compile errors come back **verbatim** in the result (the `make_shader_errors` report entry, with the line number) instead of failing silently to a magenta material:
|
|
477
|
-
|
|
478
|
-
```gdscript
|
|
479
|
-
func run(ctx):
|
|
480
|
-
var mat := ctx.make_shader("""
|
|
481
|
-
shader_type spatial;
|
|
482
|
-
uniform float threshold : hint_range(0.0, 1.0) = 0.3;
|
|
483
|
-
uniform vec4 edge_color : source_color = vec4(1.0, 0.55, 0.1, 1.0);
|
|
484
|
-
void fragment() {
|
|
485
|
-
float n = fract(sin(dot(UV * 12.0, vec2(12.9898, 78.233))) * 43758.5453);
|
|
486
|
-
if (n < threshold) { discard; }
|
|
487
|
-
ALBEDO = vec3(0.8);
|
|
488
|
-
EMISSION = edge_color.rgb * smoothstep(threshold + 0.08, threshold, n) * 4.0;
|
|
489
|
-
}
|
|
490
|
-
""", {"threshold": 0.35})
|
|
491
|
-
ctx.apply_material(ctx.find("Crystal"), mat)
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
The loop when it fails: read the exact compile error (line number included) from the `make_shader_errors` report entry → fix that line → re-run. Do not guess-and-mutate; the error text names the line and identifier. For production-grade dissolve/fire/glow (FBM noise include, controllers, cookbook variants) use the `vfx-<effect>` recipes — `make_shader` is the fast lane for one-off FX and for iterating on a shader before writing it to a file.
|
|
495
|
-
|
|
496
|
-
### GridMap fills
|
|
497
|
-
|
|
498
|
-
```gdscript
|
|
499
|
-
func run(ctx):
|
|
500
|
-
var root = ctx.get_scene_root()
|
|
501
|
-
var grid: GridMap = root.get_node("GridMap") # needs mesh_library assigned
|
|
502
|
-
for x in range(16):
|
|
503
|
-
for z in range(16):
|
|
504
|
-
grid.set_cell_item(Vector3i(x, 0, z), 0) # item = MeshLibrary index
|
|
505
|
-
ctx.report("cells", 256)
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Cells serialize into packed data — scripting is the ONLY sane way to fill them; never hand-edit `tile_map_data`-style fields as text.
|
|
509
|
-
|
|
510
|
-
### 3-point lighting rig
|
|
511
|
-
|
|
512
|
-
```gdscript
|
|
513
|
-
func run(ctx):
|
|
514
|
-
var root = ctx.get_scene_root()
|
|
515
|
-
var rig := Node3D.new()
|
|
516
|
-
rig.name = "LightRig"
|
|
517
|
-
root.add_child(rig)
|
|
518
|
-
|
|
519
|
-
var key := DirectionalLight3D.new()
|
|
520
|
-
key.name = "KeyLight"
|
|
521
|
-
key.light_energy = 1.2
|
|
522
|
-
key.shadow_enabled = true
|
|
523
|
-
key.rotation_degrees = Vector3(-45, -30, 0)
|
|
524
|
-
|
|
525
|
-
var fill := OmniLight3D.new()
|
|
526
|
-
fill.name = "FillLight"
|
|
527
|
-
fill.light_energy = 0.4
|
|
528
|
-
fill.position = Vector3(-4, 2, 4)
|
|
529
|
-
|
|
530
|
-
var rim := SpotLight3D.new()
|
|
531
|
-
rim.name = "RimLight"
|
|
532
|
-
rim.light_energy = 0.8
|
|
533
|
-
rim.position = Vector3(0, 3, -5)
|
|
534
|
-
rim.rotation_degrees = Vector3(-20, 180, 0)
|
|
535
|
-
|
|
536
|
-
for light in [key, fill, rim]:
|
|
537
|
-
rig.add_child(light)
|
|
538
|
-
ctx.set_owner_recursive(rig)
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
Then `summer_screenshot` — lighting is exactly the kind of change you cannot judge without pixels.
|
|
542
|
-
|
|
543
|
-
### 2D platformer level — tilemap paint, character body, camera limits, parallax (game-completeness tier)
|
|
544
|
-
|
|
545
|
-
One script lays down the level: paint the ground, drop a character body with collision, bound the camera to the level, stack parallax behind it. Tile coordinates are cells; camera limits are pixels (cells × tile size — 32 here).
|
|
546
|
-
|
|
547
|
-
```gdscript
|
|
548
|
-
func run(ctx):
|
|
549
|
-
const TILE := 32
|
|
550
|
-
var ground := ctx.add_tilemap("res://tilesets/grass.tres", "Ground")
|
|
551
|
-
var painted := ctx.paint_rect(ground, Rect2i(0, 12, 64, 4), 0, Vector2i(0, 0)) # floor: 64 wide, 4 deep
|
|
552
|
-
painted += ctx.paint_rect(ground, Rect2i(20, 8, 6, 1), 0, Vector2i(1, 0)) # a floating platform
|
|
553
|
-
painted += ctx.paint_tiles(ground, [Vector2i(10, 11), Vector2i(11, 11), Vector2i(12, 11)], 0, Vector2i(2, 0)) # a step
|
|
554
|
-
ctx.report("tiles_painted", painted)
|
|
555
|
-
|
|
556
|
-
var player := ctx.add_body_2d("character", "Player", null,
|
|
557
|
-
{"type": "capsule", "radius": 14.0, "height": 60.0},
|
|
558
|
-
{"position": Vector2(3 * TILE, 11 * TILE)})
|
|
559
|
-
ctx.add_sprite("res://sprites/player_idle.png", "Sprite", player)
|
|
560
|
-
|
|
561
|
-
var cam := ctx.add_camera_2d(player.position, Vector2(2, 2), true, Rect2i(0, 0, 64 * TILE, 16 * TILE))
|
|
562
|
-
cam.reparent(player, false) # follow the body...
|
|
563
|
-
cam.position = Vector2.ZERO
|
|
564
|
-
ctx.set_owner_recursive(cam) # ...and re-stamp the owner after ANY reparent
|
|
565
|
-
|
|
566
|
-
ctx.add_parallax([
|
|
567
|
-
{"texture": "res://backgrounds/sky.png", "motion_scale": Vector2(0.1, 0.1), "repeat": Vector2i(1024, 0)},
|
|
568
|
-
{"texture": "res://backgrounds/hills.png", "motion_scale": Vector2(0.4, 0.4), "repeat": Vector2i(1024, 0)},
|
|
569
|
-
], "Background")
|
|
570
|
-
ctx.report("level", ctx.summary())
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
Compare `tiles_painted` with the cell areas you asked for (256 + 6 + 3 here) — a short count means cells were skipped. Then `summer_snapshot_diff`: `added` must list `Ground`, `Player` **with its `CollisionShape2D` child**, the camera under `Player`, and the `Background` group with one `Parallax2D` per layer. Screenshot `target:"scene" nodePath:"Player"` to see the body standing on the floor row, then the whole scene to see the platform is reachable. Movement needs the controller script and the input actions — two recipes below. Want a working baseline instead of a blank scene? The `2d-platformer` template already has tilemap + `CharacterBody2D` + parallax wired.
|
|
574
|
-
|
|
575
|
-
### Animated sprite (game-completeness tier)
|
|
576
|
-
|
|
577
|
-
`add_animated_sprite` builds the `SpriteFrames` resource from per-frame texture paths — one entry per animation, `fps` and `loop` per clip — and optionally autoplays one. Cut a sheet into frames first (`summer_slice_asset_sheet`, or the `sprite-sheet` skill when generating them).
|
|
578
|
-
|
|
579
|
-
```gdscript
|
|
580
|
-
func run(ctx):
|
|
581
|
-
var player := ctx.find("Player")
|
|
582
|
-
if player == null:
|
|
583
|
-
ctx.report("error", "Player not found")
|
|
584
|
-
return
|
|
585
|
-
var anim := ctx.add_animated_sprite({
|
|
586
|
-
"idle": {"frames": ["res://sprites/player/idle_0.png", "res://sprites/player/idle_1.png"], "fps": 4.0, "loop": true},
|
|
587
|
-
"run": {"frames": ["res://sprites/player/run_0.png", "res://sprites/player/run_1.png",
|
|
588
|
-
"res://sprites/player/run_2.png", "res://sprites/player/run_3.png"], "fps": 12.0, "loop": true},
|
|
589
|
-
"jump": {"frames": ["res://sprites/player/jump_0.png"], "fps": 1.0, "loop": false},
|
|
590
|
-
}, "Anim", player, "idle")
|
|
591
|
-
ctx.report("animations", anim.sprite_frames.get_animation_names())
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
A missing texture path comes back as a report entry — read it before wondering why a clip is short. Which clip plays when is gameplay code (`anim.play("run")` from the controller's `_physics_process`), not scene setup. Verify by playing: `summer_play`, move, `summer_screenshot target:"game"` — an edit-time preview shows frame 0 only.
|
|
595
|
-
|
|
596
|
-
### HUD — canvas layer, margin, vbox, label/progress/button, anchors, theme overrides (game-completeness tier)
|
|
130
|
+
A key/fill/rim lighting rig:
|
|
597
131
|
|
|
598
|
-
```gdscript
|
|
599
|
-
func run(ctx):
|
|
600
|
-
var hud := ctx.add_canvas_layer("HUD", 1)
|
|
601
|
-
var margin := ctx.add_ui("margin", "Margin", hud, {"anchor": "full_rect"})
|
|
602
|
-
ctx.set_theme_overrides(margin, {"margin_left": 24, "margin_top": 24, "margin_right": 24, "margin_bottom": 24})
|
|
603
|
-
|
|
604
|
-
var column := ctx.add_ui("vbox", "TopLeft", margin)
|
|
605
|
-
var score := ctx.add_ui("label", "Score", column, {"text": "Score: 0"})
|
|
606
|
-
ctx.set_theme_overrides(score, {"font_size": 28, "font_color": Color(1, 1, 1)})
|
|
607
|
-
var health := ctx.add_ui("progress_bar", "Health", column,
|
|
608
|
-
{"min_value": 0, "max_value": 100, "value": 100, "show_percentage": false,
|
|
609
|
-
"custom_minimum_size": Vector2(240, 18)})
|
|
610
|
-
|
|
611
|
-
var pause := ctx.add_ui("button", "PauseButton", hud, {"text": "Pause", "anchor": "top_right"})
|
|
612
|
-
pause.process_mode = Node.PROCESS_MODE_ALWAYS # still clickable while the tree is paused
|
|
613
|
-
ctx.report("hud", ctx.summary())
|
|
614
132
|
```
|
|
615
|
-
|
|
616
|
-
`props.anchor` runs `set_anchors_and_offsets_preset` — the right way to pin a Control; hand-set `anchor_*`/`offset_*` values drift the moment the window resizes. The preset is applied before the other props, so an explicit `size`/`position` in the same dict wins. `add_ui("panel", …)` is a **PanelContainer** (it sizes to its child — a backdrop for a vbox/hbox); a plain `Panel` is `add_node("Panel", …)`. `set_theme_overrides` returns how many overrides it applied — compare with what you passed (an `int` on a key that is not `font_size` becomes a *constant* override, as with the margins above). Unknown `props` keys land in `prop_warnings` as always (use real property names: `custom_minimum_size`, not `min_size` or a guess). HUD Controls belong under a `CanvasLayer` — a Control parented under a `Node2D` scrolls with the camera and gets a `prop_warning`. For the layout and theme *design* — which container for which job, theme vs inline styling — use `ui-basics`; this is its one-script form.
|
|
617
|
-
|
|
618
|
-
### Wire a button with connect_signal() (game-completeness tier)
|
|
619
|
-
|
|
620
|
-
`ctx.connect_signal` makes a **persisted** connection (`CONNECT_PERSIST`) — it survives in the `.tscn`, exactly what the editor's ConnectionsDock writes. The target needs a method to call, so attach the script first; a script attached in the same run has not compiled yet, and `connect_signal` accepts it because the source declares `func _on_pause_pressed(` (the method gate):
|
|
621
|
-
|
|
622
|
-
```gdscript
|
|
133
|
+
summer_run_script(scenePath="res://main.tscn", source="""
|
|
623
134
|
func run(ctx):
|
|
624
|
-
var
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
func _on_pause_pressed() -> void:
|
|
630
|
-
get_tree().paused = not get_tree().paused
|
|
631
|
-
""") == null:
|
|
632
|
-
return # parse errors are in the report — fix, re-run
|
|
633
|
-
if not ctx.connect_signal(button, "pressed", hud, "_on_pause_pressed"):
|
|
634
|
-
return # the report names the unknown signal or method
|
|
635
|
-
ctx.report("wired", "PauseButton.pressed -> HUD._on_pause_pressed")
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
Never wire with a bare `button.pressed.connect(...)` inside the run script: that connection lives only in the editor process and is not saved. And the helper is `connect_signal`, not `connect` — `ctx.connect(...)` is `Object.connect` on the ctx object itself and fails with a wrong-signature error. `true` from `ctx.connect_signal` says the connection was made and flagged persistent (an already-connected pair also returns `true`, with `already_connected` in the report); both ends must be owned by the edited root or the `.tscn` cannot carry it. The proof is `summer_play`, click, `summer_screenshot target:"game"`.
|
|
639
|
-
|
|
640
|
-
### attach_script — behavior with the parse-error loop (game-completeness tier)
|
|
641
|
-
|
|
642
|
-
`attach_script` validates the source **before** writing anything: a parse error comes back as a report entry (with the line) and the return is `null` — no half-written file, no broken node. Empty `path` → `res://scripts/<SceneName>/<NodeName>.gd`.
|
|
643
|
-
|
|
644
|
-
```gdscript
|
|
645
|
-
func run(ctx):
|
|
646
|
-
var player := ctx.find("Player")
|
|
647
|
-
var script := ctx.attach_script(player, """
|
|
648
|
-
extends CharacterBody2D
|
|
649
|
-
|
|
650
|
-
const SPEED := 220.0
|
|
651
|
-
const JUMP_VELOCITY := -420.0
|
|
652
|
-
|
|
653
|
-
@onready var anim: AnimatedSprite2D = $Anim
|
|
654
|
-
|
|
655
|
-
func _physics_process(delta: float) -> void:
|
|
656
|
-
if not is_on_floor():
|
|
657
|
-
velocity += get_gravity() * delta
|
|
658
|
-
if Input.is_action_just_pressed("jump") and is_on_floor():
|
|
659
|
-
velocity.y = JUMP_VELOCITY
|
|
660
|
-
var dir := Input.get_axis("move_left", "move_right")
|
|
661
|
-
velocity.x = dir * SPEED
|
|
662
|
-
if dir != 0.0:
|
|
663
|
-
anim.flip_h = dir < 0.0
|
|
664
|
-
anim.play("jump" if not is_on_floor() else ("run" if dir != 0.0 else "idle"))
|
|
665
|
-
move_and_slide()
|
|
135
|
+
var rig = ctx.add_node("Node3D", "LightRig")
|
|
136
|
+
ctx.add_node("DirectionalLight3D", "KeyLight", rig, {"light_energy": 1.2, "shadow_enabled": true, "rotation_degrees": Vector3(-45, -30, 0)})
|
|
137
|
+
ctx.add_node("OmniLight3D", "FillLight", rig, {"light_energy": 0.4, "position": Vector3(-4, 2, 4)})
|
|
138
|
+
ctx.add_node("SpotLight3D", "RimLight", rig, {"light_energy": 0.8, "position": Vector3(0, 3, -5), "rotation_degrees": Vector3(-20, 180, 0)})
|
|
139
|
+
ctx.save_scene()
|
|
666
140
|
""")
|
|
667
|
-
if script == null:
|
|
668
|
-
return # read the report: line + message; fix THAT line; re-run
|
|
669
|
-
ctx.report("script", script.resource_path)
|
|
670
141
|
```
|
|
671
142
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
### make_prefab — turn a built subtree into a reusable .tscn (game-completeness tier)
|
|
675
|
-
|
|
676
|
-
Build the thing once in-scene, then pack it. With `replace_with_instance` (the default) the inline subtree is swapped for an instance of the new file — same name, transform, parent — and the instance is returned.
|
|
677
|
-
|
|
678
|
-
```gdscript
|
|
679
|
-
func run(ctx):
|
|
680
|
-
var torch := ctx.find("Torch") # Sprite2D + PointLight2D + Area2D you built above
|
|
681
|
-
if torch == null:
|
|
682
|
-
return
|
|
683
|
-
var first := ctx.make_prefab(torch, "res://prefabs/torch.tscn")
|
|
684
|
-
for i in range(3):
|
|
685
|
-
var t := ctx.instance_scene("res://prefabs/torch.tscn", null, "Torch%d" % (i + 2))
|
|
686
|
-
t.position = first.position + Vector2(160 * (i + 1), 0)
|
|
687
|
-
ctx.report("prefab", first.scene_file_path)
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
The owner fix-up runs before packing, so a child you forgot to own is included instead of silently dropped. The prefab is authored at its origin — the node's position is zeroed for the pack (the editor's Save Branch default) and copied back onto the instance, so `first.position` above is still where the torch stood; rotation and scale stay in the prefab. `make_prefab` refuses the scene root (pack a child subtree, never `ctx.get_scene_root()`) and a path that is open in an editor tab. The receipt: `res://prefabs/torch.tscn` in the result's `files_written`, `Torch` now reporting a `scene_file_path`, and the copies in the diff's `added`. From here on, edit the prefab file, not the instances.
|
|
691
|
-
|
|
692
|
-
### Input actions, autoload, main scene (game-completeness tier)
|
|
693
|
-
|
|
694
|
-
Project-level wiring in one run — the platformer controller above already reads these actions:
|
|
695
|
-
|
|
696
|
-
```gdscript
|
|
697
|
-
func run(ctx):
|
|
698
|
-
ctx.add_input_action("move_left", [{"type": "key", "keycode": "A"}, {"type": "key", "keycode": "Left"},
|
|
699
|
-
{"type": "joypad_axis", "axis": 0, "value": -1.0}])
|
|
700
|
-
ctx.add_input_action("move_right", [{"type": "key", "keycode": "D"}, {"type": "key", "keycode": "Right"},
|
|
701
|
-
{"type": "joypad_axis", "axis": 0, "value": 1.0}])
|
|
702
|
-
ctx.add_input_action("jump", [{"type": "key", "keycode": "Space"}, {"type": "joypad_button", "button_index": 0}])
|
|
703
|
-
ctx.add_autoload("GameState", "res://scripts/autoload/game_state.gd") # the file must exist — write it first
|
|
704
|
-
if ctx.save_scene("res://levels/level_01.tscn"):
|
|
705
|
-
ctx.set_main_scene("res://levels/level_01.tscn")
|
|
706
|
-
```
|
|
143
|
+
Then `summer_screenshot` — lighting is exactly the kind of change you cannot judge without pixels.
|
|
707
144
|
|
|
708
|
-
|
|
145
|
+
More recipes, each the `source` of one call:
|
|
709
146
|
|
|
710
|
-
|
|
147
|
+
- `references/recipes-3d.md` (`skill/scene-scripting/references/recipes-3d.md`): procedural meshes, CSG and boolean blockouts, lathe, sweep, terrain, text signage, decimate and convex collision, mirror, keyframe animation, `make_shader`, GridMap fills.
|
|
148
|
+
- `references/recipes-2d-ui.md` (`skill/scene-scripting/references/recipes-2d-ui.md`): a 2D platformer level, animated sprites, a HUD, a persisted button connection, `attach_script`, `make_prefab`, input actions, autoloads and the main scene, and how to verify 2D and UI work.
|
|
711
149
|
|
|
712
|
-
|
|
150
|
+
## Which tool
|
|
713
151
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
152
|
+
| Job | Tool |
|
|
153
|
+
|---|---|
|
|
154
|
+
| One property on one node | `summer_set_prop` |
|
|
155
|
+
| One signal, one input binding, one project setting | `summer_connect_signal`, `summer_input_map_bind`, `summer_project_setting` |
|
|
156
|
+
| 3+ related scene edits, or anything computed | `summer_run_script` with `scenePath` |
|
|
157
|
+
| Many kit pieces at exact poses, read back as one receipt | `summer_batch` with placed `InstantiateScene` ops (`spatial-placement`) |
|
|
158
|
+
| Project-wide batch work on files on disk | `summer_run_editor_script` |
|
|
718
159
|
|
|
719
160
|
## summer_run_editor_script — the cold path
|
|
720
161
|
|
|
@@ -724,41 +165,30 @@ A different tool for a different job: it boots a **fresh headless child editor a
|
|
|
724
165
|
- Use for batch/project-wide work: re-saving every scene, sweeping resources, mass fixes, generating `.tres` assets.
|
|
725
166
|
- Budget default 120s, clamp 15–600 — include boot time (30s+ on large projects).
|
|
726
167
|
- It confesses `no_rewind_point:true` when no pre-run checkpoint could be taken — surface that to the user before more destructive work.
|
|
727
|
-
- No renderer: it can never screenshot. Judge it by artifacts on disk (see `headless-scripting`
|
|
168
|
+
- No renderer: it can never screenshot. Judge it by artifacts on disk (see `headless-scripting`).
|
|
728
169
|
|
|
729
|
-
If `summer_run_script`
|
|
170
|
+
If `summer_run_script` answers `engine_lacks_op`, the engine build predates RunSceneScript (Summer Engine 0.5.66 or newer has it): update Summer Engine, or use `summer_run_editor_script` for the same work.
|
|
730
171
|
|
|
731
|
-
##
|
|
172
|
+
## Pitfalls
|
|
732
173
|
|
|
733
|
-
|
|
|
174
|
+
| Pitfall | Reality |
|
|
734
175
|
|---|---|
|
|
735
176
|
| Ten `summer_add_node`/`summer_set_prop` calls in a row | That is one script. Write the script. |
|
|
736
|
-
|
|
|
737
|
-
|
|
|
177
|
+
| A script without `scenePath` | It runs against whatever tab is open. Name the scene. |
|
|
178
|
+
| `add_child` without `ctx.set_owner_recursive(node)` (or manual `.owner`) | The saved scene silently loses the node. |
|
|
179
|
+
| `var node := ctx.add_node(...)` | Does not compile: `ctx` results are untyped. `var node = ctx.add_node(...)`. |
|
|
180
|
+
| Guessing a property name ("it's probably `color`") | `summer_api_docs` answers in one call. Wrong names fail after half the mutation applied. |
|
|
738
181
|
| Claiming "the forest looks great" without a screenshot | Describe only frames you received. Run `summer_screenshot`. |
|
|
739
182
|
| Heavy batch loop in `summer_run_script` | It blocks the live editor. Move it to `summer_run_editor_script`. |
|
|
740
183
|
| Using `summer_run_editor_script` to edit the OPEN scene | It sees only the on-disk file; live edits are invisible and collisions likely. Use `summer_run_script`. |
|
|
741
184
|
| `"Vector3(0,10,0)"` (quoted) inside script source | That is the `summer_set_prop` wire convention. In GDScript write `Vector3(0, 10, 0)`. |
|
|
742
185
|
| Ignoring `errors` because `ok` was true | A partially-failed script may have half-mutated the scene. Read them. |
|
|
743
|
-
| Hand-building CSG node trees on a geometry-tier engine | `ctx.boolean/lathe/sweep/extrude_polygon` bake clean ArrayMeshes with no live CSG left behind. |
|
|
744
|
-
| Re-running a failed `make_shader` with a guessed fix | The compile error is in the `make_shader_errors` report verbatim, with the line. Read it, fix that line. |
|
|
745
|
-
| Hand-writing AnimationPlayer/library/track plumbing | `ctx.animate` is one call per property and dodges the quaternion trap. |
|
|
746
|
-
| Painting tiles with `summer_set_prop`, or hand-editing `tile_map_data` | `ctx.paint_rect` / `paint_tiles` (or `set_cell` in a loop) — cells are packed data. |
|
|
747
|
-
| Building a HUD with a dozen `summer_add_node`/`summer_set_prop` calls | One script: `add_canvas_layer` → `add_ui` → `set_theme_overrides`. |
|
|
748
|
-
| `button.pressed.connect(...)` inside the run script | Not persisted — gone when the script ends. `ctx.connect_signal` (or `summer_connect_signal`) saves it in the `.tscn`. |
|
|
749
|
-
| `ctx.connect(button, "pressed", hud, "_on_pressed")` | That is `Object.connect` on the ctx object, not the helper — wrong-signature error. The helper is `ctx.connect_signal(...)`. |
|
|
750
|
-
| `{"min_size": ...}` in `add_ui` props | Not a Control property — lands in `prop_warnings` and does nothing. The property is `custom_minimum_size`. |
|
|
751
|
-
| `ctx.make_prefab(ctx.get_scene_root(), ...)` | Refused. Pack a child subtree; the scene itself is `save_scene`. |
|
|
752
|
-
| Re-running a failed `attach_script` with a guessed fix | The parse error is in the report with the line. Read it, fix that line. |
|
|
753
|
-
| Judging a HUD from an edit-time scene preview | Anchors resolve against the real viewport. `summer_play` + `target:"game"`. |
|
|
754
|
-
| Calling game-completeness helpers on an engine that predates them | `Invalid call to method` — the run rolls back. Use the fallback table above. |
|
|
755
|
-
| Reading `rolled_back: true` as "nothing happened" after `attach_script` / `add_autoload` / `set_main_scene` | Files and `project.godot` sit outside the undo action. Read `files_written` / `project_settings_changed`; the checkpoint is the rollback. |
|
|
756
186
|
|
|
757
187
|
**Related skills:**
|
|
758
188
|
- `verifying-scenes` — the perception discipline: snapshot/diff/screenshot before-and-after, runtime reads, honest claims.
|
|
759
189
|
- `headless-scripting` — shell-launched engine scripts, imports, exports.
|
|
760
190
|
- `scene-composition` — what a well-structured scene looks like before you generate one.
|
|
761
|
-
- `ui-basics` — Control hierarchy, containers, anchors, theme vs inline styling
|
|
191
|
+
- `ui-basics` — Control hierarchy, containers, anchors, theme vs inline styling.
|
|
192
|
+
- `character-animation-wiring` — the animation tier end to end.
|
|
762
193
|
- `gdscript-patterns` — idioms for the scripts `attach_script` writes.
|
|
763
|
-
- `sprite-sheet` — generating the frames `add_animated_sprite` consumes.
|
|
764
194
|
- `verification-before-completion` — proving the result before claiming done.
|